Mercurial > hg > xemacs-beta
comparison lwlib/xlwradio.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 | b8cc9ab3f761 |
comparison
equal
deleted
inserted
replaced
397:f4aeb21a5bad | 398:74fd4e045ea6 |
---|---|
1 /* Radio 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: Radio.c 1.1 */ | |
22 | |
23 /* | |
24 * Radio.c - Radio button widget | |
25 * | |
26 * Author: Edward A. Falk | |
27 * falk@falconer.vip.best.com | |
28 * | |
29 * Date: June 30, 1997 | |
30 * | |
31 * | |
32 * Overview: This widget is identical to the Toggle widget in behavior, | |
33 * but completely different in appearance. This widget looks like a small | |
34 * diamond-shaped button with a label to the right. | |
35 * | |
36 * To make this work, we subclass the Toggle widget to inherit its behavior | |
37 * and to inherit the label-drawing function from which Toggle is | |
38 * subclassed. We then completely replace the Expose, Set, Unset | |
39 * and Highlight member functions. | |
40 * | |
41 * The Set and Unset actions are slightly unorthodox. In Toggle's | |
42 * ClassInit function, Toggle searches the Command actions list and | |
43 * "steals" the Set and Unset functions, caching pointers to them in its | |
44 * class record. It then calls these functions from its own ToggleSet | |
45 * and Toggle actions. | |
46 * | |
47 * We, in turn, override the Set() and Unset() actions in our own ClassRec. | |
48 */ | |
49 | |
50 | |
51 #include <config.h> | |
52 #include <stdio.h> | |
53 | |
54 #include <X11/IntrinsicP.h> | |
55 #include <X11/StringDefs.h> | |
56 #include ATHENA_INCLUDE(XawInit.h) | |
57 #include "../src/xmu.h" | |
58 #include "xlwradioP.h" | |
59 | |
60 #define BOX_SIZE 13 | |
61 | |
62 #define rclass(w) ((RadioWidgetClass)((w)->core.widget_class)) | |
63 | |
64 | |
65 #ifdef _ThreeDP_h | |
66 #define swid(rw) ((rw)->threeD.shadow_width) | |
67 #else | |
68 #define swid(rw) ((rw)->core.border_width) | |
69 #endif | |
70 | |
71 #define bsize(rw) (rclass(rw)->radio_class.dsize) | |
72 #define bs(rw) (bsize(rw) + 2*swid(rw)) | |
73 | |
74 | |
75 | |
76 /**************************************************************** | |
77 * | |
78 * Full class record constant | |
79 * | |
80 ****************************************************************/ | |
81 | |
82 /* The translations table from Toggle do not need to be | |
83 * overridden by Radio | |
84 */ | |
85 | |
86 | |
87 /* Member functions */ | |
88 | |
89 static void RadioInit (Widget, Widget, ArgList, Cardinal *); | |
90 static void RadioExpose (Widget, XEvent *, Region); | |
91 static void RadioResize (Widget); | |
92 static void RadioDestroy (Widget, XtPointer, XtPointer); | |
93 static void RadioClassInit (void); | |
94 static void RadioClassPartInit (WidgetClass); | |
95 static Boolean RadioSetValues (Widget, Widget, Widget, ArgList, Cardinal *); | |
96 static void DrawDiamond (Widget); | |
97 static XtGeometryResult RadioQueryGeometry (Widget, XtWidgetGeometry *, | |
98 XtWidgetGeometry *); | |
99 | |
100 /* Action procs */ | |
101 | |
102 static void RadioHighlight (Widget, XEvent *, String *, Cardinal *); | |
103 static void RadioUnhighlight (Widget, XEvent *, String *, Cardinal *); | |
104 | |
105 /* internal privates */ | |
106 | |
107 static void RadioSize (RadioWidget, Dimension *, Dimension *); | |
108 | |
109 /* The actions table from Toggle is almost perfect, but we need | |
110 * to override Highlight, Set, and Unset. | |
111 */ | |
112 | |
113 static XtActionsRec actionsList[] = | |
114 { | |
115 {"highlight", RadioHighlight}, | |
116 {"unhighlight", RadioUnhighlight}, | |
117 }; | |
118 | |
119 #define SuperClass ((ToggleWidgetClass)&toggleClassRec) | |
120 | |
121 RadioClassRec radioClassRec = { | |
122 { | |
123 (WidgetClass) SuperClass, /* superclass */ | |
124 "Radio", /* class_name */ | |
125 sizeof(RadioRec), /* size */ | |
126 RadioClassInit, /* class_initialize */ | |
127 RadioClassPartInit, /* class_part_initialize */ | |
128 FALSE, /* class_inited */ | |
129 RadioInit, /* initialize */ | |
130 NULL, /* initialize_hook */ | |
131 XtInheritRealize, /* realize */ | |
132 actionsList, /* actions */ | |
133 XtNumber(actionsList), /* num_actions */ | |
134 NULL, /* resources */ | |
135 0, /* resource_count */ | |
136 NULLQUARK, /* xrm_class */ | |
137 TRUE, /* compress_motion */ | |
138 TRUE, /* compress_exposure */ | |
139 TRUE, /* compress_enterleave */ | |
140 FALSE, /* visible_interest */ | |
141 NULL, /* destroy */ | |
142 RadioResize, /* resize */ | |
143 RadioExpose, /* expose */ | |
144 RadioSetValues, /* set_values */ | |
145 NULL, /* set_values_hook */ | |
146 XtInheritSetValuesAlmost, /* set_values_almost */ | |
147 NULL, /* get_values_hook */ | |
148 NULL, /* accept_focus */ | |
149 XtVersion, /* version */ | |
150 NULL, /* callback_private */ | |
151 XtInheritTranslations, /* tm_table */ | |
152 RadioQueryGeometry, /* query_geometry */ | |
153 XtInheritDisplayAccelerator, /* display_accelerator */ | |
154 NULL /* extension */ | |
155 }, /* CoreClass fields initialization */ | |
156 { | |
157 XtInheritChangeSensitive /* change_sensitive */ | |
158 }, /* SimpleClass fields initialization */ | |
159 #ifdef _ThreeDP_h | |
160 { | |
161 XtInheritXaw3dShadowDraw /* field not used */ | |
162 }, /* ThreeDClass fields initialization */ | |
163 #endif | |
164 { | |
165 0 /* field not used */ | |
166 }, /* LabelClass fields initialization */ | |
167 { | |
168 0 /* field not used */ | |
169 }, /* CommandClass fields initialization */ | |
170 { | |
171 RadioSet, /* Set Procedure. */ | |
172 RadioUnset, /* Unset Procedure. */ | |
173 NULL /* extension. */ | |
174 }, /* ToggleClass fields initialization */ | |
175 { | |
176 BOX_SIZE, | |
177 DrawDiamond, /* draw procedure */ | |
178 NULL /* extension. */ | |
179 } /* RadioClass fields initialization */ | |
180 }; | |
181 | |
182 /* for public consumption */ | |
183 WidgetClass radioWidgetClass = (WidgetClass) &radioClassRec; | |
184 | |
185 | |
186 | |
187 | |
188 | |
189 | |
190 /**************************************************************** | |
191 * | |
192 * Class Methods | |
193 * | |
194 ****************************************************************/ | |
195 | |
196 static void | |
197 RadioClassInit (void) | |
198 { | |
199 XawInitializeWidgetSet(); | |
200 } | |
201 | |
202 static void | |
203 RadioClassPartInit (WidgetClass class) | |
204 { | |
205 RadioWidgetClass c = (RadioWidgetClass) class ; | |
206 RadioWidgetClass super = (RadioWidgetClass)c->core_class.superclass ; | |
207 | |
208 if( c->radio_class.drawDiamond == NULL || | |
209 c->radio_class.drawDiamond == XtInheritDrawDiamond ) | |
210 { | |
211 c->radio_class.drawDiamond = super->radio_class.drawDiamond ; | |
212 } | |
213 } | |
214 | |
215 | |
216 | |
217 | |
218 /*ARGSUSED*/ | |
219 static void | |
220 RadioInit (Widget request, | |
221 Widget new, | |
222 ArgList args, | |
223 Cardinal *num_args) | |
224 { | |
225 RadioWidget rw = (RadioWidget) new; | |
226 RadioWidget rw_req = (RadioWidget) request; | |
227 Dimension w,h ; | |
228 | |
229 /* Select initial size for the widget */ | |
230 if( rw_req->core.width == 0 || rw_req->core.height == 0 ) | |
231 { | |
232 RadioSize(rw, &w,&h) ; | |
233 if( rw_req->core.width == 0 ) | |
234 rw->core.width = w ; | |
235 if( rw_req->core.height == 0 ) | |
236 rw->core.height = h ; | |
237 rw->core.widget_class->core_class.resize(new) ; | |
238 } | |
239 } | |
240 | |
241 /* Function Name: RadioDestroy | |
242 * Description: Destroy Callback for radio widget. | |
243 * Arguments: w - the radio widget that is being destroyed. | |
244 * junk, grabage - not used. | |
245 * Returns: none. | |
246 */ | |
247 | |
248 /* ARGSUSED */ | |
249 static void | |
250 RadioDestroy (Widget w, | |
251 XtPointer junk, | |
252 XtPointer garbage) | |
253 { | |
254 /* TODO: get rid of this */ | |
255 } | |
256 | |
257 | |
258 /* React to size change from manager. Label widget will compute some internal | |
259 * stuff, but we need to override. This code requires knowledge of the | |
260 * internals of the Label widget. | |
261 */ | |
262 | |
263 static void | |
264 RadioResize (Widget w) | |
265 { | |
266 RadioWidget rw = (RadioWidget)w ; | |
267 | |
268 /* call parent resize proc */ | |
269 SuperClass->core_class.resize(w) ; | |
270 | |
271 /* override label offset */ | |
272 | |
273 switch( rw->label.justify ) { | |
274 case XtJustifyLeft: | |
275 rw->label.label_x += bs(rw) + rw->label.internal_width ; | |
276 break ; | |
277 case XtJustifyRight: | |
278 break ; | |
279 case XtJustifyCenter: | |
280 default: | |
281 rw->label.label_x += (bs(rw) + rw->label.internal_width)/2 ; | |
282 break ; | |
283 } | |
284 } | |
285 | |
286 | |
287 /* | |
288 * Repaint the widget window. | |
289 */ | |
290 | |
291 static void | |
292 RadioExpose (Widget w, | |
293 XEvent *event, | |
294 Region region) | |
295 { | |
296 RadioWidget rw = (RadioWidget) w ; | |
297 Display *dpy = XtDisplay(w) ; | |
298 Window win = XtWindow(w) ; | |
299 GC gc ; | |
300 Pixmap left_bitmap ; | |
301 extern WidgetClass labelWidgetClass ; | |
302 | |
303 /* Note: the Label widget examines the region to decide if anything | |
304 * needs to be drawn. I'm not sure that this is worth the effort, | |
305 * but it bears thinking on. | |
306 */ | |
307 | |
308 /* Command widget may sometimes override the label GC in order | |
309 * to draw inverse video. We don't use inverse video, so we need | |
310 * to restore the label's normal GC. | |
311 */ | |
312 rw->label.normal_GC = rw->command.normal_GC ; | |
313 | |
314 | |
315 /* Let label widget draw the label. If there was an lbm_x | |
316 * field, we could let Label draw the bitmap too. But there | |
317 * isn't, so we need to temporarily remove the bitmap and | |
318 * draw it ourself later. | |
319 */ | |
320 left_bitmap = rw->label.left_bitmap ; | |
321 rw->label.left_bitmap = None ; | |
322 labelWidgetClass->core_class.expose(w,event,region) ; | |
323 rw->label.left_bitmap = left_bitmap ; | |
324 | |
325 /* now manually draw the left bitmap. TODO: 3-d look, xaw-xpm */ | |
326 gc = XtIsSensitive(w) ? rw->label.normal_GC : rw->label.gray_GC ; | |
327 if( left_bitmap != None && rw->label.lbm_width > 0 ) | |
328 { | |
329 /* TODO: handle pixmaps */ | |
330 XCopyPlane(dpy, left_bitmap, win, gc, | |
331 0,0, rw->label.lbm_width, rw->label.lbm_height, | |
332 (int) rw->label.internal_width*2 + bs(rw), | |
333 (int) rw->label.internal_height + rw->label.lbm_y, | |
334 (u_long) 1L) ; | |
335 } | |
336 | |
337 /* Finally, the button itself */ | |
338 ((RadioWidgetClass)(w->core.widget_class))->radio_class.drawDiamond(w) ; | |
339 } | |
340 | |
341 | |
342 | |
343 | |
344 /************************************************************ | |
345 * | |
346 * Set specified arguments into widget | |
347 * | |
348 ***********************************************************/ | |
349 | |
350 | |
351 /* ARGSUSED */ | |
352 static Boolean | |
353 RadioSetValues (Widget current, | |
354 Widget request, | |
355 Widget new, | |
356 ArgList args, | |
357 Cardinal *num_args) | |
358 { | |
359 RadioWidget oldrw = (RadioWidget) current; | |
360 RadioWidget newrw = (RadioWidget) new; | |
361 | |
362 /* Need to find out if the size of the widget changed. Set new size | |
363 * if it did and resize is permitted. One way to determine of the | |
364 * widget changed size would be to scan the args list. Another way | |
365 * is to compare the old and new widgets and see if any of several | |
366 * size-related fields have been changed. The Label widget chose the | |
367 * former method, but I choose the latter. | |
368 */ | |
369 | |
370 if( newrw->label.resize && | |
371 ( newrw->core.width != oldrw->core.width || | |
372 newrw->core.height != oldrw->core.height || | |
373 newrw->core.border_width != oldrw->core.border_width ) ) | |
374 { | |
375 RadioSize(newrw, &newrw->core.width, &newrw->core.height) ; | |
376 } | |
377 | |
378 return FALSE ; | |
379 } | |
380 | |
381 static XtGeometryResult | |
382 RadioQueryGeometry (Widget w, | |
383 XtWidgetGeometry *intended, | |
384 XtWidgetGeometry *preferred) | |
385 { | |
386 RadioWidget rw = (RadioWidget) w; | |
387 | |
388 preferred->request_mode = CWWidth | CWHeight; | |
389 RadioSize(rw, &preferred->width, &preferred->height) ; | |
390 | |
391 if ( ((intended->request_mode & (CWWidth | CWHeight)) | |
392 == (CWWidth | CWHeight)) && | |
393 intended->width == preferred->width && | |
394 intended->height == preferred->height) | |
395 return XtGeometryYes; | |
396 else if (preferred->width == w->core.width && | |
397 preferred->height == w->core.height) | |
398 return XtGeometryNo; | |
399 else | |
400 return XtGeometryAlmost; | |
401 } | |
402 | |
403 | |
404 | |
405 | |
406 | |
407 /************************************************************ | |
408 * | |
409 * Action Procedures | |
410 * | |
411 ************************************************************/ | |
412 | |
413 /* | |
414 * Draw the highlight border around the widget. The Command widget | |
415 * did this by drawing through a mask. We do it by just drawing the | |
416 * border. | |
417 */ | |
418 | |
419 static void | |
420 DrawHighlight (Widget w, | |
421 GC gc) | |
422 { | |
423 RadioWidget rw = (RadioWidget)w; | |
424 XRectangle rects[4] ; | |
425 Dimension ht = rw->command.highlight_thickness ; | |
426 | |
427 if( ht <= 0 || | |
428 ht > rw->core.width/2 || | |
429 ht > rw->core.height/2 ) | |
430 return ; | |
431 | |
432 if( ! XtIsRealized(w) ) | |
433 return ; | |
434 | |
435 rects[0].x = 0 ; rects[0].y = 0 ; | |
436 rects[0].width = rw->core.width ; rects[0].height = ht ; | |
437 rects[1].x = 0 ; rects[1].y = rw->core.height - ht ; | |
438 rects[1].width = rw->core.width ; rects[1].height = ht ; | |
439 rects[2].x = 0 ; rects[2].y = ht ; | |
440 rects[2].width = ht ; rects[2].height = rw->core.height - ht*2 ; | |
441 rects[3].x = rw->core.width - ht ; rects[3].y = ht ; | |
442 rects[3].width = ht ; rects[3].height = rw->core.height - ht*2 ; | |
443 XFillRectangles( XtDisplay(w), XtWindow(w), gc, rects, 4) ; | |
444 } | |
445 | |
446 static void | |
447 RadioHighlight (Widget w, | |
448 XEvent *event, | |
449 String *params, | |
450 Cardinal *num_params) | |
451 { | |
452 RadioWidget rw = (RadioWidget)w; | |
453 DrawHighlight(w, rw->command.normal_GC) ; | |
454 } | |
455 | |
456 | |
457 static void | |
458 RadioUnhighlight (Widget w, | |
459 XEvent *event, | |
460 String *params, | |
461 Cardinal *num_params) | |
462 { | |
463 RadioWidget rw = (RadioWidget)w; | |
464 DrawHighlight(w, rw->command.inverse_GC) ; | |
465 } | |
466 | |
467 | |
468 /* ARGSUSED */ | |
469 void | |
470 RadioSet (Widget w, | |
471 XEvent *event, | |
472 String *params, /* unused */ | |
473 Cardinal *num_params) /* unused */ | |
474 { | |
475 RadioWidget rw = (RadioWidget)w; | |
476 RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class ; | |
477 | |
478 if( rw->command.set ) | |
479 return ; | |
480 | |
481 rw->command.set = TRUE ; | |
482 if( XtIsRealized(w) ) | |
483 class->radio_class.drawDiamond(w) ; | |
484 } | |
485 | |
486 | |
487 /* ARGSUSED */ | |
488 void | |
489 RadioUnset (Widget w, | |
490 XEvent *event, | |
491 String *params, /* unused */ | |
492 Cardinal *num_params) /* unused */ | |
493 { | |
494 RadioWidget rw = (RadioWidget)w; | |
495 RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class ; | |
496 | |
497 if( ! rw->command.set ) | |
498 return ; | |
499 | |
500 rw->command.set = FALSE ; | |
501 if( XtIsRealized(w) ) | |
502 class->radio_class.drawDiamond(w) ; | |
503 } | |
504 | |
505 | |
506 | |
507 | |
508 /************************************************************ | |
509 * | |
510 * Internal Procedures | |
511 * | |
512 ************************************************************/ | |
513 | |
514 | |
515 /* Size of widget. Width is size of box plus width of border around | |
516 * box plus width of label plus three margins plus the size of the left | |
517 * bitmap, if any. Height is max(box,bitmap,label) plus two margins. | |
518 */ | |
519 | |
520 static void | |
521 RadioSize (RadioWidget rw, | |
522 Dimension *w, | |
523 Dimension *h) | |
524 { | |
525 *w = rw->label.label_width + bs(rw) + LEFT_OFFSET(rw) + | |
526 3 * rw->label.internal_width ; | |
527 *h = Max( rw->label.label_height, bs(rw) ) + | |
528 2 * rw->label.internal_width ; | |
529 } | |
530 | |
531 | |
532 static void | |
533 DrawDiamond (Widget w) | |
534 { | |
535 RadioWidget rw = (RadioWidget) w ; | |
536 Display *dpy = XtDisplay(w) ; | |
537 Window win = XtWindow(w) ; | |
538 GC gc, gci ; | |
539 | |
540 XPoint pts[5] ; | |
541 Dimension del = bsize(rw)/2 ; | |
542 Position x,y ; /* diamond center */ | |
543 #ifdef _ThreeDP_h | |
544 int i=0; | |
545 Dimension s = swid(rw) ; | |
546 GC top, bot, ctr ; | |
547 #endif | |
548 gc = XtIsSensitive(w) ? rw->command.normal_GC : rw->label.gray_GC ; | |
549 | |
550 gci = rw->command.set ? rw->command.normal_GC : rw->command.inverse_GC ; | |
551 | |
552 x = rw->label.internal_width + bs(rw)/2 ; | |
553 y = rw->core.height/2 ; | |
554 | |
555 #ifdef _ThreeDP_h | |
556 if( ! rw->command.set ) { | |
557 top = rw->threeD.top_shadow_GC ; | |
558 bot = rw->threeD.bot_shadow_GC ; | |
559 ctr = gc ; /* TODO */ | |
560 } else { | |
561 top = rw->threeD.bot_shadow_GC ; | |
562 bot = rw->threeD.top_shadow_GC ; | |
563 ctr = gc ; /* TODO */ | |
564 } | |
565 #endif | |
566 | |
567 pts[0].x = x - del ; | |
568 pts[0].y = y ; | |
569 pts[1].x = x ; | |
570 pts[1].y = y - del ; | |
571 pts[2].x = x + del ; | |
572 pts[2].y = y ; | |
573 pts[3].x = x ; | |
574 pts[3].y = y + del ; | |
575 pts[4] = pts[0] ; | |
576 XFillPolygon(dpy,win,gci, pts,4, Convex, CoordModeOrigin) ; | |
577 | |
578 #ifdef _ThreeDP_h | |
579 for(i=0; i<s; ++i) { | |
580 XDrawLine(dpy,win,bot, x-i-del,y, x,y+del+i) ; | |
581 XDrawLine(dpy,win,bot, x+del+i,y, x,y+del+i) ; | |
582 } | |
583 for(i=0; i<s; ++i) { | |
584 XDrawLine(dpy,win,top, x-del-i,y, x,y-del-i) ; | |
585 XDrawLine(dpy,win,top, x+del+i,y, x,y-del-i) ; | |
586 } | |
587 #else | |
588 XDrawLines(dpy,win,gc, pts,5, CoordModeOrigin) ; | |
589 #endif | |
590 } |