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