NetBSD/dist/cdk/graph.c

584 lines
15 KiB
C

#include <cdk.h>
/*
* $Author: garbled $
* $Date: 2001/01/04 19:58:09 $
* $Revision: 1.1.1.1 $
*/
DeclareCDKObjects(my_funcs,Graph);
/*
* This creates a graph widget.
*/
CDKGRAPH *newCDKGraph (CDKSCREEN *cdkscreen, int xplace, int yplace, int height, int width, char *title, char *xtitle, char *ytitle)
{
/* Declare local variables. */
CDKGRAPH *graph = newCDKObject(CDKGRAPH, &my_funcs);
int parentWidth = getmaxx(cdkscreen->window);
int parentHeight = getmaxy(cdkscreen->window);
int boxWidth = width;
int boxHeight = height;
int xpos = xplace;
int ypos = yplace;
char **temp = 0;
int x;
/*
* If the height is a negative value, the height will
* be ROWS-height, otherwise, the height will be the
* given height.
*/
boxHeight = setWidgetDimension (parentHeight, height, 3);
/*
* If the width is a negative value, the width will
* be COLS-width, otherwise, the width will be the
* given width.
*/
boxWidth = setWidgetDimension (parentWidth, width, 0);
/*
* If the width is a negative value, the width will
* be COLS-width, otherwise, the width will be the
* given width.
*/
boxWidth = setWidgetDimension (parentWidth, width, 0);
/* Translate the char * items to chtype * */
if (title != 0)
{
/* We need to split the title on \n. */
temp = CDKsplitString (title, '\n');
graph->titleLines = CDKcountStrings (temp);
/* For each line in the title, convert from char * to chtype * */
for (x=0; x < graph->titleLines; x++)
{
graph->title[x] = char2Chtype (temp[x], &graph->titleLen[x], &graph->titlePos[x]);
graph->titlePos[x] = justifyString (boxWidth, graph->titleLen[x], graph->titlePos[x]);
}
CDKfreeStrings(temp);
}
else
{
/* No title? Set the required variables. */
graph->titleLines = 0;
}
boxHeight += graph->titleLines;
/*
* Make sure we didn't extend beyond the dimensions of the window.
*/
boxWidth = MINIMUM (boxWidth, parentWidth);
boxHeight = MINIMUM (boxHeight, parentHeight);
/* Rejustify the x and y positions if we need to. */
alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight);
/* Create the graph pointer. */
ScreenOf(graph) = cdkscreen;
graph->parent = cdkscreen->window;
graph->win = newwin (boxHeight, boxWidth, ypos, xpos);
graph->boxHeight = boxHeight;
graph->boxWidth = boxWidth;
ObjOf(graph)->box = FALSE;
graph->minx = 0;
graph->maxx = 0;
graph->xscale = 0;
graph->yscale = 0;
graph->count = 0;
graph->displayType = vLINE;
graph->ULChar = ACS_ULCORNER;
graph->URChar = ACS_URCORNER;
graph->LLChar = ACS_LLCORNER;
graph->LRChar = ACS_LRCORNER;
graph->HChar = ACS_HLINE;
graph->VChar = ACS_VLINE;
graph->BoxAttrib = A_NORMAL;
/* Is the graph pointer null? */
if (graph->win == 0)
{
/* Clean up any memory used. */
free (graph);
/* Return a null pointer. */
return ( 0 );
}
keypad (graph->win, TRUE);
leaveok (graph->win, TRUE);
/* Translate the X Axis title char * to a chtype * */
if (xtitle != 0)
{
graph->xtitle = char2Chtype (xtitle, &graph->xtitleLen, &graph->xtitlePos);
graph->xtitlePos = justifyString (graph->boxHeight, graph->xtitleLen, graph->xtitlePos);
}
else
{
graph->xtitle = char2Chtype ("<C></5>X Axis", &graph->xtitleLen, &graph->xtitlePos);
graph->xtitlePos = justifyString (graph->boxHeight, graph->xtitleLen, graph->xtitlePos);
}
/* Translate the Y Axis title char * to a chtype * */
if (ytitle != 0)
{
graph->ytitle = char2Chtype (ytitle, &graph->ytitleLen, &graph->ytitlePos);
graph->ytitlePos = justifyString (graph->boxWidth, graph->ytitleLen, graph->ytitlePos);
}
else
{
graph->ytitle = char2Chtype ("<C></5>Y Axis", &graph->ytitleLen, &graph->ytitlePos);
graph->ytitlePos = justifyString (graph->boxWidth, graph->ytitleLen, graph->ytitlePos);
}
/* Set some values of the graph structure. */
graph->graphChar = 0;
/* Register this baby. */
registerCDKObject (cdkscreen, vGRAPH, graph);
/* Return the graph pointer. */
return (graph);
}
/*
* This was added for the builder.
*/
void activateCDKGraph (CDKGRAPH *graph, chtype *actions GCC_UNUSED)
{
drawCDKGraph (graph, ObjOf(graph)->box);
}
/*
* This sets multiple attributes of the widget.
*/
int setCDKGraph (CDKGRAPH *graph, int *values, int count, char *graphChar, boolean startAtZero, EGraphDisplayType displayType)
{
int ret;
ret = setCDKGraphValues (graph, values, count, startAtZero);
setCDKGraphCharacters (graph, graphChar);
setCDKGraphDisplayType (graph, displayType);
return ret;
}
/*
* This sets the values of the graph.
*/
int setCDKGraphValues (CDKGRAPH *graph, int *values, int count, boolean startAtZero)
{
/* Declare local variables. */
int min = INT_MAX;
int max = INT_MIN;
int x;
/* Make sure everything is happy. */
if (count < 0)
{
return (FALSE);
}
/* Copy the X values. */
for (x=0; x < count; x++)
{
/* Determine the min/max values of the graph. */
min = MINIMUM (values[x], graph->minx);
max = MAXIMUM (values[x], graph->maxx);
/* Copy the value. */
graph->values[x] = values[x];
}
/* Keep the count and min/max values. */
graph->count = count;
graph->minx = min;
graph->maxx = max;
/* Check the start at zero status. */
if (startAtZero)
{
graph->minx = 0;
}
/* Determine the scales. */
graph->xscale = ((graph->maxx - graph->minx) / (graph->boxHeight - graph->titleLines - 5));
graph->yscale = ((graph->boxWidth-4) / count);
return (TRUE);
}
int *getCDKGraphValues (CDKGRAPH *graph, int *size)
{
(*size) = graph->count;
return graph->values;
}
/*
* This sets the value of the graph at the given index.
*/
int setCDKGraphValue (CDKGRAPH *graph, int Index, int value, boolean startAtZero)
{
/* Make sure the index is within range. */
if (Index < 0 || Index > graph->count)
{
return (FALSE);
}
/* Set the min, max, and value for the graph. */
graph->minx = MINIMUM (value, graph->minx);
graph->maxx = MAXIMUM (value, graph->maxx);
graph->values[Index] = value;
/* Check the start at zero status. */
if (startAtZero)
{
graph->minx = 0;
}
/* Determine the scales. */
graph->xscale = ((graph->maxx - graph->minx) / (graph->boxHeight - 5));
graph->yscale = ((graph->boxWidth-4) / graph->count);
return (TRUE);
}
int getCDKGraphValue (CDKGRAPH *graph, int Index)
{
return graph->values[Index];
}
/*
* This sets the characters of the graph widget.
*/
int setCDKGraphCharacters (CDKGRAPH *graph, char *characters)
{
/* Declare local variables. */
chtype *newTokens = 0;
int charCount, junk;
/* Convert the string given to us. */
newTokens = char2Chtype (characters, &charCount, &junk);
/*
* Check if the number of characters back is the same as the number
* of elements in the list.
*/
if (charCount != graph->count)
{
freeChtype (newTokens);
return (FALSE);
}
/* Evrything OK so far. Nuke the old pointer and use the new one. */
freeChtype (graph->graphChar);
graph->graphChar = newTokens;
return (TRUE);
}
chtype *getCDKGraphCharacters (CDKGRAPH *graph)
{
return graph->graphChar;
}
/*
* This sets the character of the graph widget of the given index.
*/
int setCDKGraphCharacter (CDKGRAPH *graph, int Index, char *character)
{
/* Declare local variables. */
chtype *newTokens = 0;
int charCount, junk;
/* Make sure the index is within range. */
if (Index < 0 || Index > graph->count)
{
return (FALSE);
}
/* Convert the string given to us. */
newTokens = char2Chtype (character, &charCount, &junk);
/*
* Check if the number of characters back is the same as the number
* of elements in the list.
*/
if (charCount != graph->count)
{
freeChtype (newTokens);
return (FALSE);
}
/* Evrything OK so far. Set the value of the array. */
graph->graphChar[Index] = newTokens[0];
freeChtype (newTokens);
return (TRUE);
}
chtype getCDKGraphCharacter (CDKGRAPH *graph, int Index)
{
return graph->graphChar[Index];
}
/*
* This sets the display type of the graph.
*/
void setCDKGraphDisplayType (CDKGRAPH *graph, EGraphDisplayType type)
{
graph->displayType = type;
}
EGraphDisplayType getCDKGraphDisplayType (CDKGRAPH *graph)
{
return graph->displayType;
}
/*
* These functions set the drawing characters of the widget.
*/
void setCDKGraphULChar (CDKGRAPH *graph, chtype character)
{
graph->ULChar = character;
}
void setCDKGraphURChar (CDKGRAPH *graph, chtype character)
{
graph->URChar = character;
}
void setCDKGraphLLChar (CDKGRAPH *graph, chtype character)
{
graph->LLChar = character;
}
void setCDKGraphLRChar (CDKGRAPH *graph, chtype character)
{
graph->LRChar = character;
}
void setCDKGraphVerticalChar (CDKGRAPH *graph, chtype character)
{
graph->VChar = character;
}
void setCDKGraphHorizontalChar (CDKGRAPH *graph, chtype character)
{
graph->HChar = character;
}
void setCDKGraphBoxAttribute (CDKGRAPH *graph, chtype character)
{
graph->BoxAttrib = character;
}
/*
* This sets the background color of the widget.
*/
void setCDKGraphBackgroundColor (CDKGRAPH *graph, char *color)
{
chtype *holder = 0;
int junk1, junk2;
/* Make sure the color isn't null. */
if (color == 0)
{
return;
}
/* Convert the value of the environment variable to a chtype. */
holder = char2Chtype (color, &junk1, &junk2);
/* Set the widgets background color. */
wbkgd (graph->win, holder[0]);
/* Clean up. */
freeChtype (holder);
}
/*
* This moves the graph field to the given location.
*/
static void _moveCDKGraph (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
CDKGRAPH *graph = (CDKGRAPH *)object;
/*
* If this is a relative move, then we will adjust where we want
* to move to.
*/
if (relative)
{
xplace += getbegx(graph->win);
yplace += getbegy(graph->win);
}
/* Adjust the window if we need to. */
alignxy (WindowOf(graph), &xplace, &yplace, graph->boxWidth, graph->boxHeight);
/* Move the window to the new location. */
moveCursesWindow(graph->win, xplace, yplace);
/* Redraw the window, if they asked for it. */
if (refresh_flag)
{
drawCDKGraph (graph, ObjOf(graph)->box);
}
}
/*
* This sets whether or not the graph will be boxed.
*/
void setCDKGraphBox (CDKGRAPH *graph, boolean Box)
{
ObjOf(graph)->box = Box;
}
boolean getCDKGraphBox (CDKGRAPH *graph)
{
return ObjOf(graph)->box;
}
/*
* This function draws the graph widget.
*/
static void _drawCDKGraph (CDKOBJS *object, boolean Box)
{
CDKGRAPH *graph = (CDKGRAPH *)object;
int adj = 2 + (graph->xtitle == 0 ? 0 : 1);
int spacing = 0;
chtype attrib = ' '|A_REVERSE;
char temp[100];
int x, y, xpos, ypos, len;
/* Box it if needed. */
if (Box)
{
attrbox (graph->win,
graph->ULChar, graph->URChar,
graph->LLChar, graph->LRChar,
graph->HChar, graph->VChar,
graph->BoxAttrib,
FALSE);
}
/* Draw in the vertical axis. */
drawLine (graph->win, 2, graph->titleLines + 1, 2, graph->boxHeight-3, ACS_VLINE);
/* Draw in the horizontal axis. */
drawLine (graph->win, 3, graph->boxHeight-3, graph->boxWidth, graph->boxHeight-3, ACS_HLINE);
/* Draw in the title if there is one. */
for (x=0; x < graph->titleLines; x++)
{
writeChtype (graph->win,
graph->titlePos[x],
x + 1,
graph->title[x],
HORIZONTAL, 0,
graph->titleLen[x]);
}
/* Draw in the X axis title. */
if (graph->xtitle != 0)
{
writeChtype (graph->win, 0, graph->xtitlePos, graph->xtitle, VERTICAL, 0, graph->xtitleLen);
attrib = graph->xtitle[0] & A_ATTRIBUTES;
}
/* Draw in the X axis high value. */
sprintf (temp, "%d", graph->maxx);
len = (int)strlen (temp);
writeCharAttrib (graph->win, 1, graph->titleLines + 1, temp, attrib, VERTICAL, 0, len);
/* Draw in the X axis low value. */
sprintf (temp, "%d", graph->minx);
len = (int)strlen (temp);
writeCharAttrib (graph->win, 1, graph->boxHeight-2-len, temp, attrib, VERTICAL, 0, len);
/* Draw in the Y axis title. */
if (graph->ytitle != 0)
{
writeChtype (graph->win, graph->ytitlePos, graph->boxHeight-1, graph->ytitle, HORIZONTAL, 0, graph->ytitleLen);
attrib = graph->ytitle[0] & A_ATTRIBUTES;
}
/* Draw in the Y axis high value. */
sprintf (temp, "%d", graph->count);
len = (int)strlen (temp);
writeCharAttrib (graph->win, graph->boxWidth-len-adj, graph->boxHeight-2, temp, attrib, HORIZONTAL, 0, len);
/* Draw in the Y axis low value. */
sprintf (temp, "0");
writeCharAttrib (graph->win, 3, graph->boxHeight-2, temp, attrib, HORIZONTAL, 0, (int)strlen(temp));
/* If the count is zero, then there aren't any points. */
if (graph->count == 0)
{
wnoutrefresh (graph->win);
return;
}
spacing = (graph->boxWidth - 3) / graph->count;
/* Draw in the graph line/plot points. */
for (y=0; y < graph->count; y++)
{
int colheight = (graph->values[y] / graph->xscale) - 1;
/* Add the marker on the Y axis. */
mvwaddch (graph->win, graph->boxHeight-3, (y + 1)*spacing + adj, ACS_TTEE);
/* If this is a plot graph, all we do is draw a dot. */
if (graph->displayType == vPLOT)
{
xpos = graph->boxHeight-4-colheight;
ypos = (y + 1)*spacing + adj;
mvwaddch (graph->win, xpos, ypos, graph->graphChar[y]);
}
else
{
for (x=0; x <= graph->yscale; x++)
{
xpos = graph->boxHeight-3;
ypos = (y + 1)*spacing + adj;
drawLine (graph->win, ypos, xpos-colheight, ypos, xpos, graph->graphChar[y]);
}
}
}
/* Draw in the axis corners. */
mvwaddch (graph->win, graph->titleLines, 2, ACS_URCORNER);
mvwaddch (graph->win, graph->boxHeight-3, 2, ACS_LLCORNER);
mvwaddch (graph->win, graph->boxHeight-3, graph->boxWidth, ACS_URCORNER);
/* Refresh and lets see 'er. */
wnoutrefresh (graph->win);
}
/*
* This function destroys the graph widget.
*/
void destroyCDKGraph (CDKGRAPH *graph)
{
int x;
/* Erase the object. */
eraseCDKGraph (graph);
/* Clear up the char pointers. */
for (x=0; x < graph->titleLines; x++)
{
freeChtype (graph->title[x]);
}
freeChtype (graph->xtitle);
freeChtype (graph->ytitle);
freeChtype (graph->graphChar);
/* Clean up the windows. */
deleteCursesWindow (graph->win);
/* Unregister this object. */
unregisterCDKObject (vGRAPH, graph);
/* Finish cleaning up. */
free (graph);
}
/*
* This function erases the graph widget from the screen.
*/
static void _eraseCDKGraph (CDKOBJS *object)
{
CDKGRAPH *graph = (CDKGRAPH *)object;
eraseCursesWindow (graph->win);
}