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