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