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