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