1 #include <cdk_int.h>
2 
3 /*
4  * $Author: tom $
5  * $Date: 2013/06/16 15:05:27 $
6  * $Revision: 1.86 $
7  */
8 
9 DeclareCDKObjects (GRAPH, Graph, setCdk, Unknown);
10 
11 #define TITLE_LM 3
12 
13 /*
14  * Create a graph widget.
15  */
newCDKGraph(CDKSCREEN * cdkscreen,int xplace,int yplace,int height,int width,const char * title,const char * xtitle,const char * ytitle)16 CDKGRAPH *newCDKGraph (CDKSCREEN *cdkscreen,
17 		       int xplace,
18 		       int yplace,
19 		       int height,
20 		       int width,
21 		       const char *title,
22 		       const char *xtitle,
23 		       const char *ytitle)
24 {
25    /* *INDENT-EQLS* */
26    CDKGRAPH *widget     = 0;
27    int parentWidth      = getmaxx (cdkscreen->window);
28    int parentHeight     = getmaxy (cdkscreen->window);
29    int boxWidth;
30    int boxHeight;
31    int xpos             = xplace;
32    int ypos             = yplace;
33 
34    if ((widget = newCDKObject (CDKGRAPH, &my_funcs)) == 0)
35         return (0);
36 
37    setCDKGraphBox (widget, FALSE);
38 
39    /* *INDENT-EQLS* */
40    boxHeight = setWidgetDimension (parentHeight, height, 3);
41    boxWidth  = setWidgetDimension (parentWidth, width, 0);
42    boxWidth  = setCdkTitle (ObjOf (widget), title, boxWidth);
43    boxHeight += TitleLinesOf (widget);
44    boxWidth  = MINIMUM (boxWidth, parentWidth);
45    boxHeight = MINIMUM (boxHeight, parentHeight);
46 
47    /* Rejustify the x and y positions if we need to. */
48    alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight);
49 
50    /* *INDENT-EQLS* Create the widget pointer. */
51    ScreenOf (widget)    = cdkscreen;
52    widget->parent       = cdkscreen->window;
53    widget->win          = newwin (boxHeight, boxWidth, ypos, xpos);
54    widget->boxHeight    = boxHeight;
55    widget->boxWidth     = boxWidth;
56    widget->minx         = 0;
57    widget->maxx         = 0;
58    widget->xscale       = 0;
59    widget->yscale       = 0;
60    widget->count        = 0;
61    widget->displayType  = vLINE;
62 
63    if (widget->win == 0)
64    {
65       destroyCDKObject (widget);
66       return (0);
67    }
68    keypad (widget->win, TRUE);
69 
70    /* Translate the X Axis title char * to a chtype * */
71    if (xtitle != 0)
72    {
73       widget->xtitle = char2Chtype (xtitle, &widget->xtitleLen, &widget->xtitlePos);
74       widget->xtitlePos = justifyString (widget->boxHeight,
75 					 widget->xtitleLen,
76 					 widget->xtitlePos);
77    }
78    else
79    {
80       widget->xtitle = char2Chtype ("<C></5>X Axis", &widget->xtitleLen, &widget->xtitlePos);
81       widget->xtitlePos = justifyString (widget->boxHeight,
82 					 widget->xtitleLen,
83 					 widget->xtitlePos);
84    }
85 
86    /* Translate the Y Axis title char * to a chtype * */
87    if (ytitle != 0)
88    {
89       widget->ytitle = char2Chtype (ytitle, &widget->ytitleLen, &widget->ytitlePos);
90       widget->ytitlePos = justifyString (widget->boxWidth,
91 					 widget->ytitleLen,
92 					 widget->ytitlePos);
93    }
94    else
95    {
96       widget->ytitle = char2Chtype ("<C></5>Y Axis", &widget->ytitleLen, &widget->ytitlePos);
97       widget->ytitlePos = justifyString (widget->boxWidth,
98 					 widget->ytitleLen,
99 					 widget->ytitlePos);
100    }
101 
102    widget->graphChar = 0;
103 
104    registerCDKObject (cdkscreen, vGRAPH, widget);
105 
106    return (widget);
107 }
108 
109 /*
110  * This was added for the builder.
111  */
activateCDKGraph(CDKGRAPH * widget,chtype * actions GCC_UNUSED)112 void activateCDKGraph (CDKGRAPH *widget, chtype *actions GCC_UNUSED)
113 {
114    drawCDKGraph (widget, ObjOf (widget)->box);
115 }
116 
117 /*
118  * Set multiple attributes of the widget.
119  */
setCDKGraph(CDKGRAPH * widget,int * values,int count,const char * graphChar,boolean startAtZero,EGraphDisplayType displayType)120 int setCDKGraph (CDKGRAPH *widget,
121 		 int *values,
122 		 int count,
123 		 const char *graphChar,
124 		 boolean startAtZero,
125 		 EGraphDisplayType displayType)
126 {
127    int ret;
128 
129    ret = setCDKGraphValues (widget, values, count, startAtZero);
130    setCDKGraphCharacters (widget, graphChar);
131    setCDKGraphDisplayType (widget, displayType);
132    return ret;
133 }
134 
135 /*
136  * Set the scale factors for the graph after we have loaded new values.
137  */
setScales(CDKGRAPH * widget)138 static void setScales (CDKGRAPH *widget)
139 {
140    widget->xscale = ((widget->maxx - widget->minx)
141 		     / MAXIMUM (1, (widget->boxHeight
142 				    - TitleLinesOf (widget)
143 				    - 5)));
144    if (widget->xscale <= 0)
145       widget->xscale = 1;
146 
147    widget->yscale = ((widget->boxWidth - 4) / MAXIMUM (1, widget->count));
148    if (widget->yscale <= 0)
149       widget->yscale = 1;
150 }
151 
152 /*
153  * Set the values of the graph.
154  */
setCDKGraphValues(CDKGRAPH * widget,int * values,int count,boolean startAtZero)155 int setCDKGraphValues (CDKGRAPH *widget, int *values, int count, boolean startAtZero)
156 {
157    int min = INT_MAX;
158    int max = INT_MIN;
159    int x;
160 
161    /* Make sure everything is happy. */
162    if (count < 0)
163       return (FALSE);
164 
165    if (widget->values != 0)
166    {
167       free (widget->values);
168       widget->values = 0;
169       widget->count = 0;
170    }
171    if ((widget->values = typeCallocN (int, count + 1)) == 0)
172         return FALSE;
173 
174    /* Copy the X values. */
175    for (x = 0; x < count; x++)
176    {
177       /* Determine the min/max values of the graph. */
178       min = MINIMUM (values[x], widget->minx);
179       max = MAXIMUM (values[x], widget->maxx);
180 
181       /* Copy the value. */
182       widget->values[x] = values[x];
183    }
184 
185    /* Keep the count and min/max values. */
186    widget->count = count;
187    widget->minx = min;
188    widget->maxx = max;
189 
190    /* Check the start at zero status. */
191    if (startAtZero)
192    {
193       widget->minx = 0;
194    }
195 
196    setScales (widget);
197 
198    return (TRUE);
199 }
getCDKGraphValues(CDKGRAPH * widget,int * size)200 int *getCDKGraphValues (CDKGRAPH *widget, int *size)
201 {
202    (*size) = widget->count;
203    return widget->values;
204 }
205 
206 /*
207  * Set the value of the graph at the given index.
208  */
setCDKGraphValue(CDKGRAPH * widget,int Index,int value,boolean startAtZero)209 int setCDKGraphValue (CDKGRAPH *widget, int Index, int value, boolean startAtZero)
210 {
211    /* Make sure the index is within range. */
212    if (Index < 0 || Index >= widget->count)
213    {
214       return (FALSE);
215    }
216 
217    /* Set the min, max, and value for the graph. */
218    widget->minx = MINIMUM (value, widget->minx);
219    widget->maxx = MAXIMUM (value, widget->maxx);
220    widget->values[Index] = value;
221 
222    /* Check the start at zero status. */
223    if (startAtZero)
224    {
225       widget->minx = 0;
226    }
227 
228    setScales (widget);
229 
230    return (TRUE);
231 }
getCDKGraphValue(CDKGRAPH * widget,int Index)232 int getCDKGraphValue (CDKGRAPH *widget, int Index)
233 {
234    return Index >= 0 && Index < widget->count ? widget->values[Index] : 0;
235 }
236 
237 /*
238  * Set the characters of the graph widget.
239  */
setCDKGraphCharacters(CDKGRAPH * widget,const char * characters)240 int setCDKGraphCharacters (CDKGRAPH *widget, const char *characters)
241 {
242    chtype *newTokens = 0;
243    int charCount, junk;
244 
245    newTokens = char2Chtype (characters, &charCount, &junk);
246 
247    if (charCount != widget->count)
248    {
249       freeChtype (newTokens);
250       return (FALSE);
251    }
252 
253    freeChtype (widget->graphChar);
254    widget->graphChar = newTokens;
255    return (TRUE);
256 }
getCDKGraphCharacters(CDKGRAPH * widget)257 chtype *getCDKGraphCharacters (CDKGRAPH *widget)
258 {
259    return widget->graphChar;
260 }
261 
262 /*
263  * Set the character of the graph widget of the given index.
264  */
setCDKGraphCharacter(CDKGRAPH * widget,int Index,const char * character)265 int setCDKGraphCharacter (CDKGRAPH *widget, int Index, const char *character)
266 {
267    chtype *newTokens = 0;
268    int charCount, junk;
269 
270    /* Make sure the index is within range. */
271    if (Index < 0 || Index > widget->count)
272    {
273       return (FALSE);
274    }
275 
276    /* Convert the string given to us. */
277    newTokens = char2Chtype (character, &charCount, &junk);
278 
279    /*
280     * Check if the number of characters back is the same as the number
281     * of elements in the list.
282     */
283    if (charCount != widget->count)
284    {
285       freeChtype (newTokens);
286       return (FALSE);
287    }
288 
289    /* Everything OK so far.  Set the value of the array.  */
290    widget->graphChar[Index] = newTokens[0];
291    freeChtype (newTokens);
292    return (TRUE);
293 }
getCDKGraphCharacter(CDKGRAPH * widget,int Index)294 chtype getCDKGraphCharacter (CDKGRAPH *widget, int Index)
295 {
296    return widget->graphChar[Index];
297 }
298 
299 /*
300  * Set the display type of the graph.
301  */
setCDKGraphDisplayType(CDKGRAPH * widget,EGraphDisplayType type)302 void setCDKGraphDisplayType (CDKGRAPH *widget, EGraphDisplayType type)
303 {
304    widget->displayType = type;
305 }
getCDKGraphDisplayType(CDKGRAPH * widget)306 EGraphDisplayType getCDKGraphDisplayType (CDKGRAPH *widget)
307 {
308    return widget->displayType;
309 }
310 
311 /*
312  * Set the background attribute of the widget.
313  */
_setBKattrGraph(CDKOBJS * object,chtype attrib)314 static void _setBKattrGraph (CDKOBJS *object, chtype attrib)
315 {
316    if (object != 0)
317    {
318       CDKGRAPH *widget = (CDKGRAPH *)object;
319 
320       wbkgd (widget->win, attrib);
321    }
322 }
323 
324 /*
325  * Move the graph field to the given location.
326  */
_moveCDKGraph(CDKOBJS * object,int xplace,int yplace,boolean relative,boolean refresh_flag)327 static void _moveCDKGraph (CDKOBJS *object,
328 			   int xplace,
329 			   int yplace,
330 			   boolean relative,
331 			   boolean refresh_flag)
332 {
333    CDKGRAPH *widget = (CDKGRAPH *)object;
334    /* *INDENT-EQLS* */
335    int currentX = getbegx (widget->win);
336    int currentY = getbegy (widget->win);
337    int xpos     = xplace;
338    int ypos     = yplace;
339    int xdiff    = 0;
340    int ydiff    = 0;
341 
342    /*
343     * If this is a relative move, then we will adjust where we want
344     * to move to.
345     */
346    if (relative)
347    {
348       xpos = getbegx (widget->win) + xplace;
349       ypos = getbegy (widget->win) + yplace;
350    }
351 
352    /* Adjust the window if we need to. */
353    alignxy (WindowOf (widget), &xpos, &ypos, widget->boxWidth, widget->boxHeight);
354 
355    /* Get the difference. */
356    xdiff = currentX - xpos;
357    ydiff = currentY - ypos;
358 
359    /* Move the window to the new location. */
360    moveCursesWindow (widget->win, -xdiff, -ydiff);
361    moveCursesWindow (widget->shadowWin, -xdiff, -ydiff);
362 
363    /* Touch the windows so they 'move'. */
364    refreshCDKWindow (WindowOf (widget));
365 
366    /* Redraw the window, if they asked for it. */
367    if (refresh_flag)
368    {
369       drawCDKGraph (widget, ObjOf (widget)->box);
370    }
371 }
372 
373 /*
374  * Set whether or not the graph will be boxed.
375  */
setCDKGraphBox(CDKGRAPH * widget,boolean Box)376 void setCDKGraphBox (CDKGRAPH *widget, boolean Box)
377 {
378    ObjOf (widget)->box = Box;
379    ObjOf (widget)->borderSize = Box ? 1 : 0;
380 }
getCDKGraphBox(CDKGRAPH * widget)381 boolean getCDKGraphBox (CDKGRAPH *widget)
382 {
383    return ObjOf (widget)->box;
384 }
385 
386 /*
387  * Draw the graph widget.
388  */
_drawCDKGraph(CDKOBJS * object,boolean Box)389 static void _drawCDKGraph (CDKOBJS *object, boolean Box)
390 {
391    /* *INDENT-EQLS* */
392    CDKGRAPH *widget     = (CDKGRAPH *)object;
393    int adj              = 2 + (widget->xtitle == 0 ? 0 : 1);
394    int spacing          = 0;
395    chtype attrib        = ' ' | A_REVERSE;
396    char temp[100];
397    int x, y, xpos, ypos, len;
398 
399    /* Box it if needed. */
400    if (Box)
401    {
402       drawObjBox (widget->win, ObjOf (widget));
403    }
404 
405    /* Draw in the vertical axis. */
406    drawLine (widget->win,
407 	     2,
408 	     TitleLinesOf (widget) + 1,
409 	     2,
410 	     widget->boxHeight - 3,
411 	     ACS_VLINE);
412 
413    /* Draw in the horizontal axis. */
414    drawLine (widget->win,
415 	     3,
416 	     widget->boxHeight - 3,
417 	     widget->boxWidth,
418 	     widget->boxHeight - 3,
419 	     ACS_HLINE);
420 
421    drawCdkTitle (widget->win, object);
422 
423    /* Draw in the X axis title. */
424    if (widget->xtitle != 0)
425    {
426       writeChtype (widget->win,
427 		   0,
428 		   widget->xtitlePos,
429 		   widget->xtitle,
430 		   VERTICAL,
431 		   0,
432 		   widget->xtitleLen);
433       attrib = widget->xtitle[0] & A_ATTRIBUTES;
434    }
435 
436    /* Draw in the X axis high value. */
437    sprintf (temp, "%d", widget->maxx);
438    len = (int)strlen (temp);
439    writeCharAttrib (widget->win,
440 		    1,
441 		    TitleLinesOf (widget) + 1,
442 		    temp,
443 		    attrib,
444 		    VERTICAL,
445 		    0,
446 		    len);
447 
448    /* Draw in the X axis low value. */
449    sprintf (temp, "%d", widget->minx);
450    len = (int)strlen (temp);
451    writeCharAttrib (widget->win,
452 		    1,
453 		    widget->boxHeight - 2 - len,
454 		    temp,
455 		    attrib,
456 		    VERTICAL,
457 		    0,
458 		    len);
459 
460    /* Draw in the Y axis title. */
461    if (widget->ytitle != 0)
462    {
463       writeChtype (widget->win,
464 		   widget->ytitlePos,
465 		   widget->boxHeight - 1,
466 		   widget->ytitle,
467 		   HORIZONTAL,
468 		   0,
469 		   widget->ytitleLen);
470       attrib = widget->ytitle[0] & A_ATTRIBUTES;
471    }
472 
473    /* Draw in the Y axis high value. */
474    sprintf (temp, "%d", widget->count);
475    len = (int)strlen (temp);
476    writeCharAttrib (widget->win,
477 		    widget->boxWidth - len - adj,
478 		    widget->boxHeight - 2,
479 		    temp,
480 		    attrib,
481 		    HORIZONTAL,
482 		    0,
483 		    len);
484 
485    /* Draw in the Y axis low value. */
486    sprintf (temp, "0");
487    writeCharAttrib (widget->win,
488 		    3,
489 		    widget->boxHeight - 2,
490 		    temp,
491 		    attrib,
492 		    HORIZONTAL,
493 		    0,
494 		    (int)strlen (temp));
495 
496    /* If the count is zero, then there aren't any points. */
497    if (widget->count == 0)
498    {
499       wrefresh (widget->win);
500       return;
501    }
502    spacing = (widget->boxWidth - TITLE_LM) / widget->count;
503 
504    /* Draw in the graph line/plot points. */
505    for (y = 0; y < widget->count; y++)
506    {
507       int colheight = (widget->values[y] / widget->xscale) - 1;
508       /* Add the marker on the Y axis. */
509       (void)mvwaddch (widget->win,
510 		      widget->boxHeight - 3,
511 		      (y + 1) * spacing + adj,
512 		      ACS_TTEE);
513 
514       /* If this is a plot graph, all we do is draw a dot. */
515       if (widget->displayType == vPLOT)
516       {
517 	 xpos = widget->boxHeight - 4 - colheight;
518 	 ypos = (y + 1) * spacing + adj;
519 	 (void)mvwaddch (widget->win, xpos, ypos, widget->graphChar[y]);
520       }
521       else
522       {
523 	 for (x = 0; x <= widget->yscale; x++)
524 	 {
525 	    xpos = widget->boxHeight - 3;
526 	    ypos = (y + 1) * spacing + adj;
527 	    drawLine (widget->win,
528 		      ypos,
529 		      xpos - colheight,
530 		      ypos,
531 		      xpos,
532 		      widget->graphChar[y]);
533 	 }
534       }
535    }
536 
537    /* Draw in the axis corners. */
538    (void)mvwaddch (widget->win, TitleLinesOf (widget), 2, ACS_URCORNER);
539    (void)mvwaddch (widget->win, widget->boxHeight - 3, 2, ACS_LLCORNER);
540    (void)mvwaddch (widget->win, widget->boxHeight - 3, widget->boxWidth, ACS_URCORNER);
541 
542    /* Refresh and lets see 'er. */
543    wrefresh (widget->win);
544 }
545 
546 /*
547  * Destroy the graph widget.
548  */
_destroyCDKGraph(CDKOBJS * object)549 static void _destroyCDKGraph (CDKOBJS *object)
550 {
551    if (object != 0)
552    {
553       CDKGRAPH *widget = (CDKGRAPH *)object;
554 
555       cleanCdkTitle (object);
556 
557       freeChtype (widget->xtitle);
558       freeChtype (widget->ytitle);
559       freeChtype (widget->graphChar);
560 
561       freeChecked (widget->values);
562 
563       /* Clean the key bindings. */
564       cleanCDKObjectBindings (vGRAPH, widget);
565 
566       /* Unregister this object. */
567       unregisterCDKObject (vGRAPH, widget);
568 
569       /* Clean up the windows. */
570       deleteCursesWindow (widget->win);
571    }
572 }
573 
574 /*
575  * Erase the graph widget from the screen.
576  */
_eraseCDKGraph(CDKOBJS * object)577 static void _eraseCDKGraph (CDKOBJS *object)
578 {
579    if (validCDKObject (object))
580    {
581       CDKGRAPH *widget = (CDKGRAPH *)object;
582 
583       eraseCursesWindow (widget->win);
584    }
585 }
586 
587 dummyInject (Graph)
588 
589 dummyFocus (Graph)
590 
591 dummyUnfocus (Graph)
592 
593 dummyRefreshData (Graph)
594 
595 dummySaveData (Graph)
596