237cf66a5c0d14dbb68b9c4ca4f55ef803d9d056
[framework/uifw/xorg/lib/libxaw.git] / src / StripChart.c
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
61 /*
62  * Class Methods
63  */
64 static void XawStripChartInitialize(Widget, Widget, ArgList, Cardinal*);
65 static void XawStripChartDestroy(Widget);
66 static void XawStripChartRedisplay(Widget, XEvent*, Region);
67 static void XawStripChartResize(Widget);
68 static Boolean XawStripChartSetValues(Widget, Widget, Widget,
69                                       ArgList, Cardinal*);
70
71 /*
72  * Prototypes
73  */
74 static void CreateGC(StripChartWidget, unsigned int);
75 static void DestroyGC(StripChartWidget, unsigned int);
76 static void draw_it(XtPointer, XtIntervalId*);
77 static void MoveChart(StripChartWidget, Bool);
78 static int repaint_window(StripChartWidget, int, int);
79
80 /*
81  * Initialization
82  */
83 #define offset(field)   XtOffsetOf(StripChartRec, field)
84 static XtResource resources[] = {
85   {
86     XtNwidth,
87     XtCWidth,
88     XtRDimension,
89     sizeof(Dimension),
90     offset(core.width),
91     XtRImmediate,
92     (XtPointer)
93     120
94   },
95   {
96     XtNheight,
97     XtCHeight,
98     XtRDimension,
99     sizeof(Dimension),
100     offset(core.height),
101     XtRImmediate,
102     (XtPointer)120
103   },
104   {
105     XtNupdate,
106     XtCInterval,
107     XtRInt,
108     sizeof(int),
109     offset(strip_chart.update),
110     XtRImmediate,
111     (XtPointer)10
112   },
113   {
114     XtNminScale,
115     XtCScale,
116     XtRInt,
117     sizeof(int),
118     offset(strip_chart.min_scale),
119     XtRImmediate,
120     (XtPointer)1
121   },
122   {
123     XtNforeground,
124     XtCForeground,
125     XtRPixel,
126     sizeof(Pixel),
127     offset(strip_chart.fgpixel),
128     XtRString,
129     XtDefaultForeground
130   },
131   {
132     XtNhighlight,
133     XtCForeground,
134     XtRPixel,
135     sizeof(Pixel),
136     offset(strip_chart.hipixel),
137     XtRString,
138     XtDefaultForeground
139   },
140   {
141     XtNgetValue,
142     XtCCallback,
143     XtRCallback,
144     sizeof(XtPointer),
145     offset(strip_chart.get_value),
146     XtRImmediate,
147     NULL
148   },
149   {
150     XtNjumpScroll,
151     XtCJumpScroll,
152     XtRInt,
153     sizeof(int),
154     offset(strip_chart.jump_val),
155     XtRImmediate,
156     (XtPointer)DEFAULT_JUMP
157   },
158 };
159 #undef offset
160
161 StripChartClassRec stripChartClassRec = {
162   /* core */
163   {
164     (WidgetClass)&simpleClassRec,       /* superclass */
165     "StripChart",                       /* class_name */
166     sizeof(StripChartRec),              /* widget_size */
167     XawInitializeWidgetSet,             /* class_initialize */
168     NULL,                               /* class_part_initialize */
169     False,                              /* class_inited */
170     XawStripChartInitialize,            /* initialize */
171     NULL,                               /* initialize_hook */
172     XtInheritRealize,                   /* realize */
173     NULL,                               /* actions */
174     0,                                  /* num_actions */
175     resources,                          /* resources */
176     XtNumber(resources),                /* num_resources */
177     NULLQUARK,                          /* xrm_class */
178     True,                               /* compress_motion */
179     XtExposeCompressMultiple            /* compress_exposure */
180     | XtExposeGraphicsExposeMerged,
181     True,                               /* compress_enterleave */
182     False,                              /* visible_interest */
183     XawStripChartDestroy,               /* destroy */
184     XawStripChartResize,                /* resize */
185     XawStripChartRedisplay,             /* expose */
186     XawStripChartSetValues,             /* set_values */
187     NULL,                               /* set_values_hook */
188     NULL,                               /* set_values_almost */
189     NULL,                               /* get_values_hook */
190     NULL,                               /* accept_focus */
191     XtVersion,                          /* version */
192     NULL,                               /* callback_private */
193     NULL,                               /* tm_table */
194     XtInheritQueryGeometry,             /* query_geometry */
195     XtInheritDisplayAccelerator,        /* display_accelerator */
196     NULL,                               /* extension */
197   },
198   /* simple */
199   {
200     XtInheritChangeSensitive,           /* change_sensitive */
201   }
202 };
203
204 WidgetClass stripChartWidgetClass = (WidgetClass)&stripChartClassRec;
205
206 /*
207  * Implementation
208  */
209 /*
210  * Function:
211  *      CreateGC
212  *
213  * Parameters:
214  *      w     - strip chart widget
215  *      which - GC's to create
216  *
217  * Description:
218  *      Creates the GC's
219  */
220 static void
221 CreateGC(StripChartWidget w, unsigned int which)
222 {
223     XGCValues myXGCV;
224
225     if (which & FOREGROUND) {
226         myXGCV.foreground = w->strip_chart.fgpixel;
227         w->strip_chart.fgGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
228     }
229
230     if (which & HIGHLIGHT) {
231         myXGCV.foreground = w->strip_chart.hipixel;
232         w->strip_chart.hiGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
233     }
234 }
235
236 /*
237  * Function:
238  *      DestroyGC
239  *
240  * Arguments:
241  *      w     - strip chart widget
242  *      which - which GC's to destroy
243  *
244  * Description:
245  *      Destroys the GC's
246  */
247 static void
248 DestroyGC(StripChartWidget w, unsigned int which)
249 {
250     if (which & FOREGROUND) 
251         XtReleaseGC((Widget)w, w->strip_chart.fgGC);
252
253     if (which & HIGHLIGHT) 
254         XtReleaseGC((Widget)w, w->strip_chart.hiGC);
255 }
256
257 /*ARGSUSED*/
258 static void
259 XawStripChartInitialize(Widget greq, Widget gnew,
260                         ArgList args, Cardinal *num_args)
261 {
262     StripChartWidget w = (StripChartWidget)gnew;
263
264     if (w->strip_chart.update > 0)
265     w->strip_chart.interval_id =
266     XtAppAddTimeOut(XtWidgetToApplicationContext(gnew),
267                     w->strip_chart.update * MS_PER_SEC, 
268                     draw_it, (XtPointer)gnew);
269     CreateGC(w, ALL_GCS);
270
271     w->strip_chart.scale = w->strip_chart.min_scale;
272     w->strip_chart.interval = 0;
273     w->strip_chart.max_value = 0.0;
274     w->strip_chart.points = NULL;
275     XawStripChartResize(gnew);
276 }
277  
278 static void
279 XawStripChartDestroy(Widget gw)
280 {
281     StripChartWidget w = (StripChartWidget)gw;
282
283     if (w->strip_chart.update > 0)
284         XtRemoveTimeOut(w->strip_chart.interval_id);
285     if (w->strip_chart.points)
286         XtFree((char *)w->strip_chart.points);
287     DestroyGC(w, ALL_GCS);
288 }
289
290 /*
291  * NOTE: This function really needs to recieve graphics exposure 
292  *       events, but since this is not easily supported until R4 I am
293  *       going to hold off until then.
294  */
295 /*ARGSUSED*/
296 static void
297 XawStripChartRedisplay(Widget w, XEvent *event, Region region)
298 {
299     if (event->type == GraphicsExpose)
300         (void)repaint_window((StripChartWidget)w, event->xgraphicsexpose.x,
301                              event->xgraphicsexpose.width);
302     else
303         (void)repaint_window((StripChartWidget)w, event->xexpose.x,
304                              event->xexpose.width);
305 }
306
307 /*ARGSUSED*/
308 static void 
309 draw_it(XtPointer client_data, XtIntervalId *id)
310 {
311     StripChartWidget w = (StripChartWidget)client_data;
312     double value;
313    
314     if (w->strip_chart.update > 0)
315         w->strip_chart.interval_id =
316             XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w),
317                             w->strip_chart.update * MS_PER_SEC,draw_it,
318                             client_data);
319
320     if (w->strip_chart.interval >= XtWidth(w))
321         MoveChart((StripChartWidget)w, True);
322
323     /* Get the value, stash the point and draw corresponding line */
324     if (w->strip_chart.get_value == NULL)
325         return;
326
327     XtCallCallbacks((Widget)w, XtNgetValue, (XtPointer)&value);
328
329     /* 
330      * Keep w->strip_chart.max_value up to date, and if this data 
331      * point is off the graph, change the scale to make it fit
332      */
333     if (value > w->strip_chart.max_value) {
334         w->strip_chart.max_value = value;
335         if (XtIsRealized((Widget)w) &&
336             w->strip_chart.max_value > w->strip_chart.scale) {
337             XClearWindow(XtDisplay(w), XtWindow(w));
338             w->strip_chart.interval = repaint_window(w, 0, XtWidth(w));
339         }
340     }
341
342     w->strip_chart.valuedata[w->strip_chart.interval] = value;
343     if (XtIsRealized((Widget)w)) {
344         int y = (int)(XtHeight(w) - XtHeight(w) * value
345                      / w->strip_chart.scale);
346
347         XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
348                        w->strip_chart.interval, y, 
349                        1, XtHeight(w) - y);
350
351         /*
352          * Fill in the graph lines we just painted over
353          */
354         if (w->strip_chart.points != NULL) {
355             w->strip_chart.points[0].x = w->strip_chart.interval;
356             XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
357                         w->strip_chart.points, w->strip_chart.scale - 1,
358                         CoordModePrevious);
359         }
360
361         XFlush(XtDisplay(w));               /* Flush output buffers */
362     }
363     w->strip_chart.interval++;              /* Next point */
364 }
365
366 /* Blts data according to current size, then redraws the stripChart window
367  * Next represents the number of valid points in data.  Returns the (possibly)
368  * adjusted value of next.  If next is 0, this routine draws an empty window
369  * (scale - 1 lines for graph).  If next is less than the current window width,
370  * the returned value is identical to the initial value of next and data is
371  * unchanged.  Otherwise keeps half a window's worth of data.  If data is
372  * changed, then w->strip_chart.max_value is updated to reflect the
373  * largest data point
374  */
375 static int 
376 repaint_window(StripChartWidget w, int left, int width)
377 {
378     int i, j;
379     int next = w->strip_chart.interval;
380     int scale = w->strip_chart.scale;
381     int scalewidth = 0;
382
383     /* Compute the minimum scale required to graph the data, but don't go
384        lower than min_scale */
385     if (w->strip_chart.interval != 0 || scale <= w->strip_chart.max_value)
386         scale = w->strip_chart.max_value + 1;
387         if (scale < w->strip_chart.min_scale)
388             scale = w->strip_chart.min_scale;
389
390     if (scale != w->strip_chart.scale) {
391         w->strip_chart.scale = scale;
392         left = 0;
393         width = next;
394         scalewidth = XtWidth(w);
395
396         XawStripChartResize((Widget)w);
397
398         if (XtIsRealized((Widget)w))
399             XClearWindow(XtDisplay(w), XtWindow(w));
400     }
401
402     if (XtIsRealized((Widget)w)) {
403         Display *dpy = XtDisplay(w);
404         Window win = XtWindow(w);
405
406         width += left - 1;
407         if (!scalewidth)
408             scalewidth = width;
409
410         if (next < ++width)
411             width = next;
412
413         /* Draw data point lines */
414         for (i = left; i < width; i++) {
415             int y = XtHeight(w) - (XtHeight(w) * w->strip_chart.valuedata[i])
416                                    / w->strip_chart.scale;
417
418             XFillRectangle(dpy, win, w->strip_chart.fgGC, 
419                            i, y, 1, XtHeight(w) - y);
420         }
421
422         /* Draw graph reference lines */
423         for (i = 1; i < w->strip_chart.scale; i++) {
424             j = i * ((int)XtHeight(w) / w->strip_chart.scale);
425             XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
426         }
427     }
428     return (next);
429 }
430
431 /*
432  * Function:
433  *      MoveChart
434  *
435  * Parameters:
436  *      w - chart widget
437  *      blit - blit the bits?
438  *
439  * Description:
440  *      Moves the chart over when it would run off the end.
441  */
442 static void
443 MoveChart(StripChartWidget w, Bool blit)
444 {
445     double old_max;
446     int left, i, j;
447     int next = w->strip_chart.interval;
448
449     if (!XtIsRealized((Widget)w))
450         return;
451
452     if (w->strip_chart.jump_val < 0)
453         w->strip_chart.jump_val = DEFAULT_JUMP;
454     if (w->strip_chart.jump_val == DEFAULT_JUMP)
455         j = XtWidth(w) >> 1;
456     else {
457         j = (int)XtWidth(w) - w->strip_chart.jump_val;
458         if (j < 0)
459             j = 0;
460     }
461
462     (void)memmove((char *)w->strip_chart.valuedata,
463                   (char *)(w->strip_chart.valuedata + next - j),
464                   j * sizeof(double));
465     next = w->strip_chart.interval = j;
466         
467     /*
468      * Since we just lost some data, recompute the 
469      * w->strip_chart.max_value
470      */
471     old_max = w->strip_chart.max_value;
472     w->strip_chart.max_value = 0.0;
473     for (i = 0; i < next; i++) {
474         if (w->strip_chart.valuedata[i] > w->strip_chart.max_value) 
475             w->strip_chart.max_value = w->strip_chart.valuedata[i];
476     }
477
478     if (!blit)
479         return;
480
481     if (old_max != w->strip_chart.max_value) {
482         XClearWindow(XtDisplay(w), XtWindow(w));
483         repaint_window(w, 0, XtWidth(w));
484         return;
485     }
486
487     XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
488               w->strip_chart.hiGC, (int)XtWidth(w) - j, 0, j, XtHeight(w), 0, 0);
489
490     XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w), 
491                j, 0, XtWidth(w) - j, XtHeight(w), False);
492
493     /* Draw graph reference lines */
494     left = j;
495     for (i = 1; i < w->strip_chart.scale; i++) {
496         j = i * (XtHeight(w) / w->strip_chart.scale);
497         XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w),
498                   w->strip_chart.hiGC, left, j, XtWidth(w), j);
499     }
500 }
501
502 /*ARGSUSED*/
503 static Boolean
504 XawStripChartSetValues(Widget current, Widget request, Widget cnew,
505                        ArgList args, Cardinal *num_args)
506 {
507     StripChartWidget old = (StripChartWidget)current;
508     StripChartWidget w = (StripChartWidget)cnew;
509     Bool ret_val = False;
510     unsigned int new_gc = NO_GCS;
511
512     if (w->strip_chart.update != old->strip_chart.update) {
513         if (old->strip_chart.update > 0)
514             XtRemoveTimeOut(old->strip_chart.interval_id);
515         if (w->strip_chart.update > 0)
516             w->strip_chart.interval_id =
517                 XtAppAddTimeOut(XtWidgetToApplicationContext(cnew),
518                                 w->strip_chart.update * MS_PER_SEC,
519                                 draw_it, (XtPointer)w);
520     }
521
522     if (w->strip_chart.min_scale > w->strip_chart.max_value + 1)
523         ret_val = True;
524      
525     if (w->strip_chart.fgpixel != old->strip_chart.fgpixel) {
526         new_gc |= FOREGROUND;
527         ret_val = True;
528     }
529     
530     if (w->strip_chart.hipixel != old->strip_chart.hipixel) {
531         new_gc |= HIGHLIGHT;
532         ret_val = True;
533     }
534     
535     DestroyGC(old, new_gc);
536     CreateGC(w, new_gc);
537
538     return (ret_val);
539 }
540
541 /*
542  * Function:
543  *      XawStripChartResize
544  *
545  * Parameters:
546  *      w - StripChart widget
547  *
548  * Description:
549  *      Sets up the polypoint that will be used to draw in the graph lines.
550  */
551 static void
552 XawStripChartResize(Widget widget)
553 {
554     StripChartWidget w = (StripChartWidget)widget;
555     XPoint *points;
556     Cardinal size;
557     int i;
558
559     if (w->strip_chart.scale <= 1) {
560         XtFree((char *)w->strip_chart.points);
561         w->strip_chart.points = NULL;
562         return;
563     }
564     
565     size = sizeof(XPoint) * (w->strip_chart.scale - 1);
566
567     points = (XPoint *)XtRealloc((XtPointer)w->strip_chart.points, size);
568     w->strip_chart.points = points;
569
570     /* Draw graph reference lines into clip mask */
571
572     for (i = 1; i < w->strip_chart.scale; i++) {
573         points[i - 1].x = 0;
574         points[i - 1].y = XtHeight(w) / w->strip_chart.scale;
575     }
576 }