1 /* Gauge Widget for SXEmacs.
2 Copyright (C) 1999 Edward A. Falk
4 This file is part of SXEmacs.
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.
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.
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/>. */
19 /* Synched up with: Gauge.c 1.2 */
22 * Gauge.c - Gauge widget
24 * Author: Edward A. Falk
25 * falk@falconer.vip.best.com
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
37 /* TODO: display time instead of value
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
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"
57 #include <X11/Xmu/Atoms.h>
58 #include <X11/Xmu/Drawing.h>
59 #include <X11/Xmu/StdSel.h>
62 /****************************************************************
66 ****************************************************************/
68 static char defaultTranslations[] = "<Btn1Up>: select()\n\
69 <Key>F1: select(CLIPBOARD)\n\
71 <Key>F2: paste(CLIPBOARD)";
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}
88 {XtNautoScaleUp, XtCAutoScaleUp, XtRBoolean, sizeof(Boolean),
89 offset(gauge.autoScaleUp), XtRImmediate, FALSE}
91 {XtNautoScaleDown, XtCAutoScaleDown, XtRBoolean, sizeof(Boolean),
92 offset(gauge.autoScaleDown), XtRImmediate, FALSE}
94 {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
95 offset(gauge.orientation), XtRImmediate, (XtPointer) XtorientHorizontal}
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}
105 /* member functions */
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 *,
118 static void GaugeSelect(Widget, XEvent *, String *, Cardinal *);
119 static void GaugePaste(Widget, XEvent *, String *, Cardinal *);
121 /* internal privates */
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);
130 static void GaugeGetValue(XtPointer, XtIntervalId *);
131 static void GaugeMercury(Display *, Window, GC, GaugeWidget, Cardinal,
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 *);
141 static GC Get_GC(GaugeWidget, Pixel);
143 static XtActionsRec actionsList[] = {
144 {"select", GaugeSelect},
145 {"paste", GaugePaste},
148 /****************************************************************
150 * Full class record constant
152 ****************************************************************/
154 GaugeClassRec gaugeClassRec = {
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,
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,
192 /* Simple class fields initialization */
194 /* change_sensitive */ XtInheritChangeSensitive
198 /* ThreeD class fields initialization */
200 XtInheritXaw3dShadowDraw /* shadowdraw */
204 /* Label class fields initialization */
209 /* Gauge class fields initialization */
216 WidgetClass gaugeWidgetClass = (WidgetClass) & gaugeClassRec;
218 /****************************************************************
222 ****************************************************************/
224 static void GaugeClassInit(void)
226 XawInitializeWidgetSet();
228 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
235 GaugeInit(Widget request, Widget new, ArgList args, Cardinal * num_args)
237 GaugeWidget gw = (GaugeWidget) new;
239 if (gw->gauge.v0 == 0 && gw->gauge.v1 == 0) {
240 gw->gauge.autoScaleUp = gw->gauge.autoScaleDown = TRUE;
244 /* If size not explicitly set, set it to our preferred size now. */
246 if (request->core.width == 0 || request->core.height == 0) {
248 GaugeSize(gw, &w, &h, DEF_LEN);
249 if (request->core.width == 0)
251 if (request->core.height == 0)
252 new->core.height = h;
253 gw->core.widget_class->core_class.resize(new);
256 gw->gauge.selected = None;
257 gw->gauge.selstr = NULL;
259 if (gw->gauge.update > 0)
262 gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel);
265 static void GaugeDestroy(Widget w)
267 GaugeWidget gw = (GaugeWidget) w;
269 if (gw->gauge.selstr != NULL)
270 XtFree(gw->gauge.selstr);
272 if (gw->gauge.selected != None)
273 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
275 XtReleaseGC(w, gw->gauge.inverse_GC);
277 if (gw->gauge.update > 0)
281 /* React to size change from manager. Label widget will compute some
282 * internal stuff, but we need to override.
285 static void GaugeResize(Widget w)
287 GaugeWidget gw = (GaugeWidget) w;
288 int size; /* height (width) of gauge */
289 int vmargin; /* vertical (horizontal) margin */
290 int hmargin; /* horizontal (vertical) margin */
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;
297 /* TODO: need to call parent resize proc? I don't think so since
298 * we're recomputing everything from scratch anyway.
301 /* find total height (width) of contents */
303 size = GA_WID + 2; /* gauge itself + edges */
305 if (gw->gauge.ntics > 1) /* tic marks */
306 size += vmargin + TIC_LEN;
308 if (gw->gauge.nlabels > 1) {
309 Dimension lwm, lw0, lw1; /* width of max, left, right labels */
312 MaxLabel(gw, &lwm, &lh, &lw0, &lw1);
314 if (gw->gauge.orientation == XtorientHorizontal) {
315 gw->gauge.margin0 = lw0 / 2;
316 gw->gauge.margin1 = lw1 / 2;
317 size += lh + vmargin;
319 gw->gauge.margin0 = gw->gauge.margin1 = lh / 2;
320 size += lwm + vmargin;
323 gw->gauge.margin0 = gw->gauge.margin1 = 0;
325 gw->gauge.margin0 += hmargin;
326 gw->gauge.margin1 += hmargin;
328 /* Now distribute height (width) over components */
330 if (gw->gauge.orientation == XtorientHorizontal)
331 gw->gauge.gmargin = (gw->core.height - size) / 2;
333 gw->gauge.gmargin = (gw->core.width - size) / 2;
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;
339 gw->gauge.lmargin = gw->gauge.tmargin;
343 * Repaint the widget window
347 static void GaugeExpose(Widget w, XEvent * event, Region region)
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 */
355 int len; /* length (width or height) of widget */
356 int hgt; /* height (width) of widget */
357 int e0, e1; /* ends of the gauge */
359 int y; /* vertical (horizontal) position */
361 int v0 = gw->gauge.v0;
362 int v1 = gw->gauge.v1;
363 int value = gw->gauge.value;
365 gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC;
368 gctop = gw->threeD.bot_shadow_GC;
369 gcbot = gw->threeD.top_shadow_GC;
374 if (gw->gauge.orientation == XtorientHorizontal) {
375 len = gw->core.width;
376 hgt = gw->core.height;
378 len = gw->core.height;
379 hgt = gw->core.width;
382 /* if the gauge is selected, signify by drawing the background
383 * in a contrasting color.
386 if (gw->gauge.selected) {
387 XFillRectangle(dpy, win, gc, 0, 0, w->core.width,
389 gc = gw->gauge.inverse_GC;
392 e0 = gw->gauge.margin0; /* left (top) end */
393 e1 = len - gw->gauge.margin1 - 1; /* right (bottom) end */
395 /* Draw the Gauge itself */
397 y = gw->gauge.gmargin;
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,
404 XDrawLine(dpy, win, gcbot, e1, y + 1, e1, y + GA_WID);
405 } else { /* vertical */
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);
414 /* draw the mercury */
416 GaugeMercury(dpy, win, gc, gw, 0, value);
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,
425 XDrawLine(dpy, win, gcbot, x, y, x + 1, y);
426 XDrawLine(dpy, win, gctop, x + 1, y + 1, x + 1,
428 XDrawLine(dpy, win, gctop, x, y + TIC_LEN - 1,
429 x + 1, y + TIC_LEN - 1);
431 XDrawLine(dpy, win, gcbot, y + 1, 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);
443 if (gw->gauge.nlabels > 1) {
444 char label[20], *s = label;
447 if (gw->gauge.orientation == XtorientHorizontal)
448 y = gw->gauge.lmargin +
449 gw->label.font->max_bounds.ascent - 1;
451 y = gw->gauge.lmargin;
452 h = gw->label.font->max_bounds.ascent / 2;
455 for (i = 0; i < gw->gauge.nlabels; ++i) {
456 if (gw->gauge.labels == NULL)
459 v0) / (gw->gauge.nlabels -
462 s = gw->gauge.labels[i];
464 x = e0 + i * (e1 - e0 -
465 1) / (gw->gauge.nlabels - 1);
467 if (gw->gauge.orientation == XtorientHorizontal) {
468 wd = XTextWidth(gw->label.font, s,
470 XDrawString(dpy, win, gc, x - wd / 2, y,
473 XDrawString(dpy, win, gc, y, x + h, s,
482 * Set specified arguments into widget
486 GaugeSetValues(Widget old,
487 Widget request, Widget new, ArgList args, Cardinal * num_args)
489 GaugeWidget oldgw = (GaugeWidget) old;
490 GaugeWidget gw = (GaugeWidget) new;
491 Boolean was_resized = False;
493 if (gw->gauge.selected != None) {
494 XtDisownSelection(new, gw->gauge.selected, CurrentTime);
495 gw->gauge.selected = None;
498 /* Changes to v0,v1,labels, ntics, nlabels require resize & redraw. */
499 /* Change to value requires redraw and possible resize if autoscale */
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;
508 if ((gw->gauge.autoScaleUp && gw->gauge.value > gw->gauge.v1) ||
509 (gw->gauge.autoScaleDown && gw->gauge.value < gw->gauge.v1 / 3)) {
515 if (gw->label.resize)
516 GaugeSize(gw, &gw->core.width, &gw->core.height,
522 if (gw->gauge.update != oldgw->gauge.update) {
523 if (gw->gauge.update > 0)
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);
534 return was_resized || gw->gauge.value != oldgw->gauge.value ||
535 XtIsSensitive(old) != XtIsSensitive(new);
538 static XtGeometryResult
539 GaugeQueryGeometry(Widget w,
540 XtWidgetGeometry * intended, XtWidgetGeometry * preferred)
542 register GaugeWidget gw = (GaugeWidget) w;
544 if (intended->width == w->core.width &&
545 intended->height == w->core.height)
548 preferred->request_mode = CWWidth | CWHeight;
549 GaugeSize(gw, &preferred->width, &preferred->height, DEF_LEN);
551 if ((!(intended->request_mode & CWWidth) ||
552 intended->width >= preferred->width) &&
553 (!(intended->request_mode & CWHeight) ||
554 intended->height >= preferred->height))
555 return XtGeometryYes;
557 return XtGeometryAlmost;
560 /****************************************************************
564 ****************************************************************/
567 GaugeSelect(Widget w, XEvent * event, String * params, Cardinal * num_params)
569 GaugeWidget gw = (GaugeWidget) w;
570 Atom seln = XA_PRIMARY;
572 if (gw->gauge.selected != None) {
573 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
574 gw->gauge.selected = None;
577 if (*num_params > 0) {
578 seln = XInternAtom(XtDisplay(w), params[0], False);
579 printf("atom %s is %ld\n", params[0], seln);
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
588 fprintf(stderr, "Gauge failed to get selection, try again\n");
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);
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 */
603 { /* returned format */
604 GaugeWidget gw = (GaugeWidget) w;
605 XSelectionRequestEvent *req;
607 printf("requesting selection %s:%s\n",
608 XGetAtomName(XtDisplay(w), *selection),
609 XGetAtomName(XtDisplay(w), *target));
612 if (*target == XA_TARGETS(XtDisplay(w))) {
615 unsigned long stdLength;
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.
622 req = XtGetSelectionRequest(w, *selection, NULL);
623 XmuConvertStandardSelection(w, req->time, selection, target,
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;
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? */
643 if (*target == XA_INTEGER) {
646 *value = (XtPointer) & gw->gauge.value;
647 *format = 8 * sizeof(int);
651 else if (*target == XA_STRING
653 || *target == XA_TEXT(XtDisplay(w))
657 *length = strlen(gw->gauge.selstr) * sizeof(char);
658 *value = (XtPointer) gw->gauge.selstr;
664 /* anything else, we just give it to XmuConvertStandardSelection() */
666 req = XtGetSelectionRequest(w, *selection, NULL);
667 if (XmuConvertStandardSelection(w, req->time, selection, target,
668 type, (XPointer *) value,
675 ("Gauge: requestor is requesting unsupported selection %s:%s\n",
676 XGetAtomName(XtDisplay(w), *selection),
677 XGetAtomName(XtDisplay(w), *target));
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);
689 if (gw->gauge.selstr != NULL) {
690 XtFree(gw->gauge.selstr);
691 gw->gauge.selstr = NULL;
694 gw->gauge.selected = False;
695 XClearWindow(dpy, win);
696 GaugeExpose(w, 0, 0);
699 static void GaugeDoneSel(Widget w, Atom * selection, /* usually XA_PRIMARY */
701 { /* requested target */
702 /* selection done, anything to do? */
706 GaugePaste(Widget w, XEvent * event, String * params, Cardinal * num_params)
708 Atom seln = XA_PRIMARY;
710 if (*num_params > 0) {
711 seln = XInternAtom(XtDisplay(w), params[0], False);
712 printf("atom %s is %ld\n", params[0], seln);
715 /* try for integer value first */
716 XtGetSelectionValue(w, seln, XA_INTEGER,
717 GaugeGetSelCB, (XtPointer) XA_INTEGER,
718 event->xbutton.time);
722 GaugeGetSelCB(Widget w,
725 Atom * type, XtPointer value, unsigned long *length, int *format)
727 Display *dpy = XtDisplay(w);
728 Atom target = (Atom) client;
732 if (*type == XA_INTEGER) {
734 XawGaugeSetValue(w, *iptr);
737 else if (*type == XA_STRING
739 || *type == XA_TEXT(dpy)
742 cptr = (char *)value;
743 XawGaugeSetValue(w, atoi(cptr));
746 /* failed, try string */
747 else if (*type == None && target == XA_INTEGER)
748 XtGetSelectionValue(w, *selection, XA_STRING,
749 GaugeGetSelCB, (XtPointer) XA_STRING,
753 /****************************************************************
757 ****************************************************************/
759 /* Change gauge value. Only undraw or draw what needs to be
763 void XawGaugeSetValue(Widget w, Cardinal value)
765 GaugeWidget gw = (GaugeWidget) w;
769 if (gw->gauge.selected != None) {
770 XtDisownSelection(w, gw->gauge.selected, CurrentTime);
771 gw->gauge.selected = None;
774 if (!XtIsRealized(w)) {
775 gw->gauge.value = value;
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);
786 oldvalue = gw->gauge.value;
787 gw->gauge.value = value;
789 gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC;
790 GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue, value);
793 Cardinal XawGaugeGetValue(Widget w)
795 GaugeWidget gw = (GaugeWidget) w;
796 return gw->gauge.value;
799 /****************************************************************
803 ****************************************************************/
805 /* draw the mercury over a specific region */
808 GaugeMercury(Display * dpy,
809 Window win, GC gc, GaugeWidget gw, Cardinal val0, Cardinal val1)
811 int v0 = gw->gauge.v0;
812 int v1 = gw->gauge.v1;
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;
820 len = gw->gauge.orientation == XtorientHorizontal ?
821 gw->core.width : gw->core.height;
823 e0 = gw->gauge.margin0; /* left (top) end */
824 e1 = len - gw->gauge.margin1 - 1; /* right (bottom) end */
829 if (val0 < (Cardinal)v0)
831 else if (val0 > (Cardinal)v1)
833 if (val1 < (Cardinal)v0)
835 else if (val1 > (Cardinal)v1)
838 p0 = (val0 - v0) * (e1 - e0 - 1) / vd;
839 p1 = (val1 - v0) * (e1 - e0 - 1) / vd;
844 y = gw->gauge.gmargin;
850 gc = gw->label.normal_GC;
851 XSetForeground(dpy, gc, gw->core.background_pixel);
855 if (gw->gauge.orientation == XtorientHorizontal)
856 XFillRectangle(dpy, win, gc, e0 + p0 + 1, y + 1, p1 - p0,
859 XFillRectangle(dpy, win, gc, y + 1, e1 - p1, GA_WID, p1 - p0);
862 XSetForeground(dpy, gc, gw->label.foreground);
865 /* Search the labels, find the largest one. */
866 /* TODO: handle vertical fonts? */
868 static void MaxLabel(GaugeWidget gw, Dimension * wid, /* max label width */
869 Dimension * hgt, /* max label height */
870 Dimension * w0, /* width of first label */
872 { /* width of last label */
875 XFontStruct *font = gw->label.font;
878 int v0 = gw->gauge.v0;
879 int dv = gw->gauge.v1 - v0;
880 int n = gw->gauge.nlabels;
888 /* loop through all labels, figure out how much room they
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);
896 lbl = gw->gauge.labels[i];
899 lw = XTextWidth(font, lbl, strlen(lbl));
904 if (i == 0 && w0 != NULL)
911 *hgt = font->max_bounds.ascent + font->max_bounds.descent;
916 /* Determine the preferred size for this widget. choose 100x100 for
921 GaugeSize(GaugeWidget gw, Dimension * wid, Dimension * hgt, Dimension min_len)
923 int w, h; /* width, height of gauge */
924 int vmargin; /* vertical margin */
925 int hmargin; /* horizontal margin */
927 hmargin = gw->label.internal_width;
928 vmargin = gw->label.internal_height;
930 /* find total height (width) of contents */
932 /* find minimum size for undecorated gauge */
934 if (gw->gauge.orientation == XtorientHorizontal) {
936 h = GA_WID + 2; /* gauge itself + edges */
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;
947 w += hmargin + TIC_LEN;
948 h = Max(h, gw->gauge.ntics * 3);
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.
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.
964 if (gw->gauge.nlabels > 0) {
965 Dimension lwm, lw0, lw1; /* width of max, left, right labels */
968 MaxLabel(gw, &lwm, &lh, &lw0, &lw1);
970 if (gw->gauge.orientation == XtorientHorizontal) {
972 (lwm + hmargin) * (gw->gauge.nlabels - 1) + (lw0 +
978 lh = lh * gw->gauge.nlabels + (gw->gauge.nlabels -
992 static void AutoScale(GaugeWidget gw)
994 static int scales[3] = { 1, 2, 5 };
995 int sptr = 0, smult = 1;
997 if (gw->gauge.autoScaleDown)
999 while (gw->gauge.value > gw->gauge.v1) {
1004 gw->gauge.v1 = scales[sptr] * smult;
1008 static void EnableUpdate(GaugeWidget gw)
1010 gw->gauge.intervalId =
1011 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) gw),
1012 gw->gauge.update * MS_PER_SEC, GaugeGetValue,
1016 static void DisableUpdate(GaugeWidget gw)
1018 XtRemoveTimeOut(gw->gauge.intervalId);
1021 static void GaugeGetValue(XtPointer clientData, XtIntervalId * intervalId)
1023 GaugeWidget gw = (GaugeWidget) clientData;
1026 if (gw->gauge.update > 0)
1029 if (gw->gauge.getValue != NULL) {
1030 XtCallCallbackList((Widget) gw, gw->gauge.getValue,
1031 (XtPointer) & value);
1032 XawGaugeSetValue((Widget) gw, value);
1036 static GC Get_GC(GaugeWidget gw, Pixel fg)
1039 #define vmask GCForeground
1040 #define umask (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\
1041 |GCFont|GCDashList|GCArcMode)
1043 values.foreground = fg;
1045 return XtAllocateGC((Widget) gw, 0, vmask, &values, 0L, umask);