Expansion item ${listquote }. Bug 1066
[exim.git] / src / exim_monitor / em_StripChart.c
CommitLineData
059ec3d9
PH
1/***********************************************************
2Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
3and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Digital or MIT not be
12used in advertising or publicity pertaining to distribution of the
13software without specific, written prior permission.
14
15DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21SOFTWARE.
22
23******************************************************************/
24
25/* This is the Athena StripChart widget, slightly hacked by
26Philip Hazel <ph10@cus.cam.ac.uk> in order to give access to
27its repaint_window function so that a repaint can be forced.
28
29The repaint_window function has also been nobbled so that it only
30ever changes scale to 10. There is probably a better way to handle
31this - such as inventing some new resources, but I'm not up to
32that just at the moment.
33
34On SunOS4 there are name clashes when trying to link this with the
35Athena 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
52static 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
75static void CreateGC(StripChartWidget, unsigned int);
76static void DestroyGC(StripChartWidget, unsigned int);
77static void Initialize(), Destroy(), Redisplay();
78static void MoveChart(StripChartWidget, Boolean);
79static void SetPoints(StripChartWidget);
80static Boolean SetValues();
81
82int repaint_window(StripChartWidget, int, int); /* PH hack */
83/* static int repaint_window(); */
84
85StripChartClassRec 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
126WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec;
127
128/****************************************************************
129 *
130 * Private Procedures
131 *
132 ****************************************************************/
133
134static 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
143static void
144CreateGC(w, which)
145StripChartWidget w;
146unsigned 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
168static void
169DestroyGC(w, which)
170StripChartWidget w;
171unsigned 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 */
181static 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
200static 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/*
4c04137d 213 * NOTE: This function really needs to receive graphics exposure
059ec3d9
PH
214 * events, but since this is not easily supported until R4 I am
215 * going to hold off until then.
216 */
217
218/* ARGSUSED */
219static 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 */
233static void
234draw_it(client_data, id)
235XtPointer client_data;
236XtIntervalId *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 */
304int /* PH hack */
305repaint_window(w, left, width)
306StripChartWidget w;
307int 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
372static void
373MoveChart(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 */
433static 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
479static void
480SetPoints(w)
481StripChartWidget 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}