1 /***********************************************************
2 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
3 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the names of Digital or MIT not be
12 used in advertising or publicity pertaining to distribution of the
13 software without specific, written prior permission.
15 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 ******************************************************************/
25 /* This is the Athena StripChart widget, slightly hacked by
26 Philip Hazel <ph10@cus.cam.ac.uk> in order to give access to
27 its repaint_window function so that a repaint can be forced.
29 The repaint_window function has also been nobbled so that it only
30 ever changes scale to 10. There is probably a better way to handle
31 this - such as inventing some new resources, but I'm not up to
32 that just at the moment.
34 On SunOS4 there are name clashes when trying to link this with the
35 Athena library. So to avoid them, rename a few things by inserting
36 "my" at the front of "strip". */
40 #include <X11/IntrinsicP.h>
41 #include <X11/StringDefs.h>
42 #include <X11/Xaw/XawInit.h>
43 #include <X11/Xaw/StripCharP.h>
44 #include <X11/Xfuncs.h>
46 #define MS_PER_SEC 1000
50 #define offset(field) XtOffsetOf(StripChartRec, field)
52 static XtResource resources[] = {
53 {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
54 offset(core.width), XtRImmediate, (XtPointer) 120},
55 {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
56 offset(core.height), XtRImmediate, (XtPointer) 120},
57 {XtNupdate, XtCInterval, XtRInt, sizeof(int),
58 offset(strip_chart.update), XtRImmediate, (XtPointer) 10},
59 {XtNminScale, XtCScale, XtRInt, sizeof(int),
60 offset(strip_chart.min_scale), XtRImmediate, (XtPointer) 1},
61 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
62 offset(strip_chart.fgpixel), XtRString, XtDefaultForeground},
63 {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
64 offset(strip_chart.hipixel), XtRString, XtDefaultForeground},
65 {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer),
66 offset(strip_chart.get_value), XtRImmediate, (XtPointer) NULL},
67 {XtNjumpScroll, XtCJumpScroll, XtRInt, sizeof(int),
68 offset(strip_chart.jump_val), XtRImmediate, (XtPointer) DEFAULT_JUMP},
73 /* Added argument types to these to shut picky compilers up. PH */
75 static void CreateGC(StripChartWidget, unsigned int);
76 static void DestroyGC(StripChartWidget, unsigned int);
77 static void Initialize(), Destroy(), Redisplay();
78 static void MoveChart(StripChartWidget, Boolean);
79 static void SetPoints(StripChartWidget);
80 static Boolean SetValues();
82 int repaint_window(StripChartWidget, int, int); /* PH hack */
83 /* static int repaint_window(); */
85 StripChartClassRec stripChartClassRec = {
87 /* superclass */ (WidgetClass) &simpleClassRec,
88 /* class_name */ "StripChart",
89 /* size */ sizeof(StripChartRec),
90 /* class_initialize */ XawInitializeWidgetSet,
91 /* class_part_initialize */ NULL,
92 /* class_inited */ FALSE,
93 /* initialize */ Initialize,
94 /* initialize_hook */ NULL,
95 /* realize */ XtInheritRealize,
98 /* resources */ resources,
99 /* num_resources */ XtNumber(resources),
100 /* xrm_class */ NULLQUARK,
101 /* compress_motion */ TRUE,
102 /* compress_exposure */ XtExposeCompressMultiple |
103 XtExposeGraphicsExposeMerged,
104 /* compress_enterleave */ TRUE,
105 /* visible_interest */ FALSE,
106 /* destroy */ Destroy,
107 /* resize */ (void (*)(Widget))SetPoints,
108 /* expose */ Redisplay,
109 /* set_values */ SetValues,
110 /* set_values_hook */ NULL,
111 /* set_values_almost */ NULL,
112 /* get_values_hook */ NULL,
113 /* accept_focus */ NULL,
114 /* version */ XtVersion,
115 /* callback_private */ NULL,
117 /* query_geometry */ XtInheritQueryGeometry,
118 /* display_accelerator */ XtInheritDisplayAccelerator,
121 { /* Simple class fields */
122 /* change_sensitive */ XtInheritChangeSensitive
126 WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec;
128 /****************************************************************
132 ****************************************************************/
134 static void draw_it();
136 /* Function Name: CreateGC
137 * Description: Creates the GC's
138 * Arguments: w - the strip chart widget.
139 * which - which GC's to create.
150 if (which & FOREGROUND) {
151 myXGCV.foreground = w->strip_chart.fgpixel;
152 w->strip_chart.fgGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
155 if (which & HIGHLIGHT) {
156 myXGCV.foreground = w->strip_chart.hipixel;
157 w->strip_chart.hiGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
161 /* Function Name: DestroyGC
162 * Description: Destroys the GC's
163 * Arguments: w - the strip chart widget.
164 * which - which GC's to destroy.
173 if (which & FOREGROUND)
174 XtReleaseGC((Widget) w, w->strip_chart.fgGC);
176 if (which & HIGHLIGHT)
177 XtReleaseGC((Widget) w, w->strip_chart.hiGC);
181 static void Initialize (greq, gnew)
184 StripChartWidget w = (StripChartWidget)gnew;
186 if (w->strip_chart.update > 0)
187 w->strip_chart.interval_id = XtAppAddTimeOut(
188 XtWidgetToApplicationContext(gnew),
189 w->strip_chart.update * MS_PER_SEC,
190 draw_it, (XtPointer) gnew);
191 CreateGC(w, (unsigned int) ALL_GCS);
193 w->strip_chart.scale = w->strip_chart.min_scale;
194 w->strip_chart.interval = 0;
195 w->strip_chart.max_value = 0.0;
196 w->strip_chart.points = NULL;
200 static void Destroy (gw)
203 StripChartWidget w = (StripChartWidget)gw;
205 if (w->strip_chart.update > 0)
206 XtRemoveTimeOut (w->strip_chart.interval_id);
207 if (w->strip_chart.points)
208 XtFree((char *) w->strip_chart.points);
209 DestroyGC(w, (unsigned int) ALL_GCS);
213 * NOTE: This function really needs to receive graphics exposure
214 * events, but since this is not easily supported until R4 I am
215 * going to hold off until then.
219 static void Redisplay(w, event, region)
224 if (event->type == GraphicsExpose)
225 (void) repaint_window ((StripChartWidget)w, event->xgraphicsexpose.x,
226 event->xgraphicsexpose.width);
228 (void) repaint_window ((StripChartWidget)w, event->xexpose.x,
229 event->xexpose.width);
234 draw_it(client_data, id)
235 XtPointer client_data;
236 XtIntervalId *id; /* unused */
238 StripChartWidget w = (StripChartWidget)client_data;
241 if (w->strip_chart.update > 0)
242 w->strip_chart.interval_id =
243 XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) w),
244 w->strip_chart.update * MS_PER_SEC,draw_it,client_data);
246 if (w->strip_chart.interval >= (int)w->core.width)
247 MoveChart( (StripChartWidget) w, TRUE);
249 /* Get the value, stash the point and draw corresponding line. */
251 if (w->strip_chart.get_value == NULL)
254 XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value );
257 * Keep w->strip_chart.max_value up to date, and if this data
258 * point is off the graph, change the scale to make it fit.
261 if (value > w->strip_chart.max_value) {
262 w->strip_chart.max_value = value;
263 if (w->strip_chart.max_value > w->strip_chart.scale) {
264 XClearWindow( XtDisplay (w), XtWindow (w));
265 w->strip_chart.interval = repaint_window(w, 0, (int) w->core.width);
269 w->strip_chart.valuedata[w->strip_chart.interval] = value;
270 if (XtIsRealized((Widget)w)) {
271 int y = (int) (w->core.height
272 - (int)(w->core.height * value) / w->strip_chart.scale);
274 XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
275 w->strip_chart.interval, y,
276 (unsigned int) 1, w->core.height - y);
278 * Fill in the graph lines we just painted over.
281 if (w->strip_chart.points != NULL) {
282 w->strip_chart.points[0].x = w->strip_chart.interval;
283 XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
284 w->strip_chart.points, w->strip_chart.scale - 1,
288 XFlush(XtDisplay(w)); /* Flush output buffers */
290 w->strip_chart.interval++; /* Next point */
293 /* Blts data according to current size, then redraws the stripChart window.
294 * Next represents the number of valid points in data. Returns the (possibly)
295 * adjusted value of next. If next is 0, this routine draws an empty window
296 * (scale - 1 lines for graph). If next is less than the current window width,
297 * the returned value is identical to the initial value of next and data is
298 * unchanged. Otherwise keeps half a window's worth of data. If data is
299 * changed, then w->strip_chart.max_value is updated to reflect the
300 * largest data point.
305 repaint_window(w, left, width)
310 register int next = w->strip_chart.interval;
311 int scale = w->strip_chart.scale;
314 /* Compute the minimum scale required to graph the data, but don't go
315 lower than min_scale. */
316 if (w->strip_chart.interval != 0 || scale <= (int)w->strip_chart.max_value)
317 scale = ((int) (w->strip_chart.max_value)) + 1;
318 if (scale < w->strip_chart.min_scale)
319 scale = w->strip_chart.min_scale;
321 /* if (scale != w->strip_chart.scale) { */
323 if (scale != w->strip_chart.scale && scale == 10) {
324 w->strip_chart.scale = scale;
327 scalewidth = w->core.width;
331 if (XtIsRealized ((Widget) w))
332 XClearWindow (XtDisplay (w), XtWindow (w));
336 if (XtIsRealized((Widget)w)) {
337 Display *dpy = XtDisplay(w);
338 Window win = XtWindow(w);
341 if (!scalewidth) scalewidth = width;
343 if (next < ++width) width = next;
345 /* Draw data point lines. */
346 for (i = left; i < width; i++) {
347 int y = (int) (w->core.height -
348 (int)(w->core.height * w->strip_chart.valuedata[i]) /
349 w->strip_chart.scale);
351 XFillRectangle(dpy, win, w->strip_chart.fgGC,
352 i, y, (unsigned int) 1,
353 (unsigned int) (w->core.height - y));
356 /* Draw graph reference lines */
357 for (i = 1; i < w->strip_chart.scale; i++) {
358 j = i * ((int)w->core.height / w->strip_chart.scale);
359 XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
365 /* Function Name: MoveChart
366 * Description: moves the chart over when it would run off the end.
367 * Arguments: w - the load widget.
368 * blit - blit the bits? (TRUE/FALSE).
373 MoveChart(StripChartWidget w, Boolean blit)
377 register int next = w->strip_chart.interval;
379 if (!XtIsRealized((Widget) w)) return;
381 if (w->strip_chart.jump_val == DEFAULT_JUMP)
382 j = w->core.width >> 1; /* Half the window width. */
384 j = w->core.width - w->strip_chart.jump_val;
388 bcopy((char *)(w->strip_chart.valuedata + next - j),
389 (char *)(w->strip_chart.valuedata), j * sizeof(double));
390 next = w->strip_chart.interval = j;
393 * Since we just lost some data, recompute the
394 * w->strip_chart.max_value.
397 old_max = w->strip_chart.max_value;
398 w->strip_chart.max_value = 0.0;
399 for (i = 0; i < next; i++) {
400 if (w->strip_chart.valuedata[i] > w->strip_chart.max_value)
401 w->strip_chart.max_value = w->strip_chart.valuedata[i];
404 if (!blit) return; /* we are done... */
406 if ( ((int) old_max) != ( (int) w->strip_chart.max_value) ) {
407 XClearWindow(XtDisplay(w), XtWindow(w));
408 repaint_window(w, 0, (int) w->core.width);
412 XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
413 w->strip_chart.hiGC, (int) w->core.width - j, 0,
414 (unsigned int) j, (unsigned int) w->core.height,
417 XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
419 (unsigned int) w->core.width - j, (unsigned int)w->core.height,
422 /* Draw graph reference lines */
424 for (i = 1; i < w->strip_chart.scale; i++) {
425 j = i * ((int)w->core.height / w->strip_chart.scale);
426 XDrawLine(XtDisplay((Widget) w), XtWindow( (Widget) w),
427 w->strip_chart.hiGC, left, j, (int)w->core.width, j);
433 static Boolean SetValues (current, request, new)
434 Widget current, request, new;
436 StripChartWidget old = (StripChartWidget)current;
437 StripChartWidget w = (StripChartWidget)new;
438 Boolean ret_val = FALSE;
439 unsigned int new_gc = NO_GCS;
441 if (w->strip_chart.update != old->strip_chart.update) {
442 if (old->strip_chart.update > 0)
443 XtRemoveTimeOut (old->strip_chart.interval_id);
444 if (w->strip_chart.update > 0)
445 w->strip_chart.interval_id =
446 XtAppAddTimeOut(XtWidgetToApplicationContext(new),
447 w->strip_chart.update * MS_PER_SEC,
448 draw_it, (XtPointer)w);
451 if ( w->strip_chart.min_scale > (int) ((w->strip_chart.max_value) + 1) )
454 if ( w->strip_chart.fgpixel != old->strip_chart.fgpixel ) {
455 new_gc |= FOREGROUND;
459 if ( w->strip_chart.hipixel != old->strip_chart.hipixel ) {
464 DestroyGC(old, new_gc);
470 /* Function Name: SetPoints
471 * Description: Sets up the polypoint that will be used to draw in
473 * Arguments: w - the StripChart widget.
477 #define HEIGHT ( (unsigned int) w->core.height)
487 if (w->strip_chart.scale <= 1) { /* no scale lines. */
488 XtFree ((char *) w->strip_chart.points);
489 w->strip_chart.points = NULL;
493 size = sizeof(XPoint) * (w->strip_chart.scale - 1);
495 points = (XPoint *) XtRealloc( (XtPointer) w->strip_chart.points, size);
496 w->strip_chart.points = points;
498 /* Draw graph reference lines into clip mask */
500 for (i = 1; i < w->strip_chart.scale; i++) {
502 points[i - 1].y = HEIGHT / w->strip_chart.scale;