Minor package-get cleanup + bldchain tweak
[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         /* if the gauge is selected, signify by drawing the background
384          * in a contrasting color.
385          */
386
387         if (gw->gauge.selected) {
388                 XFillRectangle(dpy, win, gc, 0, 0, w->core.width,
389                                w->core.height);
390                 gc = gw->gauge.inverse_GC;
391         }
392
393         e0 = gw->gauge.margin0; /* left (top) end */
394         e1 = len - gw->gauge.margin1 - 1;       /* right (bottom) end */
395
396         /* Draw the Gauge itself */
397
398         y = gw->gauge.gmargin;
399
400         if (gw->gauge.orientation == XtorientHorizontal) {      /* horizontal */
401                 XDrawLine(dpy, win, gctop, e0 + 1, y, e1 - 1, y);
402                 XDrawLine(dpy, win, gctop, e0, y + 1, e0, y + GA_WID);
403                 XDrawLine(dpy, win, gcbot, e0 + 1, y + GA_WID + 1, e1 - 1,
404                           y + GA_WID + 1);
405                 XDrawLine(dpy, win, gcbot, e1, y + 1, e1, y + GA_WID);
406         } else {                /* vertical */
407
408                 XDrawLine(dpy, win, gctop, y, e0 + 1, y, e1 - 1);
409                 XDrawLine(dpy, win, gctop, y + 1, e0, y + GA_WID, e0);
410                 XDrawLine(dpy, win, gcbot, y + GA_WID + 1, e0 + 1,
411                           y + GA_WID + 1, e1 - 1);
412                 XDrawLine(dpy, win, gcbot, y + 1, e1, y + GA_WID, e1);
413         }
414
415         /* draw the mercury */
416
417         GaugeMercury(dpy, win, gc, gw, 0, value);
418
419         if (gw->gauge.ntics > 1) {
420                 y = gw->gauge.tmargin;
421                 for (i = 0; i < gw->gauge.ntics; ++i) {
422                         x = e0 + i * (e1 - e0 - 1) / (gw->gauge.ntics - 1);
423                         if (gw->gauge.orientation == XtorientHorizontal) {
424                                 XDrawLine(dpy, win, gcbot, x, y + 1, x,
425                                           y + TIC_LEN - 2);
426                                 XDrawLine(dpy, win, gcbot, x, y, x + 1, y);
427                                 XDrawLine(dpy, win, gctop, x + 1, y + 1, x + 1,
428                                           y + TIC_LEN - 2);
429                                 XDrawLine(dpy, win, gctop, x, y + TIC_LEN - 1,
430                                           x + 1, y + TIC_LEN - 1);
431                         } else {
432                                 XDrawLine(dpy, win, gcbot, y + 1, x,
433                                           y + TIC_LEN - 2, x);
434                                 XDrawLine(dpy, win, gcbot, y, x, y, x + 1);
435                                 XDrawLine(dpy, win, gctop, y + 1, x + 1,
436                                           y + TIC_LEN - 2, x + 1);
437                                 XDrawLine(dpy, win, gctop, y + TIC_LEN - 1, x,
438                                           y + TIC_LEN - 1, x + 1);
439                         }
440                 }
441         }
442
443         /* draw labels */
444         if (gw->gauge.nlabels > 1) {
445                 char label[sizeof(long)*3+1], *s = label;
446                 int xlen, wd, h = 0, n;
447
448                 if (gw->gauge.orientation == XtorientHorizontal)
449                         y = gw->gauge.lmargin +
450                             gw->label.font->max_bounds.ascent - 1;
451                 else {
452                         y = gw->gauge.lmargin;
453                         h = gw->label.font->max_bounds.ascent / 2;
454                 }
455
456                 for (i = 0; i < gw->gauge.nlabels; ++i) {
457                         if (gw->gauge.labels == NULL) {
458                                 n = snprintf(label, sizeof(label),
459                                              "%d",
460                                              v0 + i * (v1 - v0) /
461                                                   (gw->gauge.nlabels - 1));
462                                 assert(n >= 0 &&  (size_t)n < sizeof(label));
463                         }
464                         else
465                                 s = gw->gauge.labels[i];
466                         if (s != NULL) {
467                                 x = e0 + i * (e1 - e0 - 1) /
468                                              (gw->gauge.nlabels - 1);
469                                 xlen = strlen(s);
470                                 if (gw->gauge.orientation == XtorientHorizontal) {
471                                         wd = XTextWidth(gw->label.font, s,
472                                                         xlen);
473                                         XDrawString(dpy, win, gc, x - wd / 2, y,
474                                                     s, xlen);
475                                 } else {
476                                         XDrawString(dpy, win, gc, y, x + h, s,
477                                                     xlen);
478                                 }
479                         }
480                 }
481         }
482 }
483
484 /*
485  * Set specified arguments into widget
486  */
487
488 static Boolean
489 GaugeSetValues(Widget old,
490                Widget request, Widget new, ArgList args, Cardinal * num_args)
491 {
492         GaugeWidget oldgw = (GaugeWidget) old;
493         GaugeWidget gw = (GaugeWidget) new;
494         Boolean was_resized = False;
495
496         if (gw->gauge.selected != None) {
497                 XtDisownSelection(new, gw->gauge.selected, CurrentTime);
498                 gw->gauge.selected = None;
499         }
500
501         /* Changes to v0,v1,labels, ntics, nlabels require resize & redraw. */
502         /* Change to value requires redraw and possible resize if autoscale */
503
504         was_resized =
505             gw->gauge.v0 != oldgw->gauge.v0 ||
506             gw->gauge.v1 != oldgw->gauge.v1 ||
507             gw->gauge.ntics != oldgw->gauge.ntics ||
508             gw->gauge.nlabels != oldgw->gauge.nlabels ||
509             gw->gauge.labels != oldgw->gauge.labels;
510
511         if ((gw->gauge.autoScaleUp && gw->gauge.value > gw->gauge.v1) ||
512             (gw->gauge.autoScaleDown && gw->gauge.value < gw->gauge.v1 / 3)) {
513                 AutoScale(gw);
514                 was_resized = TRUE;
515         }
516
517         if (was_resized) {
518                 if (gw->label.resize)
519                         GaugeSize(gw, &gw->core.width, &gw->core.height,
520                                   DEF_LEN);
521                 else
522                         GaugeResize(new);
523         }
524
525         if (gw->gauge.update != oldgw->gauge.update) {
526                 if (gw->gauge.update > 0)
527                         EnableUpdate(gw);
528                 else
529                         DisableUpdate(gw);
530         }
531
532         if (gw->core.background_pixel != oldgw->core.background_pixel) {
533                 XtReleaseGC(new, gw->gauge.inverse_GC);
534                 gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel);
535         }
536
537         return was_resized || gw->gauge.value != oldgw->gauge.value ||
538             XtIsSensitive(old) != XtIsSensitive(new);
539 }
540
541 static XtGeometryResult
542 GaugeQueryGeometry(Widget w,
543                    XtWidgetGeometry * intended, XtWidgetGeometry * preferred)
544 {
545         register GaugeWidget gw = (GaugeWidget) w;
546
547         if (intended->width == w->core.width &&
548             intended->height == w->core.height)
549                 return XtGeometryNo;
550
551         preferred->request_mode = CWWidth | CWHeight;
552         GaugeSize(gw, &preferred->width, &preferred->height, DEF_LEN);
553
554         if ((!(intended->request_mode & CWWidth) ||
555              intended->width >= preferred->width) &&
556             (!(intended->request_mode & CWHeight) ||
557              intended->height >= preferred->height))
558                 return XtGeometryYes;
559         else
560                 return XtGeometryAlmost;
561 }
562 \f
563 /****************************************************************
564  *
565  * Action Procedures
566  *
567  ****************************************************************/
568
569 static void
570 GaugeSelect(Widget w, XEvent * event, String * params, Cardinal * num_params)
571 {
572         GaugeWidget gw = (GaugeWidget) w;
573         Atom seln = XA_PRIMARY;
574
575         if (gw->gauge.selected != None) {
576                 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
577                 gw->gauge.selected = None;
578         }
579
580         if (*num_params > 0) {
581                 seln = XInternAtom(XtDisplay(w), params[0], False);
582                 printf("atom %s is %ld\n", params[0], seln);
583         }
584
585         if (!XtOwnSelection(w, seln, event->xbutton.time, GaugeConvert,
586                             GaugeLoseSel, GaugeDoneSel)) {
587                 /* in real code, this error message would be replaced by
588                  * something more elegant, or at least deleted
589                  */
590
591                 fprintf(stderr, "Gauge failed to get selection, try again\n");
592         } else {
593                 int n;
594                 const int max_selstr = 4 * sizeof(int);
595                 gw->gauge.selected = TRUE;
596                 gw->gauge.selstr = (String) XtMalloc(max_selstr);
597                 n = snprintf(gw->gauge.selstr, max_selstr, "%d", gw->gauge.value);
598                 assert(n >= 0 && n < max_selstr);
599                 GaugeExpose(w, 0, 0);
600         }
601 }
602
603 static Boolean GaugeConvert(Widget w, Atom * selection, /* usually XA_PRIMARY */
604                             Atom * target,      /* requested target */
605                             Atom * type,        /* returned type */
606                             XtPointer * value,  /* returned value */
607                             unsigned long *length,      /* returned length */
608                             int *format)
609 {                               /* returned format */
610         GaugeWidget gw = (GaugeWidget) w;
611         XSelectionRequestEvent *req;
612
613         printf("requesting selection %s:%s\n",
614                XGetAtomName(XtDisplay(w), *selection),
615                XGetAtomName(XtDisplay(w), *target));
616
617 #ifdef HAVE_XMU
618         if (*target == XA_TARGETS(XtDisplay(w))) {
619                 Atom *rval;
620                 XPointer stdTargets;
621                 unsigned long stdLength;
622
623                 /* XmuConvertStandardSelection can handle this.  This function
624                  * will return a list of standard targets.  We prepend TEXT,
625                  * STRING and INTEGER to the list and return it.
626                  */
627
628                 req = XtGetSelectionRequest(w, *selection, NULL);
629                 XmuConvertStandardSelection(w, req->time, selection, target,
630                                             type, &stdTargets,
631                                             &stdLength, format);
632
633                 *type = XA_ATOM;        /* TODO: needed? */
634                 *length = stdLength + 3;
635                 rval = (Atom *) XtMalloc(sizeof(Atom) * (stdLength + 3));
636                 *value = (XtPointer) rval;
637                 *rval++ = XA_INTEGER;
638                 *rval++ = XA_STRING;
639                 *rval++ = XA_TEXT(XtDisplay(w));
640                 memcpy((char *)rval, (char *)stdTargets,
641                        stdLength * sizeof(Atom));
642                 XtFree((char *)stdTargets);
643                 *format = 8 * sizeof(Atom);     /* TODO: needed? */
644                 return True;
645         }
646
647         else
648 #endif
649         if (*target == XA_INTEGER) {
650                 *type = XA_INTEGER;
651                 *length = 1;
652                 *value = (XtPointer) & gw->gauge.value;
653                 *format = 8 * sizeof(int);
654                 return True;
655         }
656
657         else if (*target == XA_STRING
658 #ifdef HAVE_XMU
659                  || *target == XA_TEXT(XtDisplay(w))
660 #endif
661             ) {
662                 *type = *target;
663                 *length = strlen(gw->gauge.selstr) * sizeof(char);
664                 *value = (XtPointer) gw->gauge.selstr;
665                 *format = 8;
666                 return True;
667         }
668
669         else {
670                 /* anything else, we just give it to XmuConvertStandardSelection() */
671 #ifdef HAVE_XMU
672                 req = XtGetSelectionRequest(w, *selection, NULL);
673                 if (XmuConvertStandardSelection(w, req->time, selection, target,
674                                                 type, (XPointer *) value,
675                                                 length, format))
676                         return True;
677                 else
678 #endif
679                 {
680                         printf
681                             ("Gauge: requestor is requesting unsupported selection %s:%s\n",
682                              XGetAtomName(XtDisplay(w), *selection),
683                              XGetAtomName(XtDisplay(w), *target));
684                         return False;
685                 }
686         }
687 }
688
689 static void GaugeLoseSel(Widget w, Atom * selection)
690 {                               /* usually XA_PRIMARY */
691         GaugeWidget gw = (GaugeWidget) w;
692         Display *dpy = XtDisplay(w);
693         Window win = XtWindow(w);
694
695         if (gw->gauge.selstr != NULL) {
696                 XtFree(gw->gauge.selstr);
697                 gw->gauge.selstr = NULL;
698         }
699
700         gw->gauge.selected = False;
701         XClearWindow(dpy, win);
702         GaugeExpose(w, 0, 0);
703 }
704
705 static void GaugeDoneSel(Widget w, Atom * selection,    /* usually XA_PRIMARY */
706                          Atom * target)
707 {                               /* requested target */
708         /* selection done, anything to do? */
709 }
710
711 static void
712 GaugePaste(Widget w, XEvent * event, String * params, Cardinal * num_params)
713 {
714         Atom seln = XA_PRIMARY;
715
716         if (*num_params > 0) {
717                 seln = XInternAtom(XtDisplay(w), params[0], False);
718                 printf("atom %s is %ld\n", params[0], seln);
719         }
720
721         /* try for integer value first */
722         XtGetSelectionValue(w, seln, XA_INTEGER,
723                             GaugeGetSelCB, (XtPointer) XA_INTEGER,
724                             event->xbutton.time);
725 }
726
727 static void
728 GaugeGetSelCB(Widget w,
729               XtPointer client,
730               Atom * selection,
731               Atom * type, XtPointer value, unsigned long *length, int *format)
732 {
733         Display *dpy = XtDisplay(w);
734         Atom target = (Atom) client;
735         int *iptr;
736         char *cptr;
737
738         if (*type == XA_INTEGER) {
739                 iptr = (int *)value;
740                 XawGaugeSetValue(w, *iptr);
741         }
742
743         else if (*type == XA_STRING
744 #ifdef HAVE_XMU
745                  || *type == XA_TEXT(dpy)
746 #endif
747             ) {
748                 cptr = (char *)value;
749                 XawGaugeSetValue(w, atoi(cptr));
750         }
751
752         /* failed, try string */
753         else if (*type == None && target == XA_INTEGER)
754                 XtGetSelectionValue(w, *selection, XA_STRING,
755                                     GaugeGetSelCB, (XtPointer) XA_STRING,
756                                     CurrentTime);
757 }
758 \f
759 /****************************************************************
760  *
761  * Public Procedures
762  *
763  ****************************************************************/
764
765         /* Change gauge value.  Only undraw or draw what needs to be
766          * changed.
767          */
768
769 void XawGaugeSetValue(Widget w, Cardinal value)
770 {
771         GaugeWidget gw = (GaugeWidget) w;
772         int oldvalue;
773         GC gc;
774
775         if (gw->gauge.selected != None) {
776                 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
777                 gw->gauge.selected = None;
778         }
779
780         if (!XtIsRealized(w)) {
781                 gw->gauge.value = value;
782                 return;
783         }
784
785         /* need to rescale? */
786         if ((gw->gauge.autoScaleUp && value > (Cardinal)gw->gauge.v1) ||
787             (gw->gauge.autoScaleDown && value < (Cardinal)gw->gauge.v1 / 3)) {
788                 XtVaSetValues(w, XtNvalue, value, NULL);
789                 return;
790         }
791
792         oldvalue = gw->gauge.value;
793         gw->gauge.value = value;
794
795         gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC;
796         GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue, value);
797 }
798
799 Cardinal XawGaugeGetValue(Widget w)
800 {
801         GaugeWidget gw = (GaugeWidget) w;
802         return gw->gauge.value;
803 }
804 \f
805 /****************************************************************
806  *
807  * Private Procedures
808  *
809  ****************************************************************/
810
811         /* draw the mercury over a specific region */
812
813 static void
814 GaugeMercury(Display * dpy,
815              Window win, GC gc, GaugeWidget gw, Cardinal val0, Cardinal val1)
816 {
817         int v0 = gw->gauge.v0;
818         int v1 = gw->gauge.v1;
819         int vd = v1 - v0;
820         Dimension len;          /* length (width or height) of gauge */
821         Position e0, e1;        /* gauge ends */
822         Position p0, p1;        /* mercury ends */
823         int y;                  /* vertical (horizontal) position */
824         Boolean undraw = FALSE;
825
826         len = gw->gauge.orientation == XtorientHorizontal ?
827             gw->core.width : gw->core.height;
828
829         e0 = gw->gauge.margin0; /* left (top) end */
830         e1 = len - gw->gauge.margin1 - 1;       /* right (bottom) end */
831
832         if (vd <= 0)
833                 vd = 1;
834
835         if (val0 < (Cardinal)v0)
836                 val0 = v0;
837         else if (val0 > (Cardinal)v1)
838                 val0 = v1;
839         if (val1 < (Cardinal)v0)
840                 val1 = v0;
841         else if (val1 > (Cardinal)v1)
842                 val1 = v1;
843
844         p0 = (val0 - v0) * (e1 - e0 - 1) / vd;
845         p1 = (val1 - v0) * (e1 - e0 - 1) / vd;
846
847         if (p1 == p0)
848                 return;
849
850         y = gw->gauge.gmargin;
851
852         if (p1 < p0) {
853                 Position tmp = p0;
854                 p0 = p1;
855                 p1 = tmp;
856                 gc = gw->label.normal_GC;
857                 XSetForeground(dpy, gc, gw->core.background_pixel);
858                 undraw = TRUE;
859         }
860
861         if (gw->gauge.orientation == XtorientHorizontal)
862                 XFillRectangle(dpy, win, gc, e0 + p0 + 1, y + 1, p1 - p0,
863                                GA_WID);
864         else
865                 XFillRectangle(dpy, win, gc, y + 1, e1 - p1, GA_WID, p1 - p0);
866
867         if (undraw)
868                 XSetForeground(dpy, gc, gw->label.foreground);
869 }
870
871 /* Search the labels, find the largest one. */
872 /* TODO: handle vertical fonts? */
873
874 static void MaxLabel(GaugeWidget gw, Dimension * wid,   /* max label width */
875                      Dimension * hgt,   /* max label height */
876                      Dimension * w0,    /* width of first label */
877                      Dimension * w1)
878 {                               /* width of last label */
879         char lstr[80], *lbl;
880         int w;
881         XFontStruct *font = gw->label.font;
882         int i;
883         int lw = 0;
884         int v0 = gw->gauge.v0;
885         int dv = gw->gauge.v1 - v0;
886         int n = gw->gauge.nlabels;
887
888         if (n > 0) {
889                 if (--n <= 0) {
890                         n = 1;
891                         v0 += dv / 2;
892                 }
893
894                 /* loop through all labels, figure out how much room they
895                  * need.
896                  */
897                 w = 0;
898                 for (i = 0; i < gw->gauge.nlabels; ++i) {
899                         if (gw->gauge.labels == NULL)   { /* numeric labels */
900                                 int sz = snprintf(lbl = lstr, sizeof(lstr),
901                                                   "%d", v0 + i * dv / n);
902                                 assert(sz >= 0 && (size_t)sz < sizeof(lstr));
903                         } else
904                                 lbl = gw->gauge.labels[i];
905
906                         if (lbl != NULL) {
907                                 lw = XTextWidth(font, lbl, strlen(lbl));
908                                 w = Max(w, lw);
909                         } else
910                                 lw = 0;
911
912                         if (i == 0 && w0 != NULL)
913                                 *w0 = lw;
914                 }
915                 if (w1 != NULL)
916                         *w1 = lw;
917
918                 *wid = w;
919                 *hgt = font->max_bounds.ascent + font->max_bounds.descent;
920         } else
921                 *wid = *hgt = 0;
922 }
923
924 /* Determine the preferred size for this widget.  choose 100x100 for
925  * debugging.
926  */
927
928 static void
929 GaugeSize(GaugeWidget gw, Dimension * wid, Dimension * hgt, Dimension min_len)
930 {
931         int w, h;               /* width, height of gauge */
932         int vmargin;            /* vertical margin */
933         int hmargin;            /* horizontal margin */
934
935         hmargin = gw->label.internal_width;
936         vmargin = gw->label.internal_height;
937
938         /* find total height (width) of contents */
939
940         /* find minimum size for undecorated gauge */
941
942         if (gw->gauge.orientation == XtorientHorizontal) {
943                 w = min_len;
944                 h = GA_WID + 2; /* gauge itself + edges */
945         } else {
946                 w = GA_WID + 2;
947                 h = min_len;
948         }
949
950         if (gw->gauge.ntics > 0) {
951                 if (gw->gauge.orientation == XtorientHorizontal) {
952                         w = Max(w, gw->gauge.ntics * 3);
953                         h += vmargin + TIC_LEN;
954                 } else {
955                         w += hmargin + TIC_LEN;
956                         h = Max(h, gw->gauge.ntics * 3);
957                 }
958         }
959
960         /* If labels are requested, this gets a little interesting.
961          * We want the end labels centered on the ends of the gauge and
962          * the centers of the labels evenly spaced.  The labels at the ends
963          * will not be the same width, meaning that the gauge itself need
964          * not be centered in the widget.
965          *
966          * First, determine the spacing.  This is the width of the widest
967          * label, plus the internal margin.  Total length of the gauge is
968          * spacing * (nlabels-1).  To this, we add half the width of the
969          * left-most label and half the width of the right-most label
970          * to get the entire desired width of the widget.
971          */
972         if (gw->gauge.nlabels > 0) {
973                 Dimension lwm, lw0, lw1;        /* width of max, left, right labels */
974                 Dimension lh;
975
976                 MaxLabel(gw, &lwm, &lh, &lw0, &lw1);
977
978                 if (gw->gauge.orientation == XtorientHorizontal) {
979                         lwm =
980                             (lwm + hmargin) * (gw->gauge.nlabels - 1) + (lw0 +
981                                                                          lw1) /
982                             2;
983                         w = Max(w, lwm);
984                         h += lh + vmargin;
985                 } else {
986                         lh = lh * gw->gauge.nlabels + (gw->gauge.nlabels -
987                                                        1) * vmargin;
988                         h = Max(h, lh);
989                         w += lwm + hmargin;
990                 }
991         }
992
993         w += hmargin * 2;
994         h += vmargin * 2;
995
996         *wid = w;
997         *hgt = h;
998 }
999
1000 static void AutoScale(GaugeWidget gw)
1001 {
1002         static int scales[3] = { 1, 2, 5 };
1003         int sptr = 0, smult = 1;
1004
1005         if (gw->gauge.autoScaleDown)
1006                 gw->gauge.v1 = 0;
1007         while (gw->gauge.value > gw->gauge.v1) {
1008                 if (++sptr > 2) {
1009                         sptr = 0;
1010                         smult *= 10;
1011                 }
1012                 gw->gauge.v1 = scales[sptr] * smult;
1013         }
1014 }
1015
1016 static void EnableUpdate(GaugeWidget gw)
1017 {
1018         gw->gauge.intervalId =
1019             XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) gw),
1020                             gw->gauge.update * MS_PER_SEC, GaugeGetValue,
1021                             (XtPointer) gw);
1022 }
1023
1024 static void DisableUpdate(GaugeWidget gw)
1025 {
1026         XtRemoveTimeOut(gw->gauge.intervalId);
1027 }
1028
1029 static void GaugeGetValue(XtPointer clientData, XtIntervalId * intervalId)
1030 {
1031         GaugeWidget gw = (GaugeWidget) clientData;
1032         Cardinal value;
1033
1034         if (gw->gauge.update > 0)
1035                 EnableUpdate(gw);
1036
1037         if (gw->gauge.getValue != NULL) {
1038                 XtCallCallbackList((Widget) gw, gw->gauge.getValue,
1039                                    (XtPointer) & value);
1040                 XawGaugeSetValue((Widget) gw, value);
1041         }
1042 }
1043
1044 static GC Get_GC(GaugeWidget gw, Pixel fg)
1045 {
1046         XGCValues values;
1047 #define vmask   GCForeground
1048 #define umask   (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\
1049                 |GCFont|GCDashList|GCArcMode)
1050
1051         values.foreground = fg;
1052
1053         return XtAllocateGC((Widget) gw, 0, vmask, &values, 0L, umask);
1054 }