1 /***********************************************************
2
3 Copyright 1987, 1988, 1994, 1998 The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24
25
26 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28 All Rights Reserved
29
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
37
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44 SOFTWARE.
45
46 ******************************************************************/
47
48 #ifdef HAVE_CONFIG_H
49 #include <config.h>
50 #endif
51 #include <stdio.h>
52 #include <X11/IntrinsicP.h>
53 #include <X11/StringDefs.h>
54 #include <X11/Xfuncs.h>
55 #include <X11/Xaw/StripCharP.h>
56 #include <X11/Xaw/XawInit.h>
57 #include "Private.h"
58
59 #define MS_PER_SEC 1000
60 #define NUM_VALUEDATA(w) (sizeof((w)->strip_chart.valuedata) / \
61 sizeof((w)->strip_chart.valuedata[0]))
62
63 /*
64 * Class Methods
65 */
66 static void XawStripChartInitialize(Widget, Widget, ArgList, Cardinal*);
67 static void XawStripChartDestroy(Widget);
68 static void XawStripChartRedisplay(Widget, XEvent*, Region);
69 static void XawStripChartResize(Widget);
70 static Boolean XawStripChartSetValues(Widget, Widget, Widget,
71 ArgList, Cardinal*);
72
73 /*
74 * Prototypes
75 */
76 static void CreateGC(StripChartWidget, unsigned int);
77 static void DestroyGC(StripChartWidget, unsigned int);
78 static void draw_it(XtPointer, XtIntervalId*);
79 static void MoveChart(StripChartWidget, Bool);
80 static int repaint_window(StripChartWidget, int, int);
81
82 /*
83 * Initialization
84 */
85 #define offset(field) XtOffsetOf(StripChartRec, field)
86 static XtResource resources[] = {
87 {
88 XtNwidth,
89 XtCWidth,
90 XtRDimension,
91 sizeof(Dimension),
92 offset(core.width),
93 XtRImmediate,
94 (XtPointer)
95 120
96 },
97 {
98 XtNheight,
99 XtCHeight,
100 XtRDimension,
101 sizeof(Dimension),
102 offset(core.height),
103 XtRImmediate,
104 (XtPointer)120
105 },
106 {
107 XtNupdate,
108 XtCInterval,
109 XtRInt,
110 sizeof(int),
111 offset(strip_chart.update),
112 XtRImmediate,
113 (XtPointer)10
114 },
115 {
116 XtNminScale,
117 XtCScale,
118 XtRInt,
119 sizeof(int),
120 offset(strip_chart.min_scale),
121 XtRImmediate,
122 (XtPointer)1
123 },
124 {
125 XtNforeground,
126 XtCForeground,
127 XtRPixel,
128 sizeof(Pixel),
129 offset(strip_chart.fgpixel),
130 XtRString,
131 (XtPointer)XtDefaultForeground
132 },
133 {
134 XtNhighlight,
135 XtCForeground,
136 XtRPixel,
137 sizeof(Pixel),
138 offset(strip_chart.hipixel),
139 XtRString,
140 (XtPointer)XtDefaultForeground
141 },
142 {
143 XtNgetValue,
144 XtCCallback,
145 XtRCallback,
146 sizeof(XtPointer),
147 offset(strip_chart.get_value),
148 XtRImmediate,
149 NULL
150 },
151 {
152 XtNjumpScroll,
153 XtCJumpScroll,
154 XtRInt,
155 sizeof(int),
156 offset(strip_chart.jump_val),
157 XtRImmediate,
158 (XtPointer)DEFAULT_JUMP
159 },
160 };
161 #undef offset
162
163 StripChartClassRec stripChartClassRec = {
164 /* core */
165 {
166 (WidgetClass)&simpleClassRec, /* superclass */
167 "StripChart", /* class_name */
168 sizeof(StripChartRec), /* widget_size */
169 XawInitializeWidgetSet, /* class_initialize */
170 NULL, /* class_part_initialize */
171 False, /* class_inited */
172 XawStripChartInitialize, /* initialize */
173 NULL, /* initialize_hook */
174 XtInheritRealize, /* realize */
175 NULL, /* actions */
176 0, /* num_actions */
177 resources, /* resources */
178 XtNumber(resources), /* num_resources */
179 NULLQUARK, /* xrm_class */
180 True, /* compress_motion */
181 XtExposeCompressMultiple /* compress_exposure */
182 | XtExposeGraphicsExposeMerged,
183 True, /* compress_enterleave */
184 False, /* visible_interest */
185 XawStripChartDestroy, /* destroy */
186 XawStripChartResize, /* resize */
187 XawStripChartRedisplay, /* expose */
188 XawStripChartSetValues, /* set_values */
189 NULL, /* set_values_hook */
190 NULL, /* set_values_almost */
191 NULL, /* get_values_hook */
192 NULL, /* accept_focus */
193 XtVersion, /* version */
194 NULL, /* callback_private */
195 NULL, /* tm_table */
196 XtInheritQueryGeometry, /* query_geometry */
197 XtInheritDisplayAccelerator, /* display_accelerator */
198 NULL, /* extension */
199 },
200 /* simple */
201 {
202 XtInheritChangeSensitive, /* change_sensitive */
203 #ifndef OLDXAW
204 NULL
205 #endif
206 },
207 /* strip_chart_class */
208 {
209 NULL
210 }
211 };
212
213 WidgetClass stripChartWidgetClass = (WidgetClass)&stripChartClassRec;
214
215 /*
216 * Implementation
217 */
218 /*
219 * Function:
220 * CreateGC
221 *
222 * Parameters:
223 * w - strip chart widget
224 * which - GC's to create
225 *
226 * Description:
227 * Creates the GC's
228 */
229 static void
CreateGC(StripChartWidget w,unsigned int which)230 CreateGC(StripChartWidget w, unsigned int which)
231 {
232 XGCValues myXGCV;
233
234 if (which & FOREGROUND) {
235 myXGCV.foreground = w->strip_chart.fgpixel;
236 w->strip_chart.fgGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
237 }
238
239 if (which & HIGHLIGHT) {
240 myXGCV.foreground = w->strip_chart.hipixel;
241 w->strip_chart.hiGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
242 }
243 }
244
245 /*
246 * Function:
247 * DestroyGC
248 *
249 * Arguments:
250 * w - strip chart widget
251 * which - which GC's to destroy
252 *
253 * Description:
254 * Destroys the GC's
255 */
256 static void
DestroyGC(StripChartWidget w,unsigned int which)257 DestroyGC(StripChartWidget w, unsigned int which)
258 {
259 if (which & FOREGROUND)
260 XtReleaseGC((Widget)w, w->strip_chart.fgGC);
261
262 if (which & HIGHLIGHT)
263 XtReleaseGC((Widget)w, w->strip_chart.hiGC);
264 }
265
266 /*ARGSUSED*/
267 static void
XawStripChartInitialize(Widget greq _X_UNUSED,Widget gnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)268 XawStripChartInitialize(Widget greq _X_UNUSED, Widget gnew,
269 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
270 {
271 StripChartWidget w = (StripChartWidget)gnew;
272
273 if (w->strip_chart.update > 0)
274 w->strip_chart.interval_id =
275 XtAppAddTimeOut(XtWidgetToApplicationContext(gnew),
276 (unsigned long)(w->strip_chart.update * MS_PER_SEC),
277 draw_it, (XtPointer)gnew);
278 CreateGC(w, ALL_GCS);
279
280 w->strip_chart.scale = w->strip_chart.min_scale;
281 w->strip_chart.interval = 0;
282 w->strip_chart.max_value = 0.0;
283 w->strip_chart.points = NULL;
284 XawStripChartResize(gnew);
285 }
286
287 static void
XawStripChartDestroy(Widget gw)288 XawStripChartDestroy(Widget gw)
289 {
290 StripChartWidget w = (StripChartWidget)gw;
291
292 if (w->strip_chart.update > 0)
293 XtRemoveTimeOut(w->strip_chart.interval_id);
294 if (w->strip_chart.points)
295 XtFree((char *)w->strip_chart.points);
296 DestroyGC(w, ALL_GCS);
297 }
298
299 /*
300 * NOTE: This function really needs to recieve graphics exposure
301 * events, but since this is not easily supported until R4 I am
302 * going to hold off until then.
303 */
304 /*ARGSUSED*/
305 static void
XawStripChartRedisplay(Widget w,XEvent * event,Region region _X_UNUSED)306 XawStripChartRedisplay(Widget w, XEvent *event, Region region _X_UNUSED)
307 {
308 if (event->type == GraphicsExpose)
309 (void)repaint_window((StripChartWidget)w, event->xgraphicsexpose.x,
310 event->xgraphicsexpose.width);
311 else
312 (void)repaint_window((StripChartWidget)w, event->xexpose.x,
313 event->xexpose.width);
314 }
315
316 /*ARGSUSED*/
317 static void
draw_it(XtPointer client_data,XtIntervalId * id _X_UNUSED)318 draw_it(XtPointer client_data, XtIntervalId *id _X_UNUSED)
319 {
320 StripChartWidget w = (StripChartWidget)client_data;
321 double value;
322
323 if (w->strip_chart.update > 0)
324 w->strip_chart.interval_id =
325 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w),
326 (unsigned long)(w->strip_chart.update * MS_PER_SEC), draw_it,
327 client_data);
328
329 if ((w->strip_chart.interval >= XtWidth(w)) ||
330 ((Cardinal)w->strip_chart.interval >= NUM_VALUEDATA(w)))
331 MoveChart((StripChartWidget)w, True);
332
333 /* Get the value, stash the point and draw corresponding line */
334 if (w->strip_chart.get_value == NULL)
335 return;
336
337 XtCallCallbacks((Widget)w, XtNgetValue, (XtPointer)&value);
338
339 /*
340 * Keep w->strip_chart.max_value up to date, and if this data
341 * point is off the graph, change the scale to make it fit
342 */
343 if (value > w->strip_chart.max_value) {
344 w->strip_chart.max_value = value;
345 if (XtIsRealized((Widget)w) &&
346 w->strip_chart.max_value > w->strip_chart.scale) {
347 XClearWindow(XtDisplay(w), XtWindow(w));
348 w->strip_chart.interval = repaint_window(w, 0, XtWidth(w));
349 }
350 }
351
352 w->strip_chart.valuedata[w->strip_chart.interval] = value;
353 if (XtIsRealized((Widget)w)) {
354 int y = (int)(XtHeight(w) - XtHeight(w) * value
355 / w->strip_chart.scale);
356
357 XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
358 w->strip_chart.interval, y,
359 1, (unsigned)(XtHeight(w) - y));
360
361 /*
362 * Fill in the graph lines we just painted over
363 */
364 if (w->strip_chart.points != NULL) {
365 w->strip_chart.points[0].x = (short)w->strip_chart.interval;
366 XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
367 w->strip_chart.points, w->strip_chart.scale - 1,
368 CoordModePrevious);
369 }
370
371 XFlush(XtDisplay(w)); /* Flush output buffers */
372 }
373 w->strip_chart.interval++; /* Next point */
374 }
375
376 /* Blts data according to current size, then redraws the stripChart window
377 * Next represents the number of valid points in data. Returns the (possibly)
378 * adjusted value of next. If next is 0, this routine draws an empty window
379 * (scale - 1 lines for graph). If next is less than the current window width,
380 * the returned value is identical to the initial value of next and data is
381 * unchanged. Otherwise keeps half a window's worth of data. If data is
382 * changed, then w->strip_chart.max_value is updated to reflect the
383 * largest data point
384 */
385 static int
repaint_window(StripChartWidget w,int left,int width)386 repaint_window(StripChartWidget w, int left, int width)
387 {
388 int i, j;
389 int next = w->strip_chart.interval;
390 int scale = w->strip_chart.scale;
391 int scalewidth = 0;
392
393 /* Compute the minimum scale required to graph the data, but don't go
394 lower than min_scale */
395 if (w->strip_chart.interval != 0 || scale <= w->strip_chart.max_value)
396 scale = (int)(w->strip_chart.max_value + 1);
397 if (scale < w->strip_chart.min_scale)
398 scale = w->strip_chart.min_scale;
399
400 if (scale != w->strip_chart.scale) {
401 w->strip_chart.scale = scale;
402 left = 0;
403 width = next;
404 scalewidth = XtWidth(w);
405
406 XawStripChartResize((Widget)w);
407
408 if (XtIsRealized((Widget)w))
409 XClearWindow(XtDisplay(w), XtWindow(w));
410 }
411
412 if (XtIsRealized((Widget)w)) {
413 Display *dpy = XtDisplay(w);
414 Window win = XtWindow(w);
415
416 width += left - 1;
417 if (!scalewidth)
418 scalewidth = width;
419
420 if (next < ++width)
421 width = next;
422
423 if ((Cardinal)width > NUM_VALUEDATA(w))
424 width = NUM_VALUEDATA(w);
425
426 /* Draw data point lines */
427 for (i = left; i < width; i++) {
428 int y = (int)(XtHeight(w) - (XtHeight(w) * w->strip_chart.valuedata[i])
429 / w->strip_chart.scale);
430
431 XFillRectangle(dpy, win, w->strip_chart.fgGC,
432 i, y, 1, (unsigned)(XtHeight(w) - y));
433 }
434
435 /* Draw graph reference lines */
436 for (i = 1; i < w->strip_chart.scale; i++) {
437 j = i * ((int)XtHeight(w) / w->strip_chart.scale);
438 XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
439 }
440 }
441 return (next);
442 }
443
444 /*
445 * Function:
446 * MoveChart
447 *
448 * Parameters:
449 * w - chart widget
450 * blit - blit the bits?
451 *
452 * Description:
453 * Moves the chart over when it would run off the end.
454 */
455 static void
MoveChart(StripChartWidget w,Bool blit)456 MoveChart(StripChartWidget w, Bool blit)
457 {
458 double old_max;
459 int left, i, j;
460 int next = w->strip_chart.interval;
461
462 if (!XtIsRealized((Widget)w))
463 return;
464
465 if (XtWidth(w) > NUM_VALUEDATA(w))
466 j = (int) NUM_VALUEDATA(w);
467 else
468 j = (int) XtWidth(w);
469 if (w->strip_chart.jump_val < 0)
470 w->strip_chart.jump_val = DEFAULT_JUMP;
471 if (w->strip_chart.jump_val == DEFAULT_JUMP)
472 j = j >> 1;
473 else {
474 j -= w->strip_chart.jump_val;
475 if (j < 0)
476 j = 0;
477 }
478
479 (void)memmove((char *)w->strip_chart.valuedata,
480 (char *)(w->strip_chart.valuedata + next - j),
481 (size_t)j * sizeof(double));
482 next = w->strip_chart.interval = j;
483
484 /*
485 * Since we just lost some data, recompute the
486 * w->strip_chart.max_value
487 */
488 old_max = w->strip_chart.max_value;
489 w->strip_chart.max_value = 0.0;
490 for (i = 0; i < next; i++) {
491 if (w->strip_chart.valuedata[i] > w->strip_chart.max_value)
492 w->strip_chart.max_value = w->strip_chart.valuedata[i];
493 }
494
495 if (!blit)
496 return;
497
498 if (old_max != w->strip_chart.max_value) {
499 XClearWindow(XtDisplay(w), XtWindow(w));
500 repaint_window(w, 0, XtWidth(w));
501 return;
502 }
503
504 XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
505 w->strip_chart.hiGC, (int)XtWidth(w) - j, 0, (unsigned)j, XtHeight(w), 0, 0);
506
507 XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
508 j, 0, (unsigned)(XtWidth(w) - j), XtHeight(w), False);
509
510 /* Draw graph reference lines */
511 left = j;
512 for (i = 1; i < w->strip_chart.scale; i++) {
513 j = i * (XtHeight(w) / w->strip_chart.scale);
514 XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w),
515 w->strip_chart.hiGC, left, j, XtWidth(w), j);
516 }
517 }
518
519 /*ARGSUSED*/
520 static Boolean
XawStripChartSetValues(Widget current,Widget request _X_UNUSED,Widget cnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)521 XawStripChartSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
522 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
523 {
524 StripChartWidget old = (StripChartWidget)current;
525 StripChartWidget w = (StripChartWidget)cnew;
526 Bool ret_val = False;
527 unsigned int new_gc = NO_GCS;
528
529 if (w->strip_chart.update != old->strip_chart.update) {
530 if (old->strip_chart.update > 0)
531 XtRemoveTimeOut(old->strip_chart.interval_id);
532 if (w->strip_chart.update > 0)
533 w->strip_chart.interval_id =
534 XtAppAddTimeOut(XtWidgetToApplicationContext(cnew),
535 (unsigned long)(w->strip_chart.update * MS_PER_SEC),
536 draw_it, (XtPointer)w);
537 }
538
539 if (w->strip_chart.min_scale > w->strip_chart.max_value + 1)
540 ret_val = True;
541
542 if (w->strip_chart.fgpixel != old->strip_chart.fgpixel) {
543 new_gc |= FOREGROUND;
544 ret_val = True;
545 }
546
547 if (w->strip_chart.hipixel != old->strip_chart.hipixel) {
548 new_gc |= HIGHLIGHT;
549 ret_val = True;
550 }
551
552 DestroyGC(old, new_gc);
553 CreateGC(w, new_gc);
554
555 return (Boolean)(ret_val);
556 }
557
558 /*
559 * Function:
560 * XawStripChartResize
561 *
562 * Parameters:
563 * w - StripChart widget
564 *
565 * Description:
566 * Sets up the polypoint that will be used to draw in the graph lines.
567 */
568 static void
XawStripChartResize(Widget widget)569 XawStripChartResize(Widget widget)
570 {
571 StripChartWidget w = (StripChartWidget)widget;
572 XPoint *points;
573 Cardinal size;
574 int i;
575
576 if (w->strip_chart.scale <= 1) {
577 XtFree((char *)w->strip_chart.points);
578 w->strip_chart.points = NULL;
579 return;
580 }
581
582 size = (Cardinal)(sizeof(XPoint) * (size_t)(w->strip_chart.scale - 1));
583
584 points = (XPoint *)XtRealloc((XtPointer)w->strip_chart.points, size);
585 w->strip_chart.points = points;
586
587 /* Draw graph reference lines into clip mask */
588
589 for (i = 1; i < w->strip_chart.scale; i++) {
590 points[i - 1].x = 0;
591 points[i - 1].y = (short)(XtHeight(w) / w->strip_chart.scale);
592 }
593 }
594