Mercurial > hg > xemacs-beta
diff lwlib/xlwgauge.c @ 424:11054d720c21 r21-2-20
Import from CVS: tag r21-2-20
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:26:11 +0200 |
parents | |
children | 9d177e8d4150 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlib/xlwgauge.c Mon Aug 13 11:26:11 2007 +0200 @@ -0,0 +1,1138 @@ +/* Gauge Widget for XEmacs. + Copyright (C) 1999 Edward A. Falk + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Gauge.c 1.2 */ + +/* + * Gauge.c - Gauge widget + * + * Author: Edward A. Falk + * falk@falconer.vip.best.com + * + * Date: July 9, 1997 + * + * Note: for fun and demonstration purposes, I have added selection + * capabilities to this widget. If you select the widget, you create + * a primary selection containing the current value of the widget in + * both integer and string form. If you copy into the widget, the + * primary selection is converted to an integer value and the gauge is + * set to that value. + */ + +/* TODO: display time instead of value + */ + +#define DEF_LEN 50 /* default width (or height for vertical gauge) */ +#define MIN_LEN 10 /* minimum reasonable width (height) */ +#define TIC_LEN 6 /* length of tic marks */ +#define GA_WID 3 /* width of gauge */ +#define MS_PER_SEC 1000 + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <X11/IntrinsicP.h> +#include <X11/Xatom.h> +#include <X11/StringDefs.h> +#include <X11/Xaw/XawInit.h> +#include "xlwgaugeP.h" +#include "../src/xmu.h" +#ifdef HAVE_XMU +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/Drawing.h> +#include <X11/Xmu/StdSel.h> +#endif + + +/**************************************************************** + * + * Gauge resources + * + ****************************************************************/ + + +static char defaultTranslations[] = + "<Btn1Up>: select()\n\ + <Key>F1: select(CLIPBOARD)\n\ + <Btn2Up>: paste()\n\ + <Key>F2: paste(CLIPBOARD)" ; + + + +#define offset(field) XtOffsetOf(GaugeRec, field) +static XtResource resources[] = { + {XtNvalue, XtCValue, XtRInt, sizeof(int), + offset(gauge.value), XtRImmediate, (XtPointer)0}, + {XtNminValue, XtCMinValue, XtRInt, sizeof(int), + offset(gauge.v0), XtRImmediate, (XtPointer)0}, + {XtNmaxValue, XtCMaxValue, XtRInt, sizeof(int), + offset(gauge.v1), XtRImmediate, (XtPointer)100}, + {XtNntics, XtCNTics, XtRInt, sizeof(int), + offset(gauge.ntics), XtRImmediate, (XtPointer) 0}, + {XtNnlabels, XtCNLabels, XtRInt, sizeof(int), + offset(gauge.nlabels), XtRImmediate, (XtPointer) 0}, + {XtNlabels, XtCLabels, XtRStringArray, sizeof(String *), + offset(gauge.labels), XtRStringArray, NULL}, + {XtNautoScaleUp, XtCAutoScaleUp, XtRBoolean, sizeof(Boolean), + offset(gauge.autoScaleUp), XtRImmediate, FALSE}, + {XtNautoScaleDown, XtCAutoScaleDown, XtRBoolean, sizeof(Boolean), + offset(gauge.autoScaleDown), XtRImmediate, FALSE}, + {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation), + offset(gauge.orientation), XtRImmediate, (XtPointer)XtorientHorizontal}, + {XtNupdate, XtCInterval, XtRInt, sizeof(int), + offset(gauge.update), XtRImmediate, (XtPointer)0}, + {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(gauge.getValue), XtRImmediate, (XtPointer)NULL}, +}; +#undef offset + + + + /* member functions */ + +static void GaugeClassInit (void); +static void GaugeInit (Widget, Widget, ArgList, Cardinal *); +static void GaugeDestroy (Widget); +static void GaugeResize (Widget); +static void GaugeExpose (Widget, XEvent *, Region); +static Boolean GaugeSetValues (Widget, Widget, Widget, ArgList, Cardinal *); +static XtGeometryResult GaugeQueryGeometry (Widget, XtWidgetGeometry *, + XtWidgetGeometry *); + + /* action procs */ + +static void GaugeSelect (Widget, XEvent *, String *, Cardinal *); +static void GaugePaste (Widget, XEvent *, String *, Cardinal *); + + /* internal privates */ + +static void GaugeSize (GaugeWidget, Dimension *, Dimension *, Dimension); +static void MaxLabel (GaugeWidget, Dimension *, Dimension *, + Dimension *, Dimension *); +static void AutoScale (GaugeWidget); +static void EnableUpdate (GaugeWidget); +static void DisableUpdate (GaugeWidget); + +static void GaugeGetValue (XtPointer, XtIntervalId *); +static void GaugeMercury (Display *, Window, GC, GaugeWidget, Cardinal, Cardinal); + +static Boolean GaugeConvert (Widget, Atom *, Atom *, Atom *, + XtPointer *, u_long *, int *); +static void GaugeLoseSel (Widget, Atom *); +static void GaugeDoneSel (Widget, Atom *, Atom *); +static void GaugeGetSelCB (Widget, XtPointer, Atom *, Atom *, + XtPointer, u_long *, int *); + +static GC Get_GC (GaugeWidget, Pixel); + + +static XtActionsRec actionsList[] = +{ + {"select", GaugeSelect}, + {"paste", GaugePaste}, +} ; + + + +/**************************************************************** + * + * Full class record constant + * + ****************************************************************/ + +GaugeClassRec gaugeClassRec = { + { +/* core_class fields */ + /* superclass */ (WidgetClass) &labelClassRec, + /* class_name */ "Gauge", + /* widget_size */ sizeof(GaugeRec), + /* class_initialize */ GaugeClassInit, + /* class_part_initialize */ NULL, + /* class_inited */ FALSE, + /* initialize */ GaugeInit, + /* initialize_hook */ NULL, + /* realize */ XtInheritRealize, /* TODO? */ + /* actions */ actionsList, + /* num_actions */ XtNumber(actionsList), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ GaugeDestroy, + /* resize */ GaugeResize, + /* expose */ GaugeExpose, + /* set_values */ GaugeSetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ defaultTranslations, + /* query_geometry */ GaugeQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, +/* Simple class fields initialization */ + { + /* change_sensitive */ XtInheritChangeSensitive + }, +#ifdef _ThreeDP_h +/* ThreeD class fields initialization */ + { + XtInheritXaw3dShadowDraw /* shadowdraw */ + }, +#endif +/* Label class fields initialization */ + { + /* ignore */ 0 + }, +/* Gauge class fields initialization */ + { + /* extension */ NULL + }, +}; + +WidgetClass gaugeWidgetClass = (WidgetClass)&gaugeClassRec; + + + + +/**************************************************************** + * + * Member Procedures + * + ****************************************************************/ + +static void +GaugeClassInit (void) +{ + XawInitializeWidgetSet(); +#ifdef HAVE_XMU + XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, + NULL, 0) ; +#endif +} + + + +/* ARGSUSED */ +static void +GaugeInit (Widget request, + Widget new, + ArgList args, + Cardinal *num_args) +{ + GaugeWidget gw = (GaugeWidget) new; + + if( gw->gauge.v0 == 0 && gw->gauge.v1 == 0 ) { + gw->gauge.autoScaleUp = gw->gauge.autoScaleDown = TRUE ; + AutoScale(gw) ; + } + + /* If size not explicitly set, set it to our preferred size now. */ + + if( request->core.width == 0 || request->core.height == 0 ) + { + Dimension w,h ; + GaugeSize(gw, &w,&h, DEF_LEN) ; + if( request->core.width == 0 ) + new->core.width = w ; + if( request->core.height == 0 ) + new->core.height = h ; + gw->core.widget_class->core_class.resize(new) ; + } + + gw->gauge.selected = None ; + gw->gauge.selstr = NULL ; + + if( gw->gauge.update > 0 ) + EnableUpdate(gw) ; + + gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel) ; +} + +static void +GaugeDestroy (Widget w) +{ + GaugeWidget gw = (GaugeWidget)w; + + if( gw->gauge.selstr != NULL ) + XtFree(gw->gauge.selstr) ; + + if( gw->gauge.selected != None ) + XtDisownSelection(w, gw->gauge.selected, CurrentTime) ; + + XtReleaseGC(w, gw->gauge.inverse_GC) ; + + if( gw->gauge.update > 0 ) + DisableUpdate(gw) ; +} + + +/* React to size change from manager. Label widget will compute some + * internal stuff, but we need to override. + */ + +static void +GaugeResize (Widget w) +{ + GaugeWidget gw = (GaugeWidget)w; + int size ; /* height (width) of gauge */ + int vmargin ; /* vertical (horizontal) margin */ + int hmargin ; /* horizontal (vertical) margin */ + + vmargin = gw->gauge.orientation == XtorientHorizontal ? + gw->label.internal_height : gw->label.internal_width ; + hmargin = gw->gauge.orientation == XtorientHorizontal ? + gw->label.internal_width : gw->label.internal_height ; + + /* TODO: need to call parent resize proc? I don't think so since + * we're recomputing everything from scratch anyway. + */ + + /* find total height (width) of contents */ + + size = GA_WID+2 ; /* gauge itself + edges */ + + if( gw->gauge.ntics > 1 ) /* tic marks */ + size += vmargin + TIC_LEN ; + + if( gw->gauge.nlabels > 1 ) + { + Dimension lwm, lw0, lw1 ; /* width of max, left, right labels */ + Dimension lh ; + + MaxLabel(gw,&lwm,&lh, &lw0,&lw1) ; + + if( gw->gauge.orientation == XtorientHorizontal ) + { + gw->gauge.margin0 = lw0 / 2 ; + gw->gauge.margin1 = lw1 / 2 ; + size += lh + vmargin ; + } + else + { + gw->gauge.margin0 = + gw->gauge.margin1 = lh / 2 ; + size += lwm + vmargin ; + } + } + else + gw->gauge.margin0 = gw->gauge.margin1 = 0 ; + + gw->gauge.margin0 += hmargin ; + gw->gauge.margin1 += hmargin ; + + /* Now distribute height (width) over components */ + + if( gw->gauge.orientation == XtorientHorizontal ) + gw->gauge.gmargin = (gw->core.height-size)/2 ; + else + gw->gauge.gmargin = (gw->core.width-size)/2 ; + + gw->gauge.tmargin = gw->gauge.gmargin + GA_WID+2 + vmargin ; + if( gw->gauge.ntics > 1 ) + gw->gauge.lmargin = gw->gauge.tmargin + TIC_LEN + vmargin ; + else + gw->gauge.lmargin = gw->gauge.tmargin ; +} + +/* + * Repaint the widget window + */ + +/* ARGSUSED */ +static void +GaugeExpose (Widget w, + XEvent *event, + Region region) +{ + GaugeWidget gw = (GaugeWidget) w; +register Display *dpy = XtDisplay(w) ; +register Window win = XtWindow(w) ; + GC gc; /* foreground, background */ + GC gctop, gcbot ; /* dark, light shadows */ + + int len ; /* length (width or height) of widget */ + int hgt ; /* height (width) of widget */ + int e0,e1 ; /* ends of the gauge */ + int x ; + int y ; /* vertical (horizontal) position */ + int i ; + int v0 = gw->gauge.v0 ; + int v1 = gw->gauge.v1 ; + int value = gw->gauge.value ; + + gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC ; + + +#ifdef _ThreeDP_h + gctop = gw->threeD.bot_shadow_GC ; + gcbot = gw->threeD.top_shadow_GC ; +#else + gctop = gcbot = gc ; +#endif + + if( gw->gauge.orientation == XtorientHorizontal ) { + len = gw->core.width ; + hgt = gw->core.height ; + } else { + len = gw->core.height ; + hgt = gw->core.width ; + } + + /* if the gauge is selected, signify by drawing the background + * in a constrasting color. + */ + + if( gw->gauge.selected ) + { + XFillRectangle(dpy,win, gc, 0,0, w->core.width,w->core.height) ; + gc = gw->gauge.inverse_GC ; + } + + e0 = gw->gauge.margin0 ; /* left (top) end */ + e1 = len - gw->gauge.margin1 -1 ; /* right (bottom) end */ + + /* Draw the Gauge itself */ + + y = gw->gauge.gmargin ; + + if( gw->gauge.orientation == XtorientHorizontal ) /* horizontal */ + { + XDrawLine(dpy,win,gctop, e0+1,y, e1-1,y) ; + XDrawLine(dpy,win,gctop, e0,y+1, e0,y+GA_WID) ; + XDrawLine(dpy,win,gcbot, e0+1, y+GA_WID+1, e1-1, y+GA_WID+1) ; + XDrawLine(dpy,win,gcbot, e1,y+1, e1,y+GA_WID) ; + } + else /* vertical */ + { + XDrawLine(dpy,win,gctop, y,e0+1, y,e1-1) ; + XDrawLine(dpy,win,gctop, y+1,e0, y+GA_WID,e0) ; + XDrawLine(dpy,win,gcbot, y+GA_WID+1,e0+1, y+GA_WID+1, e1-1) ; + XDrawLine(dpy,win,gcbot, y+1,e1, y+GA_WID,e1) ; + } + + + /* draw the mercury */ + + GaugeMercury(dpy, win, gc, gw, 0,value) ; + + + if( gw->gauge.ntics > 1 ) + { + y = gw->gauge.tmargin ; + for(i=0; i<gw->gauge.ntics; ++i) + { + x = e0 + i*(e1-e0-1)/(gw->gauge.ntics-1) ; + if( gw->gauge.orientation == XtorientHorizontal ) { + XDrawLine(dpy,win,gcbot, x,y+1, x,y+TIC_LEN-2) ; + XDrawLine(dpy,win,gcbot, x,y, x+1,y) ; + XDrawLine(dpy,win,gctop, x+1,y+1, x+1,y+TIC_LEN-2) ; + XDrawLine(dpy,win,gctop, x,y+TIC_LEN-1, x+1,y+TIC_LEN-1) ; + } + else { + XDrawLine(dpy,win,gcbot, y+1,x, y+TIC_LEN-2,x) ; + XDrawLine(dpy,win,gcbot, y,x, y,x+1) ; + XDrawLine(dpy,win,gctop, y+1,x+1, y+TIC_LEN-2,x+1) ; + XDrawLine(dpy,win,gctop, y+TIC_LEN-1,x, y+TIC_LEN-1,x+1) ; + } + } + } + + /* draw labels */ + if( gw->gauge.nlabels > 1 ) + { + char label[20], *s = label ; + int len, w,h =0 ; + + if( gw->gauge.orientation == XtorientHorizontal ) + y = gw->gauge.lmargin + gw->label.font->max_bounds.ascent - 1 ; + else { + y = gw->gauge.lmargin ; + h = gw->label.font->max_bounds.ascent / 2 ; + } + + for(i=0; i<gw->gauge.nlabels; ++i) + { + if( gw->gauge.labels == NULL ) + sprintf(label, "%d", v0+i*(v1 - v0)/(gw->gauge.nlabels - 1)) ; + else + s = gw->gauge.labels[i] ; + if( s != NULL ) { + x = e0 + i*(e1-e0-1)/(gw->gauge.nlabels-1) ; + len = strlen(s) ; + if( gw->gauge.orientation == XtorientHorizontal ) { + w = XTextWidth(gw->label.font, s, len) ; + XDrawString(dpy,win,gc, x-w/2,y, s,len) ; + } + else { + XDrawString(dpy,win,gc, y,x+h, s,len) ; + } + } + } + } +} + + +/* + * Set specified arguments into widget + */ + +static Boolean +GaugeSetValues (Widget old, + Widget request, + Widget new, + ArgList args, + Cardinal *num_args) +{ + GaugeWidget oldgw = (GaugeWidget) old; + GaugeWidget gw = (GaugeWidget) new; + Boolean was_resized = False; + + if( gw->gauge.selected != None ) { + XtDisownSelection(new, gw->gauge.selected, CurrentTime) ; + gw->gauge.selected = None ; + } + + /* Changes to v0,v1,labels, ntics, nlabels require resize & redraw. */ + /* Change to value requires redraw and possible resize if autoscale */ + + was_resized = + gw->gauge.v0 != oldgw->gauge.v0 || + gw->gauge.v1 != oldgw->gauge.v1 || + gw->gauge.ntics != oldgw->gauge.ntics || + gw->gauge.nlabels != oldgw->gauge.nlabels || + gw->gauge.labels != oldgw->gauge.labels ; + + if( (gw->gauge.autoScaleUp && gw->gauge.value > gw->gauge.v1) || + (gw->gauge.autoScaleDown && gw->gauge.value < gw->gauge.v1/3 )) + { + AutoScale(gw) ; + was_resized = TRUE ; + } + + if( was_resized ) { + if( gw->label.resize ) + GaugeSize(gw, &gw->core.width, &gw->core.height, DEF_LEN) ; + else + GaugeResize(new) ; + } + + if( gw->gauge.update != oldgw->gauge.update ) + { + if( gw->gauge.update > 0 ) + EnableUpdate(gw) ; + else + DisableUpdate(gw) ; + } + + if( gw->core.background_pixel != oldgw->core.background_pixel ) + { + XtReleaseGC(new, gw->gauge.inverse_GC) ; + gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel) ; + } + + return was_resized || gw->gauge.value != oldgw->gauge.value || + XtIsSensitive(old) != XtIsSensitive(new); +} + + +static XtGeometryResult +GaugeQueryGeometry (Widget w, + XtWidgetGeometry *intended, + XtWidgetGeometry *preferred) +{ + register GaugeWidget gw = (GaugeWidget)w; + + if( intended->width == w->core.width && + intended->height == w->core.height ) + return XtGeometryNo ; + + preferred->request_mode = CWWidth | CWHeight; + GaugeSize(gw, &preferred->width, &preferred->height, DEF_LEN) ; + + if( (!(intended->request_mode & CWWidth) || + intended->width >= preferred->width) && + (!(intended->request_mode & CWHeight) || + intended->height >= preferred->height) ) + return XtGeometryYes; + else + return XtGeometryAlmost; +} + + + + +/**************************************************************** + * + * Action Procedures + * + ****************************************************************/ + +static void +GaugeSelect (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + GaugeWidget gw = (GaugeWidget)w ; + Atom seln = XA_PRIMARY ; + + if( gw->gauge.selected != None ) { + XtDisownSelection(w, gw->gauge.selected, CurrentTime) ; + gw->gauge.selected = None ; + } + + if( *num_params > 0 ) { + seln = XInternAtom(XtDisplay(w), params[0], False) ; + printf("atom %s is %ld\n", params[0], seln) ; + } + + if( ! XtOwnSelection(w, seln, event->xbutton.time, GaugeConvert, + GaugeLoseSel, GaugeDoneSel) ) + { + /* in real code, this error message would be replaced by + * something more elegant, or at least deleted + */ + + fprintf(stderr, "Gauge failed to get selection, try again\n") ; + } + else + { + gw->gauge.selected = TRUE ; + gw->gauge.selstr = (String)XtMalloc(4*sizeof(int)) ; + sprintf(gw->gauge.selstr, "%d", gw->gauge.value) ; + GaugeExpose(w,0,0) ; + } +} + + +static Boolean +GaugeConvert (Widget w, + Atom *selection, /* usually XA_PRIMARY */ + Atom *target, /* requested target */ + Atom *type, /* returned type */ + XtPointer *value, /* returned value */ + u_long *length, /* returned length */ + int *format) /* returned format */ +{ + GaugeWidget gw = (GaugeWidget)w ; + XSelectionRequestEvent *req ; + + printf( "requesting selection %s:%s\n", + XGetAtomName(XtDisplay(w),*selection), + XGetAtomName(XtDisplay(w),*target)); + +#ifdef HAVE_XMU + if( *target == XA_TARGETS(XtDisplay(w)) ) + { + Atom *rval, *stdTargets ; + u_long stdLength ; + + /* XmuConvertStandardSelection can handle this. This function + * will return a list of standard targets. We prepend TEXT, + * STRING and INTEGER to the list and return it. + */ + + req = XtGetSelectionRequest(w, *selection, NULL) ; + XmuConvertStandardSelection(w, req->time, selection, target, + type, (XPointer*)&stdTargets, &stdLength, format) ; + + *type = XA_ATOM ; /* TODO: needed? */ + *length = stdLength + 3 ; + rval = (Atom *) XtMalloc(sizeof(Atom)*(stdLength+3)) ; + *value = (XtPointer) rval ; + *rval++ = XA_INTEGER ; + *rval++ = XA_STRING ; + *rval++ = XA_TEXT(XtDisplay(w)) ; + bcopy((char *)stdTargets, (char *)rval, stdLength*sizeof(Atom)) ; + XtFree((char*) stdTargets) ; + *format = 8*sizeof(Atom) ; /* TODO: needed? */ + return True ; + } + + else +#endif + if( *target == XA_INTEGER ) + { + *type = XA_INTEGER ; + *length = 1 ; + *value = (XtPointer) &gw->gauge.value ; + *format = 8*sizeof(int) ; + return True ; + } + + else if( *target == XA_STRING +#ifdef HAVE_XMU + || + *target == XA_TEXT(XtDisplay(w)) +#endif + ) + { + *type = *target ; + *length = strlen(gw->gauge.selstr)*sizeof(char) ; + *value = (XtPointer) gw->gauge.selstr ; + *format = 8 ; + return True ; + } + + else + { + /* anything else, we just give it to XmuConvertStandardSelection() */ +#ifdef HAVE_XMU + req = XtGetSelectionRequest(w, *selection, NULL) ; + if( XmuConvertStandardSelection(w, req->time, selection, target, + type, (XPointer *) value, length, format) ) + return True ; + else +#endif + { + printf( + "Gauge: requestor is requesting unsupported selection %s:%s\n", + XGetAtomName(XtDisplay(w),*selection), + XGetAtomName(XtDisplay(w),*target)); + return False ; + } + } +} + + + +static void +GaugeLoseSel (Widget w, + Atom *selection) /* usually XA_PRIMARY */ +{ + GaugeWidget gw = (GaugeWidget)w ; + Display *dpy = XtDisplay(w) ; + Window win = XtWindow(w) ; + + if( gw->gauge.selstr != NULL ) { + XtFree(gw->gauge.selstr) ; + gw->gauge.selstr = NULL ; + } + + gw->gauge.selected = False ; + XClearWindow(dpy,win) ; + GaugeExpose(w,0,0) ; +} + + +static void +GaugeDoneSel (Widget w, + Atom *selection, /* usually XA_PRIMARY */ + Atom *target) /* requested target */ +{ + /* selection done, anything to do? */ +} + + +static void +GaugePaste (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + Atom seln = XA_PRIMARY ; + + if( *num_params > 0 ) { + seln = XInternAtom(XtDisplay(w), params[0], False) ; + printf("atom %s is %ld\n", params[0], seln) ; + } + + /* try for integer value first */ + XtGetSelectionValue(w, seln, XA_INTEGER, + GaugeGetSelCB, (XtPointer)XA_INTEGER, + event->xbutton.time) ; +} + +static void +GaugeGetSelCB (Widget w, + XtPointer client, + Atom *selection, + Atom *type, + XtPointer value, + u_long *length, + int *format) +{ + Display *dpy = XtDisplay(w) ; + Atom target = (Atom)client ; + int *iptr ; + char *cptr ; + + if( *type == XA_INTEGER ) { + iptr = (int *)value ; + XawGaugeSetValue(w, *iptr) ; + } + + else if( *type == XA_STRING +#ifdef HAVE_XMU + || + *type == XA_TEXT(dpy) +#endif + ) + { + cptr = (char *)value ; + XawGaugeSetValue(w, atoi(cptr)) ; + } + + /* failed, try string */ + else if( *type == None && target == XA_INTEGER ) + XtGetSelectionValue(w, *selection, XA_STRING, + GaugeGetSelCB, (XtPointer)XA_STRING, + CurrentTime) ; +} + + + +/**************************************************************** + * + * Public Procedures + * + ****************************************************************/ + + + /* Change gauge value. Only undraw or draw what needs to be + * changed. + */ + +void +XawGaugeSetValue (Widget w, + Cardinal value) +{ + GaugeWidget gw = (GaugeWidget)w ; + int oldvalue ; + GC gc ; + + if( gw->gauge.selected != None ) { + XtDisownSelection(w, gw->gauge.selected, CurrentTime) ; + gw->gauge.selected = None ; + } + + if( !XtIsRealized(w) ) { + gw->gauge.value = value ; + return ; + } + + /* need to rescale? */ + if(( gw->gauge.autoScaleUp && value > gw->gauge.v1) || + (gw->gauge.autoScaleDown && value < gw->gauge.v1/3 )) + { + XtVaSetValues(w, XtNvalue, value, 0) ; + return ; + } + + oldvalue = gw->gauge.value ; + gw->gauge.value = value ; + + gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC ; + GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue,value) ; +} + + +Cardinal +XawGaugeGetValue (Widget w) +{ + GaugeWidget gw = (GaugeWidget)w ; + return gw->gauge.value ; +} + + + + +/**************************************************************** + * + * Private Procedures + * + ****************************************************************/ + + /* draw the mercury over a specific region */ + +static void +GaugeMercury (Display *dpy, + Window win, + GC gc, + GaugeWidget gw, + Cardinal val0, + Cardinal val1) +{ + int v0 = gw->gauge.v0 ; + int v1 = gw->gauge.v1 ; + int vd = v1 - v0 ; + Dimension len ; /* length (width or height) of gauge */ + Position e0, e1 ; /* gauge ends */ + Position p0, p1 ; /* mercury ends */ + int y ; /* vertical (horizontal) position */ + Boolean undraw = FALSE ; + + len = gw->gauge.orientation == XtorientHorizontal ? + gw->core.width : gw->core.height ; + + e0 = gw->gauge.margin0 ; /* left (top) end */ + e1 = len - gw->gauge.margin1 -1 ; /* right (bottom) end */ + + if( vd <= 0 ) vd = 1 ; + + if( val0 < v0 ) val0 = v0 ; + else if( val0 > v1 ) val0 = v1 ; + if( val1 < v0 ) val1 = v0 ; + else if( val1 > v1 ) val1 = v1 ; + + p0 = (val0-v0)*(e1-e0-1)/vd ; + p1 = (val1-v0)*(e1-e0-1)/vd ; + + if( p1 == p0 ) + return ; + + y = gw->gauge.gmargin ; + + if( p1 < p0 ) + { + Position tmp = p0 ; + p0 = p1 ; + p1 = tmp ; + gc = gw->label.normal_GC ; + XSetForeground(dpy,gc, gw->core.background_pixel) ; + undraw = TRUE ; + } + + if( gw->gauge.orientation == XtorientHorizontal ) + XFillRectangle(dpy,win,gc, e0+p0+1,y+1, p1-p0,GA_WID) ; + else + XFillRectangle(dpy,win,gc, y+1,e1-p1, GA_WID,p1-p0) ; + + if( undraw ) + XSetForeground(dpy,gc, gw->label.foreground) ; +} + + + +/* Search the labels, find the largest one. */ +/* TODO: handle vertical fonts? */ + +static void +MaxLabel (GaugeWidget gw, + Dimension *wid, /* max label width */ + Dimension *hgt, /* max label height */ + Dimension *w0, /* width of first label */ + Dimension *w1) /* width of last label */ +{ + char lstr[80], *lbl ; + int w ; + XFontStruct *font = gw->label.font ; + int i ; + int lw = 0; + int v0 = gw->gauge.v0 ; + int dv = gw->gauge.v1 - v0 ; + int n = gw->gauge.nlabels ; + + if( n > 0 ) + { + if( --n <= 0 ) {n = 1 ; v0 += dv/2 ;} + + /* loop through all labels, figure out how much room they + * need. + */ + w = 0 ; + for(i=0; i<gw->gauge.nlabels; ++i) + { + if( gw->gauge.labels == NULL ) /* numeric labels */ + sprintf(lbl = lstr,"%d", v0 + i*dv/n) ; + else + lbl = gw->gauge.labels[i] ; + + if( lbl != NULL ) { + lw = XTextWidth(font, lbl, strlen(lbl)) ; + w = Max( w, lw ) ; + } + else + lw = 0 ; + + if( i == 0 && w0 != NULL ) *w0 = lw ; + } + if( w1 != NULL ) *w1 = lw ; + + *wid = w ; + *hgt = font->max_bounds.ascent + font->max_bounds.descent ; + } + else + *wid = *hgt = 0 ; +} + + +/* Determine the preferred size for this widget. choose 100x100 for + * debugging. + */ + +static void +GaugeSize (GaugeWidget gw, + Dimension *wid, + Dimension *hgt, + Dimension min_len) +{ + int w,h ; /* width, height of gauge */ + int vmargin ; /* vertical margin */ + int hmargin ; /* horizontal margin */ + + hmargin = gw->label.internal_width ; + vmargin = gw->label.internal_height ; + + /* find total height (width) of contents */ + + + /* find minimum size for undecorated gauge */ + + if( gw->gauge.orientation == XtorientHorizontal ) + { + w = min_len ; + h = GA_WID+2 ; /* gauge itself + edges */ + } + else + { + w = GA_WID+2 ; + h = min_len ; + } + + if( gw->gauge.ntics > 0 ) + { + if( gw->gauge.orientation == XtorientHorizontal ) + { + w = Max(w, gw->gauge.ntics*3) ; + h += vmargin + TIC_LEN ; + } + else + { + w += hmargin + TIC_LEN ; + h = Max(h, gw->gauge.ntics*3) ; + } + } + + + /* If labels are requested, this gets a little interesting. + * We want the end labels centered on the ends of the gauge and + * the centers of the labels evenly spaced. The labels at the ends + * will not be the same width, meaning that the gauge itself need + * not be centered in the widget. + * + * First, determine the spacing. This is the width of the widest + * label, plus the internal margin. Total length of the gauge is + * spacing * (nlabels-1). To this, we add half the width of the + * left-most label and half the width of the right-most label + * to get the entire desired width of the widget. + */ + if( gw->gauge.nlabels > 0 ) + { + Dimension lwm, lw0, lw1 ; /* width of max, left, right labels */ + Dimension lh ; + + MaxLabel(gw,&lwm,&lh, &lw0,&lw1) ; + + if( gw->gauge.orientation == XtorientHorizontal ) + { + lwm = (lwm+hmargin) * (gw->gauge.nlabels-1) + (lw0+lw1)/2 ; + w = Max(w, lwm) ; + h += lh + vmargin ; + } + else + { + lh = lh*gw->gauge.nlabels + (gw->gauge.nlabels - 1)*vmargin ; + h = Max(h, lh) ; + w += lwm + hmargin ; + } + } + + w += hmargin*2 ; + h += vmargin*2 ; + + *wid = w ; + *hgt = h ; +} + + + +static void +AutoScale (GaugeWidget gw) +{ + static int scales[3] = {1,2,5} ; + int sptr = 0, smult=1 ; + + if( gw->gauge.autoScaleDown ) + gw->gauge.v1 = 0 ; + while( gw->gauge.value > gw->gauge.v1 ) + { + if( ++sptr > 2 ) { + sptr = 0 ; + smult *= 10 ; + } + gw->gauge.v1 = scales[sptr] * smult ; + } +} + +static void +EnableUpdate (GaugeWidget gw) +{ + gw->gauge.intervalId = + XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)gw), + gw->gauge.update * MS_PER_SEC, GaugeGetValue, + (XtPointer)gw) ; +} + +static void +DisableUpdate (GaugeWidget gw) +{ + XtRemoveTimeOut(gw->gauge.intervalId) ; +} + +static void +GaugeGetValue (XtPointer clientData, + XtIntervalId *intervalId) +{ + GaugeWidget gw = (GaugeWidget)clientData ; + Cardinal value ; + + if( gw->gauge.update > 0 ) + EnableUpdate(gw) ; + + if( gw->gauge.getValue != NULL ) + { + XtCallCallbackList((Widget)gw, gw->gauge.getValue, (XtPointer)&value); + XawGaugeSetValue((Widget)gw, value) ; + } +} + + +static GC +Get_GC (GaugeWidget gw, + Pixel fg) +{ + XGCValues values ; +#define vmask GCForeground +#define umask (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\ + |GCFont|GCDashList|GCArcMode) + + values.foreground = fg ; + + return XtAllocateGC((Widget)gw, 0, vmask, &values, 0L, umask) ; +}