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