XEmacs 21.2.36 "Notos"
[chise/xemacs-chise.git-] / lwlib / xlwradio.c
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_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 \f
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, garbage - 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     /* The label set values routine can resize the widget. We need to
379      * recalculate if this is true.
380      */
381     if (newrw->label.label_x != oldrw->label.label_x)
382     {
383       RadioResize (new);
384     }
385     return FALSE ;
386 }
387
388 static XtGeometryResult
389 RadioQueryGeometry (Widget w,
390                     XtWidgetGeometry *intended,
391                     XtWidgetGeometry *preferred)
392 {
393     RadioWidget rw = (RadioWidget) w;
394
395     preferred->request_mode = CWWidth | CWHeight;
396     RadioSize(rw, &preferred->width, &preferred->height) ;
397
398     if (  ((intended->request_mode & (CWWidth | CWHeight))
399                 == (CWWidth | CWHeight)) &&
400           intended->width == preferred->width &&
401           intended->height == preferred->height)
402         return XtGeometryYes;
403     else if (preferred->width == w->core.width &&
404              preferred->height == w->core.height)
405         return XtGeometryNo;
406     else
407         return XtGeometryAlmost;
408 }
409
410
411
412 \f
413
414 /************************************************************
415  *
416  *  Action Procedures
417  *
418  ************************************************************/
419
420 /*
421  * Draw the highlight border around the widget.  The Command widget
422  * did this by drawing through a mask.  We do it by just drawing the
423  * border.
424  */
425
426 static void
427 DrawHighlight (Widget w,
428                GC gc)
429 {
430         RadioWidget     rw = (RadioWidget)w;
431         XRectangle      rects[4] ;
432         Dimension       ht = rw->command.highlight_thickness ;
433
434         if( ht <= 0 ||
435             ht > rw->core.width/2  ||
436             ht > rw->core.height/2 )
437           return ;
438
439         if( ! XtIsRealized(w) )
440           return ;
441
442         rects[0].x = 0 ; rects[0].y = 0 ;
443         rects[0].width = rw->core.width ; rects[0].height = ht ;
444         rects[1].x = 0 ; rects[1].y = rw->core.height - ht ;
445         rects[1].width = rw->core.width ; rects[1].height = ht ;
446         rects[2].x = 0 ; rects[2].y = ht ;
447         rects[2].width = ht ; rects[2].height = rw->core.height - ht*2 ;
448         rects[3].x = rw->core.width - ht ; rects[3].y = ht ;
449         rects[3].width = ht ; rects[3].height = rw->core.height - ht*2 ;
450         XFillRectangles( XtDisplay(w), XtWindow(w), gc, rects, 4) ;
451 }
452
453 static  void
454 RadioHighlight (Widget   w,
455                 XEvent   *event,
456                 String   *params,
457                 Cardinal *num_params)
458 {
459     RadioWidget rw = (RadioWidget)w;
460     DrawHighlight(w, rw->command.normal_GC) ;
461 }
462
463
464 static  void
465 RadioUnhighlight (Widget   w,
466                   XEvent   *event,
467                   String   *params,
468                   Cardinal *num_params)
469 {
470     RadioWidget rw = (RadioWidget)w;
471     DrawHighlight(w, rw->command.inverse_GC) ;
472 }
473
474
475 /* ARGSUSED */
476 void
477 RadioSet (Widget   w,
478           XEvent   *event,
479           String   *params,     /* unused */
480           Cardinal *num_params) /* unused */
481 {
482     RadioWidget rw = (RadioWidget)w;
483     RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class ;
484
485     if( rw->command.set )
486       return ;
487
488     rw->command.set = TRUE ;
489     if( XtIsRealized(w) )
490       class->radio_class.drawDiamond(w) ;
491 }
492
493
494 /* ARGSUSED */
495 void
496 RadioUnset (Widget   w,
497             XEvent   *event,
498             String   *params,     /* unused */
499             Cardinal *num_params) /* unused */
500 {
501     RadioWidget rw = (RadioWidget)w;
502     RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class ;
503
504     if( ! rw->command.set )
505       return ;
506
507     rw->command.set = FALSE ;
508     if( XtIsRealized(w) )
509       class->radio_class.drawDiamond(w) ;
510 }
511
512
513 \f
514
515 /************************************************************
516  *
517  *  Internal Procedures
518  *
519  ************************************************************/
520
521
522 /* Size of widget.  Width is size of box plus width of border around
523  * box plus width of label plus three margins plus the size of the left
524  * bitmap, if any.  Height is max(box,bitmap,label) plus two margins.
525  */
526
527 static  void
528 RadioSize (RadioWidget rw,
529            Dimension   *w,
530            Dimension   *h)
531 {
532         *w = rw->label.label_width + bs(rw) + LEFT_OFFSET(rw) +
533                 3 * rw->label.internal_width ;
534         *h = Max( rw->label.label_height, bs(rw) ) +
535                 2 * rw->label.internal_width ;
536 }
537
538
539 static  void
540 DrawDiamond (Widget w)
541 {
542         RadioWidget     rw = (RadioWidget) w ;
543         Display         *dpy = XtDisplay(w) ;
544         Window          win = XtWindow(w) ;
545         GC              gc, gci ;
546
547         XPoint          pts[5] ;
548         Dimension       del = bsize(rw)/2 ;
549         Position        x,y ;           /* diamond center */
550 #ifdef _ThreeDP_h
551         int i=0;
552         Dimension       s = swid(rw) ;
553         GC              top, bot, ctr ;
554 #endif
555         gc = XtIsSensitive(w) ? rw->command.normal_GC : rw->label.gray_GC ;
556
557         gci = rw->command.set ? rw->command.normal_GC : rw->command.inverse_GC ;
558
559         x = rw->label.internal_width + bs(rw)/2 ;
560         y = rw->core.height/2 ;
561
562 #ifdef  _ThreeDP_h
563         if( ! rw->command.set ) {
564           top = rw->threeD.top_shadow_GC ;
565           bot = rw->threeD.bot_shadow_GC ;
566           ctr = gc ;            /* TODO */
567         } else {
568           top = rw->threeD.bot_shadow_GC ;
569           bot = rw->threeD.top_shadow_GC ;
570           ctr = gc ;            /* TODO */
571         }
572 #endif
573
574         pts[0].x = x - del ;
575         pts[0].y = y ;
576         pts[1].x = x ;
577         pts[1].y = y - del ;
578         pts[2].x = x + del ;
579         pts[2].y = y ;
580         pts[3].x = x ;
581         pts[3].y = y + del ;
582         pts[4] = pts[0] ;
583         XFillPolygon(dpy,win,gci, pts,4, Convex, CoordModeOrigin) ;
584
585 #ifdef  _ThreeDP_h
586         for(i=0; i<s; ++i) {
587           XDrawLine(dpy,win,bot, x-i-del,y, x,y+del+i) ;
588           XDrawLine(dpy,win,bot, x+del+i,y, x,y+del+i) ;
589         }
590         for(i=0; i<s; ++i) {
591           XDrawLine(dpy,win,top, x-del-i,y, x,y-del-i) ;
592           XDrawLine(dpy,win,top, x+del+i,y, x,y-del-i) ;
593         }
594 #else
595         XDrawLines(dpy,win,gc, pts,5, CoordModeOrigin) ;
596 #endif
597 }