Build Fix -- compatibility issue with newer autoconf
[sxemacs] / src / ui / lwlib / xlwgauge.c
1 /* Gauge Widget for SXEmacs.
2    Copyright (C) 1999 Edward A. Falk
3
4 This file is part of SXEmacs.
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19 /* Synched up with: Gauge.c 1.2 */
20
21 /*
22  * Gauge.c - Gauge widget
23  *
24  * Author: Edward A. Falk
25  *         falk@falconer.vip.best.com
26  *
27  * Date:   July 9, 1997
28  *
29  * Note: for fun and demonstration purposes, I have added selection
30  * capabilities to this widget.  If you select the widget, you create
31  * a primary selection containing the current value of the widget in
32  * both integer and string form.  If you copy into the widget, the
33  * primary selection is converted to an integer value and the gauge is
34  * set to that value.
35  */
36
37 /* TODO:  display time instead of value
38  */
39
40 #define DEF_LEN 50              /* default width (or height for vertical gauge) */
41 #define MIN_LEN 10              /* minimum reasonable width (height) */
42 #define TIC_LEN 6               /* length of tic marks */
43 #define GA_WID  3               /* width of gauge */
44 #define MS_PER_SEC 1000
45
46 #include <config.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <ctype.h>
50 #include "lwlib-internal.h"
51 #include <X11/IntrinsicP.h>
52 #include <X11/Xatom.h>
53 #include <X11/StringDefs.h>
54 #include ATHENA_XawInit_h_
55 #include "xlwgaugeP.h"
56 #include "ui/X11/xmu.h"
57 #ifdef HAVE_XMU
58 #include <X11/Xmu/Atoms.h>
59 #include <X11/Xmu/Drawing.h>
60 #include <X11/Xmu/StdSel.h>
61 #endif
62
63 /****************************************************************
64  *
65  * Gauge resources
66  *
67  ****************************************************************/
68
69 static char defaultTranslations[] = "<Btn1Up>:  select()\n\
70          <Key>F1:       select(CLIPBOARD)\n\
71          <Btn2Up>:      paste()\n\
72          <Key>F2:       paste(CLIPBOARD)";
73
74 #define offset(field) XtOffsetOf(GaugeRec, field)
75 static XtResource resources[] = {
76         {XtNvalue, XtCValue, XtRInt, sizeof(int),
77          offset(gauge.value), XtRImmediate, (XtPointer) 0},
78         {XtNminValue, XtCMinValue, XtRInt, sizeof(int),
79          offset(gauge.v0), XtRImmediate, (XtPointer) 0},
80         {XtNmaxValue, XtCMaxValue, XtRInt, sizeof(int),
81          offset(gauge.v1), XtRImmediate, (XtPointer) 100},
82         {XtNntics, XtCNTics, XtRInt, sizeof(int),
83          offset(gauge.ntics), XtRImmediate, (XtPointer) 0},
84         {XtNnlabels, XtCNLabels, XtRInt, sizeof(int),
85          offset(gauge.nlabels), XtRImmediate, (XtPointer) 0},
86         {XtNlabels, XtCLabels, XtRStringArray, sizeof(String *),
87          offset(gauge.labels), XtRStringArray, NULL}
88         ,
89         {XtNautoScaleUp, XtCAutoScaleUp, XtRBoolean, sizeof(Boolean),
90          offset(gauge.autoScaleUp), XtRImmediate, FALSE}
91         ,
92         {XtNautoScaleDown, XtCAutoScaleDown, XtRBoolean, sizeof(Boolean),
93          offset(gauge.autoScaleDown), XtRImmediate, FALSE}
94         ,
95         {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
96          offset(gauge.orientation), XtRImmediate, (XtPointer) XtorientHorizontal}
97         ,
98         {XtNupdate, XtCInterval, XtRInt, sizeof(int),
99          offset(gauge.update), XtRImmediate, (XtPointer) 0},
100         {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer),
101          offset(gauge.getValue), XtRImmediate, (XtPointer) NULL}
102         ,
103 };
104 #undef offset
105
106         /* member functions */
107
108 static void GaugeClassInit(void);
109 static void GaugeInit(Widget, Widget, ArgList, Cardinal *);
110 static void GaugeDestroy(Widget);
111 static void GaugeResize(Widget);
112 static void GaugeExpose(Widget, XEvent *, Region);
113 static Boolean GaugeSetValues(Widget, Widget, Widget, ArgList, Cardinal *);
114 static XtGeometryResult GaugeQueryGeometry(Widget, XtWidgetGeometry *,
115                                            XtWidgetGeometry *);
116
117         /* action procs */
118
119 static void GaugeSelect(Widget, XEvent *, String *, Cardinal *);
120 static void GaugePaste(Widget, XEvent *, String *, Cardinal *);
121
122         /* internal privates */
123
124 static void GaugeSize(GaugeWidget, Dimension *, Dimension *, Dimension);
125 static void MaxLabel(GaugeWidget, Dimension *, Dimension *,
126                      Dimension *, Dimension *);
127 static void AutoScale(GaugeWidget);
128 static void EnableUpdate(GaugeWidget);
129 static void DisableUpdate(GaugeWidget);
130
131 static void GaugeGetValue(XtPointer, XtIntervalId *);
132 static void GaugeMercury(Display *, Window, GC, GaugeWidget, Cardinal,
133                          Cardinal);
134
135 static Boolean GaugeConvert(Widget, Atom *, Atom *, Atom *,
136                             XtPointer *, unsigned long *, int *);
137 static void GaugeLoseSel(Widget, Atom *);
138 static void GaugeDoneSel(Widget, Atom *, Atom *);
139 static void GaugeGetSelCB(Widget, XtPointer, Atom *, Atom *,
140                           XtPointer, unsigned long *, int *);
141
142 static GC Get_GC(GaugeWidget, Pixel);
143
144 static XtActionsRec actionsList[] = {
145         {"select", GaugeSelect},
146         {"paste", GaugePaste},
147 };
148
149 /****************************************************************
150  *
151  * Full class record constant
152  *
153  ****************************************************************/
154
155 GaugeClassRec gaugeClassRec = {
156         {
157 /* core_class fields */
158          /* superclass               */ (WidgetClass) & labelClassRec,
159          /* class_name               */ "Gauge",
160          /* widget_size              */ sizeof(GaugeRec),
161          /* class_initialize         */ GaugeClassInit,
162          /* class_part_initialize    */ NULL,
163          /* class_inited             */ FALSE,
164          /* initialize               */ GaugeInit,
165          /* initialize_hook          */ NULL,
166                                                                 /* realize                  */ XtInheritRealize,
167                                                                 /* TODO? */
168          /* actions                  */ actionsList,
169          /* num_actions              */ XtNumber(actionsList),
170          /* resources                */ resources,
171          /* num_resources            */ XtNumber(resources),
172          /* xrm_class                */ NULLQUARK,
173          /* compress_motion          */ TRUE,
174          /* compress_exposure        */ TRUE,
175          /* compress_enterleave      */ TRUE,
176          /* visible_interest         */ FALSE,
177          /* destroy                  */ GaugeDestroy,
178          /* resize                   */ GaugeResize,
179          /* expose                   */ GaugeExpose,
180          /* set_values               */ GaugeSetValues,
181          /* set_values_hook          */ NULL,
182          /* set_values_almost        */ XtInheritSetValuesAlmost,
183          /* get_values_hook          */ NULL,
184          /* accept_focus             */ NULL,
185          /* version                  */ XtVersion,
186          /* callback_private         */ NULL,
187          /* tm_table                 */ defaultTranslations,
188          /* query_geometry           */ GaugeQueryGeometry,
189          /* display_accelerator      */ XtInheritDisplayAccelerator,
190          /* extension                */ NULL
191          }
192         ,
193 /* Simple class fields initialization */
194         {
195          /* change_sensitive         */ XtInheritChangeSensitive
196          }
197         ,
198 #ifdef  _ThreeDP_h
199 /* ThreeD class fields initialization */
200         {
201          XtInheritXaw3dShadowDraw       /* shadowdraw           */
202          }
203         ,
204 #endif
205 /* Label class fields initialization */
206         {
207          /* ignore                   */ 0
208          }
209         ,
210 /* Gauge class fields initialization */
211         {
212          /* extension                */ NULL
213          }
214         ,
215 };
216
217 WidgetClass gaugeWidgetClass = (WidgetClass) & gaugeClassRec;
218 \f
219 /****************************************************************
220  *
221  * Member Procedures
222  *
223  ****************************************************************/
224
225 static void GaugeClassInit(void)
226 {
227         XawInitializeWidgetSet();
228 #ifdef HAVE_XMU
229         XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
230                        NULL, 0);
231 #endif
232 }
233
234 /* ARGSUSED */
235 static void
236 GaugeInit(Widget request, Widget new, ArgList args, Cardinal * num_args)
237 {
238         GaugeWidget gw = (GaugeWidget) new;
239
240         if (gw->gauge.v0 == 0 && gw->gauge.v1 == 0) {
241                 gw->gauge.autoScaleUp = gw->gauge.autoScaleDown = TRUE;
242                 AutoScale(gw);
243         }
244
245         /* If size not explicitly set, set it to our preferred size now.  */
246
247         if (request->core.width == 0 || request->core.height == 0) {
248                 Dimension w, h;
249                 GaugeSize(gw, &w, &h, DEF_LEN);
250                 if (request->core.width == 0)
251                         new->core.width = w;
252                 if (request->core.height == 0)
253                         new->core.height = h;
254                 gw->core.widget_class->core_class.resize(new);
255         }
256
257         gw->gauge.selected = None;
258         gw->gauge.selstr = NULL;
259
260         if (gw->gauge.update > 0)
261                 EnableUpdate(gw);
262
263         gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel);
264 }
265
266 static void GaugeDestroy(Widget w)
267 {
268         GaugeWidget gw = (GaugeWidget) w;
269
270         if (gw->gauge.selstr != NULL)
271                 XtFree(gw->gauge.selstr);
272
273         if (gw->gauge.selected != None)
274                 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
275
276         XtReleaseGC(w, gw->gauge.inverse_GC);
277
278         if (gw->gauge.update > 0)
279                 DisableUpdate(gw);
280 }
281
282 /* React to size change from manager.  Label widget will compute some
283  * internal stuff, but we need to override.
284  */
285
286 static void GaugeResize(Widget w)
287 {
288         GaugeWidget gw = (GaugeWidget) w;
289         int size;               /* height (width) of gauge */
290         int vmargin;            /* vertical (horizontal) margin */
291         int hmargin;            /* horizontal (vertical) margin */
292
293         vmargin = gw->gauge.orientation == XtorientHorizontal ?
294             gw->label.internal_height : gw->label.internal_width;
295         hmargin = gw->gauge.orientation == XtorientHorizontal ?
296             gw->label.internal_width : gw->label.internal_height;
297
298         /* TODO: need to call parent resize proc?  I don't think so since
299          * we're recomputing everything from scratch anyway.
300          */
301
302         /* find total height (width) of contents */
303
304         size = GA_WID + 2;      /* gauge itself + edges */
305
306         if (gw->gauge.ntics > 1)        /* tic marks */
307                 size += vmargin + TIC_LEN;
308
309         if (gw->gauge.nlabels > 1) {
310                 Dimension lwm, lw0, lw1;        /* width of max, left, right labels */
311                 Dimension lh;
312
313                 MaxLabel(gw, &lwm, &lh, &lw0, &lw1);
314
315                 if (gw->gauge.orientation == XtorientHorizontal) {
316                         gw->gauge.margin0 = lw0 / 2;
317                         gw->gauge.margin1 = lw1 / 2;
318                         size += lh + vmargin;
319                 } else {
320                         gw->gauge.margin0 = gw->gauge.margin1 = lh / 2;
321                         size += lwm + vmargin;
322                 }
323         } else
324                 gw->gauge.margin0 = gw->gauge.margin1 = 0;
325
326         gw->gauge.margin0 += hmargin;
327         gw->gauge.margin1 += hmargin;
328
329         /* Now distribute height (width) over components */
330
331         if (gw->gauge.orientation == XtorientHorizontal)
332                 gw->gauge.gmargin = (gw->core.height - size) / 2;
333         else
334                 gw->gauge.gmargin = (gw->core.width - size) / 2;
335
336         gw->gauge.tmargin = gw->gauge.gmargin + GA_WID + 2 + vmargin;
337         if (gw->gauge.ntics > 1)
338                 gw->gauge.lmargin = gw->gauge.tmargin + TIC_LEN + vmargin;
339         else
340                 gw->gauge.lmargin = gw->gauge.tmargin;
341 }
342
343 /*
344  * Repaint the widget window
345  */
346
347 /* ARGSUSED */
348 static void GaugeExpose(Widget w, XEvent * event, Region region)
349 {
350         GaugeWidget gw = (GaugeWidget) w;
351         register Display *dpy = XtDisplay(w);
352         register Window win = XtWindow(w);
353         GC gc;                  /* foreground, background */
354         GC gctop, gcbot;        /* dark, light shadows */
355
356         int len;                /* length (width or height) of widget */
357         int hgt;                /* height (width) of widget */
358         int e0, e1;             /* ends of the gauge */
359         int x;
360         int y;                  /* vertical (horizontal) position */
361         int i;
362         int v0 = gw->gauge.v0;
363         int v1 = gw->gauge.v1;
364         int value = gw->gauge.value;
365
366         gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC;
367
368 #ifdef  _ThreeDP_h
369         gctop = gw->threeD.bot_shadow_GC;
370         gcbot = gw->threeD.top_shadow_GC;
371 #else
372         gctop = gcbot = gc;
373 #endif
374
375         if (gw->gauge.orientation == XtorientHorizontal) {
376                 len = gw->core.width;
377                 hgt = gw->core.height;
378         } else {
379                 len = gw->core.height;
380                 hgt = gw->core.width;
381         }
382
383         SXE_SET_UNUSED(hgt);
384
385         /* if the gauge is selected, signify by drawing the background
386          * in a contrasting color.
387          */
388
389         if (gw->gauge.selected) {
390                 XFillRectangle(dpy, win, gc, 0, 0, w->core.width,
391                                w->core.height);
392                 gc = gw->gauge.inverse_GC;
393         }
394
395         e0 = gw->gauge.margin0; /* left (top) end */
396         e1 = len - gw->gauge.margin1 - 1;       /* right (bottom) end */
397
398         /* Draw the Gauge itself */
399
400         y = gw->gauge.gmargin;
401
402         if (gw->gauge.orientation == XtorientHorizontal) {      /* horizontal */
403                 XDrawLine(dpy, win, gctop, e0 + 1, y, e1 - 1, y);
404                 XDrawLine(dpy, win, gctop, e0, y + 1, e0, y + GA_WID);
405                 XDrawLine(dpy, win, gcbot, e0 + 1, y + GA_WID + 1, e1 - 1,
406                           y + GA_WID + 1);
407                 XDrawLine(dpy, win, gcbot, e1, y + 1, e1, y + GA_WID);
408         } else {                /* vertical */
409
410                 XDrawLine(dpy, win, gctop, y, e0 + 1, y, e1 - 1);
411                 XDrawLine(dpy, win, gctop, y + 1, e0, y + GA_WID, e0);
412                 XDrawLine(dpy, win, gcbot, y + GA_WID + 1, e0 + 1,
413                           y + GA_WID + 1, e1 - 1);
414                 XDrawLine(dpy, win, gcbot, y + 1, e1, y + GA_WID, e1);
415         }
416
417         /* draw the mercury */
418
419         GaugeMercury(dpy, win, gc, gw, 0, value);
420
421         if (gw->gauge.ntics > 1) {
422                 y = gw->gauge.tmargin;
423                 for (i = 0; i < gw->gauge.ntics; ++i) {
424                         x = e0 + i * (e1 - e0 - 1) / (gw->gauge.ntics - 1);
425                         if (gw->gauge.orientation == XtorientHorizontal) {
426                                 XDrawLine(dpy, win, gcbot, x, y + 1, x,
427                                           y + TIC_LEN - 2);
428                                 XDrawLine(dpy, win, gcbot, x, y, x + 1, y);
429                                 XDrawLine(dpy, win, gctop, x + 1, y + 1, x + 1,
430                                           y + TIC_LEN - 2);
431                                 XDrawLine(dpy, win, gctop, x, y + TIC_LEN - 1,
432                                           x + 1, y + TIC_LEN - 1);
433                         } else {
434                                 XDrawLine(dpy, win, gcbot, y + 1, x,
435                                           y + TIC_LEN - 2, x);
436                                 XDrawLine(dpy, win, gcbot, y, x, y, x + 1);
437                                 XDrawLine(dpy, win, gctop, y + 1, x + 1,
438                                           y + TIC_LEN - 2, x + 1);
439                                 XDrawLine(dpy, win, gctop, y + TIC_LEN - 1, x,
440                                           y + TIC_LEN - 1, x + 1);
441                         }
442                 }
443         }
444
445         /* draw labels */
446         if (gw->gauge.nlabels > 1) {
447                 char label[sizeof(long)*3+1], *s = label;
448                 int xlen, wd, h = 0, n;
449
450                 if (gw->gauge.orientation == XtorientHorizontal)
451                         y = gw->gauge.lmargin +
452                             gw->label.font->max_bounds.ascent - 1;
453                 else {
454                         y = gw->gauge.lmargin;
455                         h = gw->label.font->max_bounds.ascent / 2;
456                 }
457
458                 for (i = 0; i < gw->gauge.nlabels; ++i) {
459                         if (gw->gauge.labels == NULL) {
460                                 n = snprintf(label, sizeof(label),
461                                              "%d",
462                                              v0 + i * (v1 - v0) /
463                                                   (gw->gauge.nlabels - 1));
464                                 assert(n >= 0 &&  (size_t)n < sizeof(label));
465                         }
466                         else
467                                 s = gw->gauge.labels[i];
468                         if (s != NULL) {
469                                 x = e0 + i * (e1 - e0 - 1) /
470                                              (gw->gauge.nlabels - 1);
471                                 xlen = strlen(s);
472                                 if (gw->gauge.orientation == XtorientHorizontal) {
473                                         wd = XTextWidth(gw->label.font, s,
474                                                         xlen);
475                                         XDrawString(dpy, win, gc, x - wd / 2, y,
476                                                     s, xlen);
477                                 } else {
478                                         XDrawString(dpy, win, gc, y, x + h, s,
479                                                     xlen);
480                                 }
481                         }
482                 }
483         }
484 }
485
486 /*
487  * Set specified arguments into widget
488  */
489
490 static Boolean
491 GaugeSetValues(Widget old,
492                Widget request, Widget new, ArgList args, Cardinal * num_args)
493 {
494         GaugeWidget oldgw = (GaugeWidget) old;
495         GaugeWidget gw = (GaugeWidget) new;
496         Boolean was_resized = False;
497
498         if (gw->gauge.selected != None) {
499                 XtDisownSelection(new, gw->gauge.selected, CurrentTime);
500                 gw->gauge.selected = None;
501         }
502
503         /* Changes to v0,v1,labels, ntics, nlabels require resize & redraw. */
504         /* Change to value requires redraw and possible resize if autoscale */
505
506         was_resized =
507             gw->gauge.v0 != oldgw->gauge.v0 ||
508             gw->gauge.v1 != oldgw->gauge.v1 ||
509             gw->gauge.ntics != oldgw->gauge.ntics ||
510             gw->gauge.nlabels != oldgw->gauge.nlabels ||
511             gw->gauge.labels != oldgw->gauge.labels;
512
513         if ((gw->gauge.autoScaleUp && gw->gauge.value > gw->gauge.v1) ||
514             (gw->gauge.autoScaleDown && gw->gauge.value < gw->gauge.v1 / 3)) {
515                 AutoScale(gw);
516                 was_resized = TRUE;
517         }
518
519         if (was_resized) {
520                 if (gw->label.resize)
521                         GaugeSize(gw, &gw->core.width, &gw->core.height,
522                                   DEF_LEN);
523                 else
524                         GaugeResize(new);
525         }
526
527         if (gw->gauge.update != oldgw->gauge.update) {
528                 if (gw->gauge.update > 0)
529                         EnableUpdate(gw);
530                 else
531                         DisableUpdate(gw);
532         }
533
534         if (gw->core.background_pixel != oldgw->core.background_pixel) {
535                 XtReleaseGC(new, gw->gauge.inverse_GC);
536                 gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel);
537         }
538
539         return was_resized || gw->gauge.value != oldgw->gauge.value ||
540             XtIsSensitive(old) != XtIsSensitive(new);
541 }
542
543 static XtGeometryResult
544 GaugeQueryGeometry(Widget w,
545                    XtWidgetGeometry * intended, XtWidgetGeometry * preferred)
546 {
547         register GaugeWidget gw = (GaugeWidget) w;
548
549         if (intended->width == w->core.width &&
550             intended->height == w->core.height)
551                 return XtGeometryNo;
552
553         preferred->request_mode = CWWidth | CWHeight;
554         GaugeSize(gw, &preferred->width, &preferred->height, DEF_LEN);
555
556         if ((!(intended->request_mode & CWWidth) ||
557              intended->width >= preferred->width) &&
558             (!(intended->request_mode & CWHeight) ||
559              intended->height >= preferred->height))
560                 return XtGeometryYes;
561         else
562                 return XtGeometryAlmost;
563 }
564 \f
565 /****************************************************************
566  *
567  * Action Procedures
568  *
569  ****************************************************************/
570
571 static void
572 GaugeSelect(Widget w, XEvent * event, String * params, Cardinal * num_params)
573 {
574         GaugeWidget gw = (GaugeWidget) w;
575         Atom seln = XA_PRIMARY;
576
577         if (gw->gauge.selected != None) {
578                 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
579                 gw->gauge.selected = None;
580         }
581
582         if (*num_params > 0) {
583                 seln = XInternAtom(XtDisplay(w), params[0], False);
584                 printf("atom %s is %ld\n", params[0], seln);
585         }
586
587         if (!XtOwnSelection(w, seln, event->xbutton.time, GaugeConvert,
588                             GaugeLoseSel, GaugeDoneSel)) {
589                 /* in real code, this error message would be replaced by
590                  * something more elegant, or at least deleted
591                  */
592
593                 fprintf(stderr, "Gauge failed to get selection, try again\n");
594         } else {
595                 int n;
596                 const int max_selstr = 4 * sizeof(int);
597                 gw->gauge.selected = TRUE;
598                 gw->gauge.selstr = (String) XtMalloc(max_selstr);
599                 n = snprintf(gw->gauge.selstr, max_selstr, "%d", gw->gauge.value);
600                 assert(n >= 0 && n < max_selstr);
601                 GaugeExpose(w, 0, 0);
602         }
603 }
604
605 static Boolean GaugeConvert(Widget w, Atom * selection, /* usually XA_PRIMARY */
606                             Atom * target,      /* requested target */
607                             Atom * type,        /* returned type */
608                             XtPointer * value,  /* returned value */
609                             unsigned long *length,      /* returned length */
610                             int *format)
611 {                               /* returned format */
612         GaugeWidget gw = (GaugeWidget) w;
613         XSelectionRequestEvent *req;
614
615         printf("requesting selection %s:%s\n",
616                XGetAtomName(XtDisplay(w), *selection),
617                XGetAtomName(XtDisplay(w), *target));
618
619 #ifdef HAVE_XMU
620         if (*target == XA_TARGETS(XtDisplay(w))) {
621                 Atom *rval;
622                 XPointer stdTargets;
623                 unsigned long stdLength;
624
625                 /* XmuConvertStandardSelection can handle this.  This function
626                  * will return a list of standard targets.  We prepend TEXT,
627                  * STRING and INTEGER to the list and return it.
628                  */
629
630                 req = XtGetSelectionRequest(w, *selection, NULL);
631                 XmuConvertStandardSelection(w, req->time, selection, target,
632                                             type, &stdTargets,
633                                             &stdLength, format);
634
635                 *type = XA_ATOM;        /* TODO: needed? */
636                 *length = stdLength + 3;
637                 rval = (Atom *) XtMalloc(sizeof(Atom) * (stdLength + 3));
638                 *value = (XtPointer) rval;
639                 *rval++ = XA_INTEGER;
640                 *rval++ = XA_STRING;
641                 *rval++ = XA_TEXT(XtDisplay(w));
642                 memcpy((char *)rval, (char *)stdTargets,
643                        stdLength * sizeof(Atom));
644                 XtFree((char *)stdTargets);
645                 *format = 8 * sizeof(Atom);     /* TODO: needed? */
646                 return True;
647         }
648
649         else
650 #endif
651         if (*target == XA_INTEGER) {
652                 *type = XA_INTEGER;
653                 *length = 1;
654                 *value = (XtPointer) & gw->gauge.value;
655                 *format = 8 * sizeof(int);
656                 return True;
657         }
658
659         else if (*target == XA_STRING
660 #ifdef HAVE_XMU
661                  || *target == XA_TEXT(XtDisplay(w))
662 #endif
663             ) {
664                 *type = *target;
665                 *length = strlen(gw->gauge.selstr) * sizeof(char);
666                 *value = (XtPointer) gw->gauge.selstr;
667                 *format = 8;
668                 return True;
669         }
670
671         else {
672                 /* anything else, we just give it to XmuConvertStandardSelection() */
673 #ifdef HAVE_XMU
674                 req = XtGetSelectionRequest(w, *selection, NULL);
675                 if (XmuConvertStandardSelection(w, req->time, selection, target,
676                                                 type, (XPointer *) value,
677                                                 length, format))
678                         return True;
679                 else
680 #endif
681                 {
682                         printf
683                             ("Gauge: requestor is requesting unsupported selection %s:%s\n",
684                              XGetAtomName(XtDisplay(w), *selection),
685                              XGetAtomName(XtDisplay(w), *target));
686                         return False;
687                 }
688         }
689 }
690
691 static void GaugeLoseSel(Widget w, Atom * selection)
692 {                               /* usually XA_PRIMARY */
693         GaugeWidget gw = (GaugeWidget) w;
694         Display *dpy = XtDisplay(w);
695         Window win = XtWindow(w);
696
697         if (gw->gauge.selstr != NULL) {
698                 XtFree(gw->gauge.selstr);
699                 gw->gauge.selstr = NULL;
700         }
701
702         gw->gauge.selected = False;
703         XClearWindow(dpy, win);
704         GaugeExpose(w, 0, 0);
705 }
706
707 static void GaugeDoneSel(Widget w, Atom * selection,    /* usually XA_PRIMARY */
708                          Atom * target)
709 {                               /* requested target */
710         /* selection done, anything to do? */
711 }
712
713 static void
714 GaugePaste(Widget w, XEvent * event, String * params, Cardinal * num_params)
715 {
716         Atom seln = XA_PRIMARY;
717
718         if (*num_params > 0) {
719                 seln = XInternAtom(XtDisplay(w), params[0], False);
720                 printf("atom %s is %ld\n", params[0], seln);
721         }
722
723         /* try for integer value first */
724         XtGetSelectionValue(w, seln, XA_INTEGER,
725                             GaugeGetSelCB, (XtPointer) XA_INTEGER,
726                             event->xbutton.time);
727 }
728
729 static void
730 GaugeGetSelCB(Widget w,
731               XtPointer client,
732               Atom * selection,
733               Atom * type, XtPointer value, unsigned long *length, int *format)
734 {
735         Display *dpy = XtDisplay(w);
736         Atom target = (Atom) client;
737         int *iptr;
738         char *cptr;
739
740         if (*type == XA_INTEGER) {
741                 iptr = (int *)value;
742                 XawGaugeSetValue(w, *iptr);
743         }
744
745         else if (*type == XA_STRING
746 #ifdef HAVE_XMU
747                  || *type == XA_TEXT(dpy)
748 #endif
749             ) {
750                 cptr = (char *)value;
751                 XawGaugeSetValue(w, atoi(cptr));
752         }
753
754         /* failed, try string */
755         else if (*type == None && target == XA_INTEGER)
756                 XtGetSelectionValue(w, *selection, XA_STRING,
757                                     GaugeGetSelCB, (XtPointer) XA_STRING,
758                                     CurrentTime);
759 }
760 \f
761 /****************************************************************
762  *
763  * Public Procedures
764  *
765  ****************************************************************/
766
767         /* Change gauge value.  Only undraw or draw what needs to be
768          * changed.
769          */
770
771 void XawGaugeSetValue(Widget w, Cardinal value)
772 {
773         GaugeWidget gw = (GaugeWidget) w;
774         int oldvalue;
775         GC gc;
776
777         if (gw->gauge.selected != None) {
778                 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
779                 gw->gauge.selected = None;
780         }
781
782         if (!XtIsRealized(w)) {
783                 gw->gauge.value = value;
784                 return;
785         }
786
787         /* need to rescale? */
788         if ((gw->gauge.autoScaleUp && value > (Cardinal)gw->gauge.v1) ||
789             (gw->gauge.autoScaleDown && value < (Cardinal)gw->gauge.v1 / 3)) {
790                 XtVaSetValues(w, XtNvalue, value, NULL);
791                 return;
792         }
793
794         oldvalue = gw->gauge.value;
795         gw->gauge.value = value;
796
797         gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC;
798         GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue, value);
799 }
800
801 Cardinal XawGaugeGetValue(Widget w)
802 {
803         GaugeWidget gw = (GaugeWidget) w;
804         return gw->gauge.value;
805 }
806 \f
807 /****************************************************************
808  *
809  * Private Procedures
810  *
811  ****************************************************************/
812
813         /* draw the mercury over a specific region */
814
815 static void
816 GaugeMercury(Display * dpy,
817              Window win, GC gc, GaugeWidget gw, Cardinal val0, Cardinal val1)
818 {
819         int v0 = gw->gauge.v0;
820         int v1 = gw->gauge.v1;
821         int vd = v1 - v0;
822         Dimension len;          /* length (width or height) of gauge */
823         Position e0, e1;        /* gauge ends */
824         Position p0, p1;        /* mercury ends */
825         int y;                  /* vertical (horizontal) position */
826         Boolean undraw = FALSE;
827
828         len = gw->gauge.orientation == XtorientHorizontal ?
829             gw->core.width : gw->core.height;
830
831         e0 = gw->gauge.margin0; /* left (top) end */
832         e1 = len - gw->gauge.margin1 - 1;       /* right (bottom) end */
833
834         if (vd <= 0)
835                 vd = 1;
836
837         if (val0 < (Cardinal)v0)
838                 val0 = v0;
839         else if (val0 > (Cardinal)v1)
840                 val0 = v1;
841         if (val1 < (Cardinal)v0)
842                 val1 = v0;
843         else if (val1 > (Cardinal)v1)
844                 val1 = v1;
845
846         p0 = (val0 - v0) * (e1 - e0 - 1) / vd;
847         p1 = (val1 - v0) * (e1 - e0 - 1) / vd;
848
849         if (p1 == p0)
850                 return;
851
852         y = gw->gauge.gmargin;
853
854         if (p1 < p0) {
855                 Position tmp = p0;
856                 p0 = p1;
857                 p1 = tmp;
858                 gc = gw->label.normal_GC;
859                 XSetForeground(dpy, gc, gw->core.background_pixel);
860                 undraw = TRUE;
861         }
862
863         if (gw->gauge.orientation == XtorientHorizontal)
864                 XFillRectangle(dpy, win, gc, e0 + p0 + 1, y + 1, p1 - p0,
865                                GA_WID);
866         else
867                 XFillRectangle(dpy, win, gc, y + 1, e1 - p1, GA_WID, p1 - p0);
868
869         if (undraw)
870                 XSetForeground(dpy, gc, gw->label.foreground);
871 }
872
873 /* Search the labels, find the largest one. */
874 /* TODO: handle vertical fonts? */
875
876 static void MaxLabel(GaugeWidget gw, Dimension * wid,   /* max label width */
877                      Dimension * hgt,   /* max label height */
878                      Dimension * w0,    /* width of first label */
879                      Dimension * w1)
880 {                               /* width of last label */
881         char lstr[80], *lbl;
882         int w;
883         XFontStruct *font = gw->label.font;
884         int i;
885         int lw = 0;
886         int v0 = gw->gauge.v0;
887         int dv = gw->gauge.v1 - v0;
888         int n = gw->gauge.nlabels;
889
890         if (n > 0) {
891                 if (--n <= 0) {
892                         n = 1;
893                         v0 += dv / 2;
894                 }
895
896                 /* loop through all labels, figure out how much room they
897                  * need.
898                  */
899                 w = 0;
900                 for (i = 0; i < gw->gauge.nlabels; ++i) {
901                         if (gw->gauge.labels == NULL)   { /* numeric labels */
902                                 int sz = snprintf(lbl = lstr, sizeof(lstr),
903                                                   "%d", v0 + i * dv / n);
904                                 assert(sz >= 0 && (size_t)sz < sizeof(lstr));
905                         } else
906                                 lbl = gw->gauge.labels[i];
907
908                         if (lbl != NULL) {
909                                 lw = XTextWidth(font, lbl, strlen(lbl));
910                                 w = Max(w, lw);
911                         } else
912                                 lw = 0;
913
914                         if (i == 0 && w0 != NULL)
915                                 *w0 = lw;
916                 }
917                 if (w1 != NULL)
918                         *w1 = lw;
919
920                 *wid = w;
921                 *hgt = font->max_bounds.ascent + font->max_bounds.descent;
922         } else
923                 *wid = *hgt = 0;
924 }
925
926 /* Determine the preferred size for this widget.  choose 100x100 for
927  * debugging.
928  */
929
930 static void
931 GaugeSize(GaugeWidget gw, Dimension * wid, Dimension * hgt, Dimension min_len)
932 {
933         int w, h;               /* width, height of gauge */
934         int vmargin;            /* vertical margin */
935         int hmargin;            /* horizontal margin */
936
937         hmargin = gw->label.internal_width;
938         vmargin = gw->label.internal_height;
939
940         /* find total height (width) of contents */
941
942         /* find minimum size for undecorated gauge */
943
944         if (gw->gauge.orientation == XtorientHorizontal) {
945                 w = min_len;
946                 h = GA_WID + 2; /* gauge itself + edges */
947         } else {
948                 w = GA_WID + 2;
949                 h = min_len;
950         }
951
952         if (gw->gauge.ntics > 0) {
953                 if (gw->gauge.orientation == XtorientHorizontal) {
954                         w = Max(w, gw->gauge.ntics * 3);
955                         h += vmargin + TIC_LEN;
956                 } else {
957                         w += hmargin + TIC_LEN;
958                         h = Max(h, gw->gauge.ntics * 3);
959                 }
960         }
961
962         /* If labels are requested, this gets a little interesting.
963          * We want the end labels centered on the ends of the gauge and
964          * the centers of the labels evenly spaced.  The labels at the ends
965          * will not be the same width, meaning that the gauge itself need
966          * not be centered in the widget.
967          *
968          * First, determine the spacing.  This is the width of the widest
969          * label, plus the internal margin.  Total length of the gauge is
970          * spacing * (nlabels-1).  To this, we add half the width of the
971          * left-most label and half the width of the right-most label
972          * to get the entire desired width of the widget.
973          */
974         if (gw->gauge.nlabels > 0) {
975                 Dimension lwm, lw0, lw1;        /* width of max, left, right labels */
976                 Dimension lh;
977
978                 MaxLabel(gw, &lwm, &lh, &lw0, &lw1);
979
980                 if (gw->gauge.orientation == XtorientHorizontal) {
981                         lwm =
982                             (lwm + hmargin) * (gw->gauge.nlabels - 1) + (lw0 +
983                                                                          lw1) /
984                             2;
985                         w = Max(w, lwm);
986                         h += lh + vmargin;
987                 } else {
988                         lh = lh * gw->gauge.nlabels + (gw->gauge.nlabels -
989                                                        1) * vmargin;
990                         h = Max(h, lh);
991                         w += lwm + hmargin;
992                 }
993         }
994
995         w += hmargin * 2;
996         h += vmargin * 2;
997
998         *wid = w;
999         *hgt = h;
1000 }
1001
1002 static void AutoScale(GaugeWidget gw)
1003 {
1004         static int scales[3] = { 1, 2, 5 };
1005         int sptr = 0, smult = 1;
1006
1007         if (gw->gauge.autoScaleDown)
1008                 gw->gauge.v1 = 0;
1009         while (gw->gauge.value > gw->gauge.v1) {
1010                 if (++sptr > 2) {
1011                         sptr = 0;
1012                         smult *= 10;
1013                 }
1014                 gw->gauge.v1 = scales[sptr] * smult;
1015         }
1016 }
1017
1018 static void EnableUpdate(GaugeWidget gw)
1019 {
1020         gw->gauge.intervalId =
1021             XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) gw),
1022                             gw->gauge.update * MS_PER_SEC, GaugeGetValue,
1023                             (XtPointer) gw);
1024 }
1025
1026 static void DisableUpdate(GaugeWidget gw)
1027 {
1028         XtRemoveTimeOut(gw->gauge.intervalId);
1029 }
1030
1031 static void GaugeGetValue(XtPointer clientData, XtIntervalId * intervalId)
1032 {
1033         GaugeWidget gw = (GaugeWidget) clientData;
1034         Cardinal value;
1035
1036         if (gw->gauge.update > 0)
1037                 EnableUpdate(gw);
1038
1039         if (gw->gauge.getValue != NULL) {
1040                 XtCallCallbackList((Widget) gw, gw->gauge.getValue,
1041                                    (XtPointer) & value);
1042                 XawGaugeSetValue((Widget) gw, value);
1043         }
1044 }
1045
1046 static GC Get_GC(GaugeWidget gw, Pixel fg)
1047 {
1048         XGCValues values;
1049 #define vmask   GCForeground
1050 #define umask   (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\
1051                 |GCFont|GCDashList|GCArcMode)
1052
1053         values.foreground = fg;
1054
1055         return XtAllocateGC((Widget) gw, 0, vmask, &values, 0L, umask);
1056 }