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