update.
[chise/xemacs-chise.git.1] / 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         /* Let label widget draw the label.  If there was an lbm_x
309          * field, we could let Label draw the bitmap too.  But there
310          * isn't, so we need to temporarily remove the bitmap and
311          * draw it ourself later.
312          */
313         left_bitmap = rw->label.left_bitmap ;
314         rw->label.left_bitmap = None ;
315         labelWidgetClass->core_class.expose(w,event,region) ;
316         rw->label.left_bitmap = left_bitmap ;
317
318         /* now manually draw the left bitmap.  TODO: 3-d look, xaw-xpm */
319         gc = XtIsSensitive(w) ? rw->label.normal_GC : rw->label.gray_GC ;
320         if( left_bitmap != None && rw->label.lbm_width > 0 )
321         {
322           /* TODO: handle pixmaps */
323           XCopyPlane(dpy, left_bitmap, win, gc,
324                      0,0, rw->label.lbm_width, rw->label.lbm_height,
325                      (int) rw->label.internal_width*2 + bs(rw),
326                      (int) rw->label.internal_height + rw->label.lbm_y,
327                      1UL) ;
328         }
329
330         /* Finally, the button itself */
331         ((RadioWidgetClass)(w->core.widget_class))->radio_class.drawDiamond(w) ;
332 }
333
334
335
336
337 /************************************************************
338  *
339  * Set specified arguments into widget
340  *
341  ***********************************************************/
342
343
344 /* ARGSUSED */
345 static Boolean
346 RadioSetValues (Widget   current,
347                 Widget   request,
348                 Widget   new,
349                 ArgList  args,
350                 Cardinal *num_args)
351 {
352     RadioWidget oldrw = (RadioWidget) current;
353     RadioWidget newrw = (RadioWidget) new;
354
355     /* Need to find out if the size of the widget changed.  Set new size
356      * if it did and resize is permitted.  One way to determine of the
357      * widget changed size would be to scan the args list.  Another way
358      * is to compare the old and new widgets and see if any of several
359      * size-related fields have been changed.  The Label widget chose the
360      * former method, but I choose the latter.
361      */
362
363     if( newrw->label.resize &&
364         ( newrw->core.width != oldrw->core.width ||
365           newrw->core.height != oldrw->core.height ||
366           newrw->core.border_width != oldrw->core.border_width ) )
367     {
368       RadioSize(newrw, &newrw->core.width, &newrw->core.height) ;
369     }
370
371     /* The label set values routine can resize the widget. We need to
372      * recalculate if this is true.
373      */
374     if (newrw->label.label_x != oldrw->label.label_x)
375     {
376       RadioResize (new);
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 \f
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 \f
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 }