Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /*********************************************************** |
2 | Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, | |
3 | and the Massachusetts Institute of Technology, Cambridge, Massachusetts. | |
4 | ||
5 | All Rights Reserved | |
6 | ||
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. | |
14 | ||
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 | |
21 | SOFTWARE. | |
22 | ||
23 | ******************************************************************/ | |
24 | ||
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. | |
28 | ||
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. | |
33 | ||
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". */ | |
37 | ||
38 | ||
39 | #include <stdio.h> | |
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> | |
45 | ||
46 | #define MS_PER_SEC 1000 | |
47 | ||
48 | /* Private Data */ | |
49 | ||
50 | #define offset(field) XtOffsetOf(StripChartRec, field) | |
51 | ||
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}, | |
69 | }; | |
70 | ||
71 | #undef offset | |
72 | ||
73 | /* Added argument types to these to shut picky compilers up. PH */ | |
74 | ||
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(); | |
81 | ||
82 | int repaint_window(StripChartWidget, int, int); /* PH hack */ | |
83 | /* static int repaint_window(); */ | |
84 | ||
85 | StripChartClassRec stripChartClassRec = { | |
86 | { /* core fields */ | |
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, | |
96 | /* actions */ NULL, | |
97 | /* num_actions */ 0, | |
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, | |
116 | /* tm_table */ NULL, | |
117 | /* query_geometry */ XtInheritQueryGeometry, | |
118 | /* display_accelerator */ XtInheritDisplayAccelerator, | |
119 | /* extension */ NULL | |
120 | }, | |
121 | { /* Simple class fields */ | |
122 | /* change_sensitive */ XtInheritChangeSensitive | |
123 | } | |
124 | }; | |
125 | ||
126 | WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec; | |
127 | ||
128 | /**************************************************************** | |
129 | * | |
130 | * Private Procedures | |
131 | * | |
132 | ****************************************************************/ | |
133 | ||
134 | static void draw_it(); | |
135 | ||
136 | /* Function Name: CreateGC | |
137 | * Description: Creates the GC's | |
138 | * Arguments: w - the strip chart widget. | |
139 | * which - which GC's to create. | |
140 | * Returns: none | |
141 | */ | |
142 | ||
143 | static void | |
144 | CreateGC(w, which) | |
145 | StripChartWidget w; | |
146 | unsigned int which; | |
147 | { | |
148 | XGCValues myXGCV; | |
149 | ||
150 | if (which & FOREGROUND) { | |
151 | myXGCV.foreground = w->strip_chart.fgpixel; | |
152 | w->strip_chart.fgGC = XtGetGC((Widget) w, GCForeground, &myXGCV); | |
153 | } | |
154 | ||
155 | if (which & HIGHLIGHT) { | |
156 | myXGCV.foreground = w->strip_chart.hipixel; | |
157 | w->strip_chart.hiGC = XtGetGC((Widget) w, GCForeground, &myXGCV); | |
158 | } | |
159 | } | |
160 | ||
161 | /* Function Name: DestroyGC | |
162 | * Description: Destroys the GC's | |
163 | * Arguments: w - the strip chart widget. | |
164 | * which - which GC's to destroy. | |
165 | * Returns: none | |
166 | */ | |
167 | ||
168 | static void | |
169 | DestroyGC(w, which) | |
170 | StripChartWidget w; | |
171 | unsigned int which; | |
172 | { | |
173 | if (which & FOREGROUND) | |
174 | XtReleaseGC((Widget) w, w->strip_chart.fgGC); | |
175 | ||
176 | if (which & HIGHLIGHT) | |
177 | XtReleaseGC((Widget) w, w->strip_chart.hiGC); | |
178 | } | |
179 | ||
180 | /* ARGSUSED */ | |
181 | static void Initialize (greq, gnew) | |
182 | Widget greq, gnew; | |
183 | { | |
184 | StripChartWidget w = (StripChartWidget)gnew; | |
185 | ||
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); | |
192 | ||
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; | |
197 | SetPoints(w); | |
198 | } | |
199 | ||
200 | static void Destroy (gw) | |
201 | Widget gw; | |
202 | { | |
203 | StripChartWidget w = (StripChartWidget)gw; | |
204 | ||
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); | |
210 | } | |
211 | ||
212 | /* | |
213 | * NOTE: This function really needs to recieve graphics exposure | |
214 | * events, but since this is not easily supported until R4 I am | |
215 | * going to hold off until then. | |
216 | */ | |
217 | ||
218 | /* ARGSUSED */ | |
219 | static void Redisplay(w, event, region) | |
220 | Widget w; | |
221 | XEvent *event; | |
222 | Region region; | |
223 | { | |
224 | if (event->type == GraphicsExpose) | |
225 | (void) repaint_window ((StripChartWidget)w, event->xgraphicsexpose.x, | |
226 | event->xgraphicsexpose.width); | |
227 | else | |
228 | (void) repaint_window ((StripChartWidget)w, event->xexpose.x, | |
229 | event->xexpose.width); | |
230 | } | |
231 | ||
232 | /* ARGSUSED */ | |
233 | static void | |
234 | draw_it(client_data, id) | |
235 | XtPointer client_data; | |
236 | XtIntervalId *id; /* unused */ | |
237 | { | |
238 | StripChartWidget w = (StripChartWidget)client_data; | |
239 | double value; | |
240 | ||
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); | |
245 | ||
246 | if (w->strip_chart.interval >= (int)w->core.width) | |
247 | MoveChart( (StripChartWidget) w, TRUE); | |
248 | ||
249 | /* Get the value, stash the point and draw corresponding line. */ | |
250 | ||
251 | if (w->strip_chart.get_value == NULL) | |
252 | return; | |
253 | ||
254 | XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value ); | |
255 | ||
256 | /* | |
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. | |
259 | */ | |
260 | ||
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); | |
266 | } | |
267 | } | |
268 | ||
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); | |
273 | ||
274 | XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC, | |
275 | w->strip_chart.interval, y, | |
276 | (unsigned int) 1, w->core.height - y); | |
277 | /* | |
278 | * Fill in the graph lines we just painted over. | |
279 | */ | |
280 | ||
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, | |
285 | CoordModePrevious); | |
286 | } | |
287 | ||
288 | XFlush(XtDisplay(w)); /* Flush output buffers */ | |
289 | } | |
290 | w->strip_chart.interval++; /* Next point */ | |
291 | } /* draw_it */ | |
292 | ||
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. | |
301 | */ | |
302 | ||
303 | /* static int */ | |
304 | int /* PH hack */ | |
305 | repaint_window(w, left, width) | |
306 | StripChartWidget w; | |
307 | int left, width; | |
308 | { | |
309 | register int i, j; | |
310 | register int next = w->strip_chart.interval; | |
311 | int scale = w->strip_chart.scale; | |
312 | int scalewidth = 0; | |
313 | ||
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; | |
320 | ||
321 | /* if (scale != w->strip_chart.scale) { */ | |
322 | ||
323 | if (scale != w->strip_chart.scale && scale == 10) { | |
324 | w->strip_chart.scale = scale; | |
325 | left = 0; | |
326 | width = next; | |
327 | scalewidth = w->core.width; | |
328 | ||
329 | SetPoints(w); | |
330 | ||
331 | if (XtIsRealized ((Widget) w)) | |
332 | XClearWindow (XtDisplay (w), XtWindow (w)); | |
333 | ||
334 | } | |
335 | ||
336 | if (XtIsRealized((Widget)w)) { | |
337 | Display *dpy = XtDisplay(w); | |
338 | Window win = XtWindow(w); | |
339 | ||
340 | width += left - 1; | |
341 | if (!scalewidth) scalewidth = width; | |
342 | ||
343 | if (next < ++width) width = next; | |
344 | ||
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); | |
350 | ||
351 | XFillRectangle(dpy, win, w->strip_chart.fgGC, | |
352 | i, y, (unsigned int) 1, | |
353 | (unsigned int) (w->core.height - y)); | |
354 | } | |
355 | ||
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); | |
360 | } | |
361 | } | |
362 | return(next); | |
363 | } | |
364 | ||
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). | |
369 | * Returns: none. | |
370 | */ | |
371 | ||
372 | static void | |
373 | MoveChart(StripChartWidget w, Boolean blit) | |
374 | { | |
375 | double old_max; | |
376 | int left, i, j; | |
377 | register int next = w->strip_chart.interval; | |
378 | ||
379 | if (!XtIsRealized((Widget) w)) return; | |
380 | ||
381 | if (w->strip_chart.jump_val == DEFAULT_JUMP) | |
382 | j = w->core.width >> 1; /* Half the window width. */ | |
383 | else { | |
384 | j = w->core.width - w->strip_chart.jump_val; | |
385 | if (j < 0) j = 0; | |
386 | } | |
387 | ||
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; | |
391 | ||
392 | /* | |
393 | * Since we just lost some data, recompute the | |
394 | * w->strip_chart.max_value. | |
395 | */ | |
396 | ||
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]; | |
402 | } | |
403 | ||
404 | if (!blit) return; /* we are done... */ | |
405 | ||
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); | |
409 | return; | |
410 | } | |
411 | ||
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, | |
415 | 0, 0); | |
416 | ||
417 | XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w), | |
418 | (int) j, 0, | |
419 | (unsigned int) w->core.width - j, (unsigned int)w->core.height, | |
420 | FALSE); | |
421 | ||
422 | /* Draw graph reference lines */ | |
423 | left = j; | |
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); | |
428 | } | |
429 | return; | |
430 | } | |
431 | ||
432 | /* ARGSUSED */ | |
433 | static Boolean SetValues (current, request, new) | |
434 | Widget current, request, new; | |
435 | { | |
436 | StripChartWidget old = (StripChartWidget)current; | |
437 | StripChartWidget w = (StripChartWidget)new; | |
438 | Boolean ret_val = FALSE; | |
439 | unsigned int new_gc = NO_GCS; | |
440 | ||
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); | |
449 | } | |
450 | ||
451 | if ( w->strip_chart.min_scale > (int) ((w->strip_chart.max_value) + 1) ) | |
452 | ret_val = TRUE; | |
453 | ||
454 | if ( w->strip_chart.fgpixel != old->strip_chart.fgpixel ) { | |
455 | new_gc |= FOREGROUND; | |
456 | ret_val = True; | |
457 | } | |
458 | ||
459 | if ( w->strip_chart.hipixel != old->strip_chart.hipixel ) { | |
460 | new_gc |= HIGHLIGHT; | |
461 | ret_val = True; | |
462 | } | |
463 | ||
464 | DestroyGC(old, new_gc); | |
465 | CreateGC(w, new_gc); | |
466 | ||
467 | return( ret_val ); | |
468 | } | |
469 | ||
470 | /* Function Name: SetPoints | |
471 | * Description: Sets up the polypoint that will be used to draw in | |
472 | * the graph lines. | |
473 | * Arguments: w - the StripChart widget. | |
474 | * Returns: none. | |
475 | */ | |
476 | ||
477 | #define HEIGHT ( (unsigned int) w->core.height) | |
478 | ||
479 | static void | |
480 | SetPoints(w) | |
481 | StripChartWidget w; | |
482 | { | |
483 | XPoint * points; | |
484 | Cardinal size; | |
485 | int i; | |
486 | ||
487 | if (w->strip_chart.scale <= 1) { /* no scale lines. */ | |
488 | XtFree ((char *) w->strip_chart.points); | |
489 | w->strip_chart.points = NULL; | |
490 | return; | |
491 | } | |
492 | ||
493 | size = sizeof(XPoint) * (w->strip_chart.scale - 1); | |
494 | ||
495 | points = (XPoint *) XtRealloc( (XtPointer) w->strip_chart.points, size); | |
496 | w->strip_chart.points = points; | |
497 | ||
498 | /* Draw graph reference lines into clip mask */ | |
499 | ||
500 | for (i = 1; i < w->strip_chart.scale; i++) { | |
501 | points[i - 1].x = 0; | |
502 | points[i - 1].y = HEIGHT / w->strip_chart.scale; | |
503 | } | |
504 | } |