Toggle.c revision 5ec34c4c
1/*
2
3Copyright 1989, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27/*
28 * Author: Chris D. Peterson
29 *         MIT X Consortium
30 *         kit@expo.lcs.mit.edu
31 *
32 * Date:   January 12, 1989
33 *
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include <stdio.h>
40#include <X11/IntrinsicP.h>
41#include <X11/StringDefs.h>
42#include <X11/Xmu/Converters.h>
43#include <X11/Xmu/Misc.h>
44#include <X11/Xaw/ToggleP.h>
45#include <X11/Xaw/XawInit.h>
46
47/*
48 * Class Methods
49 */
50static void XawToggleClassInitialize(void);
51static void XawToggleInitialize(Widget, Widget, ArgList, Cardinal*);
52static Boolean XawToggleSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
53
54/*
55 * Prototypes
56 */
57static void AddToRadioGroup(RadioGroup*, Widget);
58static void CreateRadioGroup(Widget, Widget);
59static RadioGroup *GetRadioGroup(Widget);
60static void RemoveFromRadioGroup(Widget);
61static void TurnOffRadioSiblings(Widget);
62static void XawToggleDestroy(Widget, XtPointer, XtPointer);
63
64/*
65 * Actions
66 */
67static void Notify(Widget, XEvent*, String*, Cardinal*);
68static void Toggle(Widget, XEvent*, String*, Cardinal*);
69static void ToggleSet(Widget, XEvent*, String*, Cardinal*);
70
71/*
72 * Initialization
73 */
74/*
75 * The order of toggle and notify are important, as the state has
76 * to be set when we call the notify proc
77 */
78static char defaultTranslations[] =
79"<Enter>:"		"highlight(Always)\n"
80"<Leave>:"		"unhighlight()\n"
81"<Btn1Down>,<Btn1Up>:"	"toggle() notify()\n"
82;
83
84#define offset(field) XtOffsetOf(ToggleRec, field)
85static XtResource resources[] = {
86  {
87    XtNstate,
88    XtCState,
89    XtRBoolean,
90    sizeof(Boolean),
91    offset(command.set),
92    XtRString,
93    (XtPointer)"off"
94  },
95  {
96    XtNradioGroup,
97    XtCWidget,
98    XtRWidget,
99    sizeof(Widget),
100    offset(toggle.widget),
101    XtRWidget,
102    NULL
103  },
104  {
105    XtNradioData,
106    XtCRadioData,
107    XtRPointer,
108    sizeof(XtPointer),
109    offset(toggle.radio_data),
110    XtRPointer,
111    NULL
112  },
113};
114#undef offset
115
116static XtActionsRec actionsList[] = {
117  {"toggle",		Toggle},
118  {"notify",		Notify},
119  {"set",		ToggleSet},
120};
121
122#define Superclass	((CommandWidgetClass)&commandClassRec)
123ToggleClassRec toggleClassRec = {
124  /* core */
125  {
126    (WidgetClass)Superclass,		/* superclass		  */
127    "Toggle",				/* class_name		  */
128    sizeof(ToggleRec),			/* size			  */
129    XawToggleClassInitialize,		/* class_initialize	  */
130    NULL,				/* class_part_initialize  */
131    False,				/* class_inited		  */
132    XawToggleInitialize,		/* initialize		  */
133    NULL,				/* initialize_hook	  */
134    XtInheritRealize,			/* realize		  */
135    actionsList,			/* actions		  */
136    XtNumber(actionsList),		/* num_actions		  */
137    resources,				/* resources		  */
138    XtNumber(resources),		/* resource_count	  */
139    NULLQUARK,				/* xrm_class		  */
140    False,				/* compress_motion	  */
141    True,				/* compress_exposure	  */
142    True,				/* compress_enterleave	  */
143    False,				/* visible_interest	  */
144    NULL,				/* destroy		  */
145    XtInheritResize,			/* resize		  */
146    XtInheritExpose,			/* expose		  */
147    XawToggleSetValues,			/* set_values		  */
148    NULL,				/* set_values_hook	  */
149    XtInheritSetValuesAlmost,		/* set_values_almost	  */
150    NULL,				/* get_values_hook	  */
151    NULL,				/* accept_focus		  */
152    XtVersion,				/* version		  */
153    NULL,				/* callback_private	  */
154    defaultTranslations,		/* tm_table		  */
155    XtInheritQueryGeometry,		/* query_geometry	  */
156    XtInheritDisplayAccelerator,	/* display_accelerator	  */
157    NULL,				/* extension		  */
158  },
159  /* simple */
160  {
161    XtInheritChangeSensitive,		/* change_sensitive */
162  },
163  /* label */
164  {
165    NULL,				/* extension */
166  },
167  /* command */
168  {
169    NULL,				/* extension */
170  },
171  /* toggle */
172  {
173    NULL,				/* Set */
174    NULL,				/* Unset */
175    NULL,				/* extension */
176  }
177};
178
179WidgetClass toggleWidgetClass = (WidgetClass)&toggleClassRec;
180
181/*
182 * Impelementation
183 */
184static void
185XawToggleClassInitialize(void)
186{
187    XtActionList actions;
188    Cardinal num_actions;
189    Cardinal i;
190    ToggleWidgetClass cclass = (ToggleWidgetClass)toggleWidgetClass;
191    static XtConvertArgRec parentCvtArgs[] = {
192	{XtBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.parent),
193	 sizeof(Widget)}
194    };
195
196    XawInitializeWidgetSet();
197    XtSetTypeConverter(XtRString, XtRWidget, XmuNewCvtStringToWidget,
198		       parentCvtArgs, XtNumber(parentCvtArgs),
199		       XtCacheNone, NULL);
200    XtSetTypeConverter(XtRWidget, XtRString, XmuCvtWidgetToString,
201		       NULL, 0, XtCacheNone, NULL);
202
203    /*
204     * Find the set and unset actions in the command widget's action table
205     */
206    XtGetActionList(commandWidgetClass, &actions, &num_actions);
207
208    for (i = 0 ; i < num_actions ; i++) {
209	if (streq(actions[i].string, "set"))
210	    cclass->toggle_class.Set = actions[i].proc;
211	if (streq(actions[i].string, "unset"))
212	    cclass->toggle_class.Unset = actions[i].proc;
213
214	if (cclass->toggle_class.Set != NULL &&
215	    cclass->toggle_class.Unset != NULL)	{
216	    XtFree((char *)actions);
217	    return;
218	}
219    }
220
221    /* We should never get here */
222    XtError("Aborting, due to errors resolving bindings in the Toggle widget.");
223}
224
225/*ARGSUSED*/
226static void
227XawToggleInitialize(Widget request, Widget cnew,
228		    ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
229{
230    ToggleWidget tw = (ToggleWidget)cnew;
231    ToggleWidget tw_req = (ToggleWidget)request;
232
233    tw->toggle.radio_group = NULL;
234
235    if (tw->toggle.radio_data == NULL)
236	tw->toggle.radio_data = (XtPointer)cnew->core.name;
237
238    if (tw->toggle.widget != NULL) {
239	if (GetRadioGroup(tw->toggle.widget) == NULL)
240	    CreateRadioGroup(cnew, tw->toggle.widget);
241	else
242	    AddToRadioGroup(GetRadioGroup(tw->toggle.widget), cnew);
243    }
244    XtAddCallback(cnew, XtNdestroyCallback, XawToggleDestroy, NULL);
245
246    /*
247     * Command widget assumes that the widget is unset, so we only
248     * have to handle the case where it needs to be set
249     *
250     * If this widget is in a radio group then it may cause another
251     * widget to be unset, thus calling the notify proceedure
252     *
253     * I want to set the toggle if the user set the state to "On" in
254     * the resource group, reguardless of what my ancestors did
255     */
256    if (tw_req->command.set)
257	ToggleSet(cnew, NULL, NULL, NULL);
258}
259
260/*ARGSUSED*/
261static void
262ToggleSet(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
263{
264    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
265
266    TurnOffRadioSiblings(w);
267    cclass->toggle_class.Set(w, event, NULL, NULL);
268}
269
270static void
271Toggle(Widget w, XEvent *event, String *params, Cardinal *num_params)
272{
273    ToggleWidget tw = (ToggleWidget)w;
274    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
275
276    if (tw->command.set)
277	cclass->toggle_class.Unset(w, event, NULL, NULL);
278    else
279	ToggleSet(w, event, params, num_params);
280}
281
282/*ARGSUSED*/
283static void
284Notify(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
285{
286    ToggleWidget tw = (ToggleWidget)w;
287    long antilint = tw->command.set;
288
289    XtCallCallbacks(w, XtNcallback, (XtPointer)antilint);
290}
291
292/*ARGSUSED*/
293static Boolean
294XawToggleSetValues(Widget current, Widget request, Widget cnew,
295		   ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
296{
297    ToggleWidget oldtw = (ToggleWidget)current;
298    ToggleWidget tw = (ToggleWidget)cnew;
299    ToggleWidget rtw = (ToggleWidget)request;
300
301    if (oldtw->toggle.widget != tw->toggle.widget)
302	XawToggleChangeRadioGroup(cnew, tw->toggle.widget);
303
304    if (!tw->core.sensitive && oldtw->core.sensitive && rtw->command.set)
305	tw->command.set = True;
306
307    if (oldtw->command.set != tw->command.set) {
308	tw->command.set = oldtw->command.set;
309	Toggle(cnew, NULL, NULL, NULL);
310    }
311
312    return (False);
313}
314
315/*
316 * Function:
317 *	XawToggleDestroy
318 *
319 * Parameters:
320 *	w     - toggle widget that is being destroyed
321 *	temp1 - not used
322 *	temp2 - ""
323 *
324 * Description:
325 *	Destroy Callback for toggle widget.
326 */
327/*ARGSUSED*/
328static void
329XawToggleDestroy(Widget w, XtPointer temp1 _X_UNUSED, XtPointer temp2 _X_UNUSED)
330{
331    RemoveFromRadioGroup(w);
332}
333
334/*
335 * Function:
336 *	GetRadioGroup
337 *
338 * Parameters:
339 *	w - toggle widget who's radio group we are getting
340 *
341 * Description:
342 *	Gets the radio group associated with a give toggle widget.
343 *
344 * Returns:
345 *	The radio group associated with this toggle group
346 */
347static RadioGroup *
348GetRadioGroup(Widget w)
349{
350    ToggleWidget tw = (ToggleWidget)w;
351
352    if (tw == NULL)
353	return (NULL);
354
355    return (tw->toggle.radio_group);
356}
357
358/*
359 * Function:
360 *	CreateRadioGroup
361 *
362 * Parameters:
363 *	w1 - toggle widgets to add to the radio group
364 *	w2 - ""
365 *
366 * Description:
367 *	Creates a radio group. give two widgets.
368 *
369 * Note:
370 *	A pointer to the group is added to each widget's radio_group field.
371 */
372static void
373CreateRadioGroup(Widget w1, Widget w2)
374{
375    ToggleWidget tw1 = (ToggleWidget)w1;
376    ToggleWidget tw2 = (ToggleWidget) w2;
377
378    if (tw1->toggle.radio_group != NULL || tw2->toggle.radio_group != NULL)
379	XtAppWarning(XtWidgetToApplicationContext(w1),
380		     "Toggle Widget Error - Attempting to create a "
381		     "new toggle group, when one already exists.");
382
383    AddToRadioGroup(NULL, w1);
384    AddToRadioGroup(GetRadioGroup(w1), w2);
385}
386
387/*
388 * Function:
389 *	AddToRadioGroup
390 *
391 * Parameters:
392 *	group - element of the radio group the we are adding to
393 *	w     - new toggle widget to add to the group
394 *
395 * Description:
396 *	Adds a toggle to the radio group.
397 */
398static void
399AddToRadioGroup(RadioGroup *group, Widget w)
400{
401    ToggleWidget tw = (ToggleWidget)w;
402    RadioGroup *local;
403
404    local = (RadioGroup *)XtMalloc(sizeof(RadioGroup));
405    local->widget = w;
406    tw->toggle.radio_group = local;
407
408    if (group == NULL) {		  /* Creating new group */
409	group = local;
410	group->next = NULL;
411	group->prev = NULL;
412	return;
413    }
414    local->prev = group;	  /* Adding to previous group */
415    if ((local->next = group->next) != NULL)
416	local->next->prev = local;
417    group->next = local;
418}
419
420/*
421 * Function:
422 *	TurnOffRadioSiblings
423 *
424 * Parameters:
425 *	widget - toggle widget
426 *
427 * Description:
428 *	Deactivates all radio siblings.
429 */
430static void
431TurnOffRadioSiblings(Widget w)
432{
433    RadioGroup *group;
434    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
435
436    if ((group = GetRadioGroup(w)) == NULL)	/* Punt if there is no group */
437	return;
438
439    /* Go to the top of the group */
440    for (; group->prev != NULL ; group = group->prev)
441	;
442
443    while (group != NULL) {
444	ToggleWidget local_tog = (ToggleWidget)group->widget;
445
446	if (local_tog->command.set) {
447	    cclass->toggle_class.Unset(group->widget, NULL, NULL, NULL);
448	    Notify(group->widget, NULL, NULL, NULL);
449	}
450	group = group->next;
451    }
452}
453
454/*
455 * Function:
456 *	RemoveFromRadioGroup
457 *
458 * Parameters:
459 *	w - toggle widget to remove
460 *
461 * Description:
462 *	Removes a toggle from a RadioGroup.
463 */
464static void
465RemoveFromRadioGroup(Widget w)
466{
467    RadioGroup *group = GetRadioGroup(w);
468    if (group != NULL) {
469	if (group->prev != NULL)
470	    (group->prev)->next = group->next;
471	if (group->next != NULL)
472	    (group->next)->prev = group->prev;
473	XtFree((char *)group);
474    }
475}
476
477/*
478 * Function:
479 *	XawToggleChangeRadioGroup
480 *
481 * Parameters:
482 *	w	    - toggle widget to change groups
483 *	radio_group - any widget in the new group
484 *
485 * Description:
486 *	Allows a toggle widget to change radio groups.
487 */
488void
489XawToggleChangeRadioGroup(Widget w, Widget radio_group)
490{
491    ToggleWidget tw = (ToggleWidget)w;
492    RadioGroup *group;
493
494    RemoveFromRadioGroup(w);
495
496    /*
497     * If the toggle that we are about to add is set then we will
498     * unset all toggles in the new radio group
499     */
500
501    if (tw->command.set && radio_group != NULL)
502	XawToggleUnsetCurrent(radio_group);
503
504    if (radio_group != NULL) {
505	if ((group = GetRadioGroup(radio_group)) == NULL)
506	    CreateRadioGroup(w, radio_group);
507	else
508	    AddToRadioGroup(group, w);
509    }
510}
511
512/*
513 * Function:
514 *	XawToggleGetCurrent
515 *
516 * Parameters:
517 *	w - any toggle widget in the toggle group
518 *
519 * Description:
520 *	  Returns the RadioData associated with the toggle
521 *	widget that is currently active in a toggle group.
522 *
523 * Returns:
524 *	The XtNradioData associated with the toggle widget
525 */
526XtPointer
527XawToggleGetCurrent(Widget w)
528{
529    RadioGroup *group;
530
531    if ((group = GetRadioGroup(w)) == NULL)
532	return (NULL);
533
534    for (; group->prev != NULL ; group = group->prev)
535	;
536
537    while (group != NULL) {
538	ToggleWidget local_tog = (ToggleWidget)group->widget;
539
540	if (local_tog->command.set)
541	    return (local_tog->toggle.radio_data);
542	group = group->next;
543    }
544
545    return (NULL);
546}
547
548/*
549 * Function:
550 *	XawToggleSetCurrent
551 *
552 * Parameters:
553 *	radio_group - any toggle widget in the toggle group
554 *	radio_data  - radio data of the toggle widget to set
555 *
556 * Description:
557 *	Sets the Toggle widget associated with the radio_data specified.
558 */
559void
560XawToggleSetCurrent(Widget radio_group, XtPointer radio_data)
561{
562    RadioGroup *group;
563    ToggleWidget local_tog;
564
565    /* Special case of no radio group */
566
567    if ((group = GetRadioGroup(radio_group)) == NULL) {
568	local_tog = (ToggleWidget)radio_group;
569
570	if (local_tog->toggle.radio_data == radio_data &&
571	    !local_tog->command.set) {
572	    ToggleSet(radio_group, NULL, NULL, NULL);
573	    Notify(radio_group, NULL, NULL, NULL);
574	}
575	return;
576    }
577
578    /*
579     * find top of radio_roup
580     */
581    for (; group->prev != NULL ; group = group->prev)
582	;
583
584    /*
585     * search for matching radio data
586     */
587    while (group != NULL) {
588	local_tog = (ToggleWidget)group->widget;
589
590	if (local_tog->toggle.radio_data == radio_data) {
591	    if (!local_tog->command.set) {	/* if not already set */
592		ToggleSet(group->widget, NULL, NULL, NULL);
593		Notify(group->widget, NULL, NULL, NULL);
594	    }
595	    return;			/* found it, done */
596	}
597	group = group->next;
598    }
599}
600
601/*
602 * Function:
603 *	XawToggleUnsetCurrent
604 *
605 * Parameters:
606 *	radio_group - any toggle widget in the toggle group
607 *
608 * Description:
609 *	Unsets all Toggles in the radio_group specified.
610 */
611void
612XawToggleUnsetCurrent(Widget radio_group)
613{
614    ToggleWidgetClass cclass;
615    ToggleWidget local_tog = (ToggleWidget)radio_group;
616
617    /* Special Case no radio group */
618
619    if (local_tog->command.set) {
620	cclass = (ToggleWidgetClass)local_tog->core.widget_class;
621	cclass->toggle_class.Unset(radio_group, NULL, NULL, NULL);
622	Notify(radio_group, NULL, NULL, NULL);
623    }
624    if (GetRadioGroup(radio_group) == NULL)
625	return;
626
627    TurnOffRadioSiblings(radio_group);
628}
629