Mercurial > hg > xemacs-beta
comparison lwlib/xlwgauge.c @ 398:74fd4e045ea6 r21-2-29
Import from CVS: tag r21-2-29
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:13:30 +0200 |
parents | |
children | a86b2b5e0111 |
comparison
equal
deleted
inserted
replaced
397:f4aeb21a5bad | 398:74fd4e045ea6 |
---|---|
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_INCLUDE(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, Cardinal, Cardinal); | |
136 | |
137 static Boolean GaugeConvert (Widget, Atom *, Atom *, Atom *, | |
138 XtPointer *, u_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, u_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 | |
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 constrasting 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 len, w,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 len = strlen(s) ; | |
487 if( gw->gauge.orientation == XtorientHorizontal ) { | |
488 w = XTextWidth(gw->label.font, s, len) ; | |
489 XDrawString(dpy,win,gc, x-w/2,y, s,len) ; | |
490 } | |
491 else { | |
492 XDrawString(dpy,win,gc, y,x+h, s,len) ; | |
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 | |
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 u_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 u_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 u_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 | |
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, | |
823 Cardinal value) | |
824 { | |
825 GaugeWidget gw = (GaugeWidget)w ; | |
826 int oldvalue ; | |
827 GC gc ; | |
828 | |
829 if( gw->gauge.selected != None ) { | |
830 XtDisownSelection(w, gw->gauge.selected, CurrentTime) ; | |
831 gw->gauge.selected = None ; | |
832 } | |
833 | |
834 if( !XtIsRealized(w) ) { | |
835 gw->gauge.value = value ; | |
836 return ; | |
837 } | |
838 | |
839 /* need to rescale? */ | |
840 if(( gw->gauge.autoScaleUp && value > gw->gauge.v1) || | |
841 (gw->gauge.autoScaleDown && value < gw->gauge.v1/3 )) | |
842 { | |
843 XtVaSetValues(w, XtNvalue, value, 0) ; | |
844 return ; | |
845 } | |
846 | |
847 oldvalue = gw->gauge.value ; | |
848 gw->gauge.value = value ; | |
849 | |
850 gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC ; | |
851 GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue,value) ; | |
852 } | |
853 | |
854 | |
855 Cardinal | |
856 XawGaugeGetValue (Widget w) | |
857 { | |
858 GaugeWidget gw = (GaugeWidget)w ; | |
859 return gw->gauge.value ; | |
860 } | |
861 | |
862 | |
863 | |
864 | |
865 /**************************************************************** | |
866 * | |
867 * Private Procedures | |
868 * | |
869 ****************************************************************/ | |
870 | |
871 /* draw the mercury over a specific region */ | |
872 | |
873 static void | |
874 GaugeMercury (Display *dpy, | |
875 Window win, | |
876 GC gc, | |
877 GaugeWidget gw, | |
878 Cardinal val0, | |
879 Cardinal val1) | |
880 { | |
881 int v0 = gw->gauge.v0 ; | |
882 int v1 = gw->gauge.v1 ; | |
883 int vd = v1 - v0 ; | |
884 Dimension len ; /* length (width or height) of gauge */ | |
885 Position e0, e1 ; /* gauge ends */ | |
886 Position p0, p1 ; /* mercury ends */ | |
887 int y ; /* vertical (horizontal) position */ | |
888 Boolean undraw = FALSE ; | |
889 | |
890 len = gw->gauge.orientation == XtorientHorizontal ? | |
891 gw->core.width : gw->core.height ; | |
892 | |
893 e0 = gw->gauge.margin0 ; /* left (top) end */ | |
894 e1 = len - gw->gauge.margin1 -1 ; /* right (bottom) end */ | |
895 | |
896 if( vd <= 0 ) vd = 1 ; | |
897 | |
898 if( val0 < v0 ) val0 = v0 ; | |
899 else if( val0 > v1 ) val0 = v1 ; | |
900 if( val1 < v0 ) val1 = v0 ; | |
901 else if( val1 > v1 ) val1 = v1 ; | |
902 | |
903 p0 = (val0-v0)*(e1-e0-1)/vd ; | |
904 p1 = (val1-v0)*(e1-e0-1)/vd ; | |
905 | |
906 if( p1 == p0 ) | |
907 return ; | |
908 | |
909 y = gw->gauge.gmargin ; | |
910 | |
911 if( p1 < p0 ) | |
912 { | |
913 Position tmp = p0 ; | |
914 p0 = p1 ; | |
915 p1 = tmp ; | |
916 gc = gw->label.normal_GC ; | |
917 XSetForeground(dpy,gc, gw->core.background_pixel) ; | |
918 undraw = TRUE ; | |
919 } | |
920 | |
921 if( gw->gauge.orientation == XtorientHorizontal ) | |
922 XFillRectangle(dpy,win,gc, e0+p0+1,y+1, p1-p0,GA_WID) ; | |
923 else | |
924 XFillRectangle(dpy,win,gc, y+1,e1-p1, GA_WID,p1-p0) ; | |
925 | |
926 if( undraw ) | |
927 XSetForeground(dpy,gc, gw->label.foreground) ; | |
928 } | |
929 | |
930 | |
931 | |
932 /* Search the labels, find the largest one. */ | |
933 /* TODO: handle vertical fonts? */ | |
934 | |
935 static void | |
936 MaxLabel (GaugeWidget gw, | |
937 Dimension *wid, /* max label width */ | |
938 Dimension *hgt, /* max label height */ | |
939 Dimension *w0, /* width of first label */ | |
940 Dimension *w1) /* width of last label */ | |
941 { | |
942 char lstr[80], *lbl ; | |
943 int w ; | |
944 XFontStruct *font = gw->label.font ; | |
945 int i ; | |
946 int lw = 0; | |
947 int v0 = gw->gauge.v0 ; | |
948 int dv = gw->gauge.v1 - v0 ; | |
949 int n = gw->gauge.nlabels ; | |
950 | |
951 if( n > 0 ) | |
952 { | |
953 if( --n <= 0 ) {n = 1 ; v0 += dv/2 ;} | |
954 | |
955 /* loop through all labels, figure out how much room they | |
956 * need. | |
957 */ | |
958 w = 0 ; | |
959 for(i=0; i<gw->gauge.nlabels; ++i) | |
960 { | |
961 if( gw->gauge.labels == NULL ) /* numeric labels */ | |
962 sprintf(lbl = lstr,"%d", v0 + i*dv/n) ; | |
963 else | |
964 lbl = gw->gauge.labels[i] ; | |
965 | |
966 if( lbl != NULL ) { | |
967 lw = XTextWidth(font, lbl, strlen(lbl)) ; | |
968 w = Max( w, lw ) ; | |
969 } | |
970 else | |
971 lw = 0 ; | |
972 | |
973 if( i == 0 && w0 != NULL ) *w0 = lw ; | |
974 } | |
975 if( w1 != NULL ) *w1 = lw ; | |
976 | |
977 *wid = w ; | |
978 *hgt = font->max_bounds.ascent + font->max_bounds.descent ; | |
979 } | |
980 else | |
981 *wid = *hgt = 0 ; | |
982 } | |
983 | |
984 | |
985 /* Determine the preferred size for this widget. choose 100x100 for | |
986 * debugging. | |
987 */ | |
988 | |
989 static void | |
990 GaugeSize (GaugeWidget gw, | |
991 Dimension *wid, | |
992 Dimension *hgt, | |
993 Dimension min_len) | |
994 { | |
995 int w,h ; /* width, height of gauge */ | |
996 int vmargin ; /* vertical margin */ | |
997 int hmargin ; /* horizontal margin */ | |
998 | |
999 hmargin = gw->label.internal_width ; | |
1000 vmargin = gw->label.internal_height ; | |
1001 | |
1002 /* find total height (width) of contents */ | |
1003 | |
1004 | |
1005 /* find minimum size for undecorated gauge */ | |
1006 | |
1007 if( gw->gauge.orientation == XtorientHorizontal ) | |
1008 { | |
1009 w = min_len ; | |
1010 h = GA_WID+2 ; /* gauge itself + edges */ | |
1011 } | |
1012 else | |
1013 { | |
1014 w = GA_WID+2 ; | |
1015 h = min_len ; | |
1016 } | |
1017 | |
1018 if( gw->gauge.ntics > 0 ) | |
1019 { | |
1020 if( gw->gauge.orientation == XtorientHorizontal ) | |
1021 { | |
1022 w = Max(w, gw->gauge.ntics*3) ; | |
1023 h += vmargin + TIC_LEN ; | |
1024 } | |
1025 else | |
1026 { | |
1027 w += hmargin + TIC_LEN ; | |
1028 h = Max(h, gw->gauge.ntics*3) ; | |
1029 } | |
1030 } | |
1031 | |
1032 | |
1033 /* If labels are requested, this gets a little interesting. | |
1034 * We want the end labels centered on the ends of the gauge and | |
1035 * the centers of the labels evenly spaced. The labels at the ends | |
1036 * will not be the same width, meaning that the gauge itself need | |
1037 * not be centered in the widget. | |
1038 * | |
1039 * First, determine the spacing. This is the width of the widest | |
1040 * label, plus the internal margin. Total length of the gauge is | |
1041 * spacing * (nlabels-1). To this, we add half the width of the | |
1042 * left-most label and half the width of the right-most label | |
1043 * to get the entire desired width of the widget. | |
1044 */ | |
1045 if( gw->gauge.nlabels > 0 ) | |
1046 { | |
1047 Dimension lwm, lw0, lw1 ; /* width of max, left, right labels */ | |
1048 Dimension lh ; | |
1049 | |
1050 MaxLabel(gw,&lwm,&lh, &lw0,&lw1) ; | |
1051 | |
1052 if( gw->gauge.orientation == XtorientHorizontal ) | |
1053 { | |
1054 lwm = (lwm+hmargin) * (gw->gauge.nlabels-1) + (lw0+lw1)/2 ; | |
1055 w = Max(w, lwm) ; | |
1056 h += lh + vmargin ; | |
1057 } | |
1058 else | |
1059 { | |
1060 lh = lh*gw->gauge.nlabels + (gw->gauge.nlabels - 1)*vmargin ; | |
1061 h = Max(h, lh) ; | |
1062 w += lwm + hmargin ; | |
1063 } | |
1064 } | |
1065 | |
1066 w += hmargin*2 ; | |
1067 h += vmargin*2 ; | |
1068 | |
1069 *wid = w ; | |
1070 *hgt = h ; | |
1071 } | |
1072 | |
1073 | |
1074 | |
1075 static void | |
1076 AutoScale (GaugeWidget gw) | |
1077 { | |
1078 static int scales[3] = {1,2,5} ; | |
1079 int sptr = 0, smult=1 ; | |
1080 | |
1081 if( gw->gauge.autoScaleDown ) | |
1082 gw->gauge.v1 = 0 ; | |
1083 while( gw->gauge.value > gw->gauge.v1 ) | |
1084 { | |
1085 if( ++sptr > 2 ) { | |
1086 sptr = 0 ; | |
1087 smult *= 10 ; | |
1088 } | |
1089 gw->gauge.v1 = scales[sptr] * smult ; | |
1090 } | |
1091 } | |
1092 | |
1093 static void | |
1094 EnableUpdate (GaugeWidget gw) | |
1095 { | |
1096 gw->gauge.intervalId = | |
1097 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)gw), | |
1098 gw->gauge.update * MS_PER_SEC, GaugeGetValue, | |
1099 (XtPointer)gw) ; | |
1100 } | |
1101 | |
1102 static void | |
1103 DisableUpdate (GaugeWidget gw) | |
1104 { | |
1105 XtRemoveTimeOut(gw->gauge.intervalId) ; | |
1106 } | |
1107 | |
1108 static void | |
1109 GaugeGetValue (XtPointer clientData, | |
1110 XtIntervalId *intervalId) | |
1111 { | |
1112 GaugeWidget gw = (GaugeWidget)clientData ; | |
1113 Cardinal value ; | |
1114 | |
1115 if( gw->gauge.update > 0 ) | |
1116 EnableUpdate(gw) ; | |
1117 | |
1118 if( gw->gauge.getValue != NULL ) | |
1119 { | |
1120 XtCallCallbackList((Widget)gw, gw->gauge.getValue, (XtPointer)&value); | |
1121 XawGaugeSetValue((Widget)gw, value) ; | |
1122 } | |
1123 } | |
1124 | |
1125 | |
1126 static GC | |
1127 Get_GC (GaugeWidget gw, | |
1128 Pixel fg) | |
1129 { | |
1130 XGCValues values ; | |
1131 #define vmask GCForeground | |
1132 #define umask (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\ | |
1133 |GCFont|GCDashList|GCArcMode) | |
1134 | |
1135 values.foreground = fg ; | |
1136 | |
1137 return XtAllocateGC((Widget)gw, 0, vmask, &values, 0L, umask) ; | |
1138 } |