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