1 /* Radio Widget for SXEmacs.
2 Copyright (C) 1999 Edward A. Falk
4 This file is part of SXEmacs.
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.
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.
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/>. */
19 /* Synched up with: Radio.c 1.1 */
22 * Radio.c - Radio button widget
24 * Author: Edward A. Falk
25 * falk@falconer.vip.best.com
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.
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.
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
45 * We, in turn, override the Set() and Unset() actions in our own ClassRec.
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"
59 #define rclass(w) ((RadioWidgetClass)((w)->core.widget_class))
62 #define swid(rw) ((rw)->threeD.shadow_width)
64 #define swid(rw) ((rw)->core.border_width)
67 #define bsize(rw) (rclass(rw)->radio_class.dsize)
68 #define bs(rw) (bsize(rw) + 2*swid(rw))
70 /****************************************************************
72 * Full class record constant
74 ****************************************************************/
76 /* The translations table from Toggle do not need to be
80 /* Member functions */
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 *,
94 static void RadioHighlight(Widget, XEvent *, String *, Cardinal *);
95 static void RadioUnhighlight(Widget, XEvent *, String *, Cardinal *);
97 /* internal privates */
99 static void RadioSize(RadioWidget, Dimension *, Dimension *);
101 /* The actions table from Toggle is almost perfect, but we need
102 * to override Highlight, Set, and Unset.
105 static XtActionsRec actionsList[] = {
106 {"highlight", RadioHighlight},
107 {"unhighlight", RadioUnhighlight},
110 #define SuperClass ((ToggleWidgetClass)&toggleClassRec)
112 RadioClassRec radioClassRec = {
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 */
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 */
147 , /* CoreClass fields initialization */
149 XtInheritChangeSensitive /* change_sensitive */
151 , /* SimpleClass fields initialization */
154 XtInheritXaw3dShadowDraw /* field not used */
156 , /* ThreeDClass fields initialization */
159 0 /* field not used */
161 , /* LabelClass fields initialization */
163 0 /* field not used */
165 , /* CommandClass fields initialization */
167 RadioSet, /* Set Procedure. */
168 RadioUnset, /* Unset Procedure. */
169 NULL /* extension. */
171 , /* ToggleClass fields initialization */
174 DrawDiamond, /* draw procedure */
175 NULL /* extension. */
176 } /* RadioClass fields initialization */
179 /* for public consumption */
180 WidgetClass radioWidgetClass = (WidgetClass) & radioClassRec;
182 /****************************************************************
186 ****************************************************************/
188 static void RadioClassInit(void)
190 XawInitializeWidgetSet();
193 static void RadioClassPartInit(WidgetClass class)
195 RadioWidgetClass c = (RadioWidgetClass) class;
196 RadioWidgetClass super = (RadioWidgetClass) c->core_class.superclass;
198 if (c->radio_class.drawDiamond == NULL ||
199 c->radio_class.drawDiamond == XtInheritDrawDiamond) {
200 c->radio_class.drawDiamond = super->radio_class.drawDiamond;
206 RadioInit(Widget request, Widget new, ArgList args, Cardinal * num_args)
208 RadioWidget rw = (RadioWidget) new;
209 RadioWidget rw_req = (RadioWidget) request;
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)
217 if (rw_req->core.height == 0)
219 rw->core.widget_class->core_class.resize(new);
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.
228 static void RadioResize(Widget w)
230 RadioWidget rw = (RadioWidget) w;
232 /* call parent resize proc */
233 SuperClass->core_class.resize(w);
235 /* override label offset */
237 switch (rw->label.justify) {
239 rw->label.label_x += (bs(rw) + rw->label.internal_width);
243 case XtJustifyCenter:
245 rw->label.label_x += (bs(rw) + rw->label.internal_width) / 2;
251 * Repaint the widget window.
254 static void RadioExpose(Widget w, XEvent * event, Region region)
256 RadioWidget rw = (RadioWidget) w;
257 Display *dpy = XtDisplay(w);
258 Window win = XtWindow(w);
261 extern WidgetClass labelWidgetClass;
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.
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.
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;
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,
289 /* Finally, the button itself */
290 ((RadioWidgetClass) (w->core.widget_class))->radio_class.drawDiamond(w);
293 /************************************************************
295 * Set specified arguments into widget
297 ***********************************************************/
301 RadioSetValues(Widget current,
302 Widget request, Widget new, ArgList args, Cardinal * num_args)
304 RadioWidget oldrw = (RadioWidget) current;
305 RadioWidget newrw = (RadioWidget) new;
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.
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);
322 /* The label set values routine can resize the widget. We need to
323 * recalculate if this is true.
325 if (newrw->label.label_x != oldrw->label.label_x) {
331 static XtGeometryResult
332 RadioQueryGeometry(Widget w,
333 XtWidgetGeometry * intended, XtWidgetGeometry * preferred)
335 RadioWidget rw = (RadioWidget) w;
337 preferred->request_mode = CWWidth | CWHeight;
338 RadioSize(rw, &preferred->width, &preferred->height);
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)
349 return XtGeometryAlmost;
352 /************************************************************
356 ************************************************************/
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
364 static void DrawHighlight(Widget w, GC gc)
366 RadioWidget rw = (RadioWidget) w;
368 Dimension ht = rw->command.highlight_thickness;
370 if (ht <= 0 || ht > rw->core.width / 2 || ht > rw->core.height / 2)
373 if (!XtIsRealized(w))
378 rects[0].width = rw->core.width;
379 rects[0].height = ht;
381 rects[1].y = rw->core.height - ht;
382 rects[1].width = rw->core.width;
383 rects[1].height = ht;
387 rects[2].height = rw->core.height - ht * 2;
388 rects[3].x = rw->core.width - ht;
391 rects[3].height = rw->core.height - ht * 2;
392 XFillRectangles(XtDisplay(w), XtWindow(w), gc, rects, 4);
396 RadioHighlight(Widget w, XEvent * event, String * params, Cardinal * num_params)
398 RadioWidget rw = (RadioWidget) w;
399 DrawHighlight(w, rw->command.normal_GC);
403 RadioUnhighlight(Widget w,
404 XEvent * event, String * params, Cardinal * num_params)
406 RadioWidget rw = (RadioWidget) w;
407 DrawHighlight(w, rw->command.inverse_GC);
411 void RadioSet(Widget w, XEvent * event, String * params, /* unused */
412 Cardinal * num_params)
414 RadioWidget rw = (RadioWidget) w;
415 RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class;
420 rw->command.set = TRUE;
422 class->radio_class.drawDiamond(w);
426 void RadioUnset(Widget w, XEvent * event, String * params, /* unused */
427 Cardinal * num_params)
429 RadioWidget rw = (RadioWidget) w;
430 RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class;
432 if (!rw->command.set)
435 rw->command.set = FALSE;
437 class->radio_class.drawDiamond(w);
440 /************************************************************
442 * Internal Procedures
444 ************************************************************/
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.
451 static void RadioSize(RadioWidget rw, Dimension * w, Dimension * h)
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;
458 static void DrawDiamond(Widget w)
460 RadioWidget rw = (RadioWidget) w;
461 Display *dpy = XtDisplay(w);
462 Window win = XtWindow(w);
466 Dimension del = bsize(rw) / 2;
467 Position x, y; /* diamond center */
470 Dimension s = swid(rw);
473 gc = XtIsSensitive(w) ? rw->command.normal_GC : rw->label.gray_GC;
475 gci = rw->command.set ? rw->command.normal_GC : rw->command.inverse_GC;
477 x = rw->label.internal_width + bs(rw) / 2;
478 y = rw->core.height / 2;
481 if (!rw->command.set) {
482 top = rw->threeD.top_shadow_GC;
483 bot = rw->threeD.bot_shadow_GC;
486 top = rw->threeD.bot_shadow_GC;
487 bot = rw->threeD.top_shadow_GC;
501 XFillPolygon(dpy, win, gci, pts, 4, Convex, CoordModeOrigin);
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);
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);
513 XDrawLines(dpy, win, gc, pts, 5, CoordModeOrigin);