Toggle.c revision efbcb2bf
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#ifndef OLDXAW
163    NULL,
164#endif
165  },
166  /* label */
167  {
168    NULL,				/* extension */
169  },
170  /* command */
171  {
172    NULL,				/* extension */
173  },
174  /* toggle */
175  {
176    NULL,				/* Set */
177    NULL,				/* Unset */
178    NULL,				/* extension */
179  }
180};
181
182WidgetClass toggleWidgetClass = (WidgetClass)&toggleClassRec;
183
184/*
185 * Implementation
186 */
187static void
188XawToggleClassInitialize(void)
189{
190    XtActionList actions;
191    Cardinal num_actions;
192    Cardinal i;
193    ToggleWidgetClass cclass = (ToggleWidgetClass)toggleWidgetClass;
194    static XtConvertArgRec parentCvtArgs[] = {
195	{XtBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.parent),
196	 sizeof(Widget)}
197    };
198
199    XawInitializeWidgetSet();
200    XtSetTypeConverter(XtRString, XtRWidget, XmuNewCvtStringToWidget,
201		       parentCvtArgs, XtNumber(parentCvtArgs),
202		       XtCacheNone, NULL);
203    XtSetTypeConverter(XtRWidget, XtRString, XmuCvtWidgetToString,
204		       NULL, 0, XtCacheNone, NULL);
205
206    /*
207     * Find the set and unset actions in the command widget's action table
208     */
209    XtGetActionList(commandWidgetClass, &actions, &num_actions);
210
211    for (i = 0 ; i < num_actions ; i++) {
212	if (streq(actions[i].string, "set"))
213	    cclass->toggle_class.Set = actions[i].proc;
214	if (streq(actions[i].string, "unset"))
215	    cclass->toggle_class.Unset = actions[i].proc;
216
217	if (cclass->toggle_class.Set != NULL &&
218	    cclass->toggle_class.Unset != NULL)	{
219	    XtFree((char *)actions);
220	    return;
221	}
222    }
223
224    /* We should never get here */
225    XtError("Aborting, due to errors resolving bindings in the Toggle widget.");
226}
227
228/*ARGSUSED*/
229static void
230XawToggleInitialize(Widget request, Widget cnew,
231		    ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
232{
233    ToggleWidget tw = (ToggleWidget)cnew;
234    ToggleWidget tw_req = (ToggleWidget)request;
235
236    tw->toggle.radio_group = NULL;
237
238    if (tw->toggle.radio_data == NULL)
239	tw->toggle.radio_data = (XtPointer)cnew->core.name;
240
241    if (tw->toggle.widget != NULL) {
242	if (GetRadioGroup(tw->toggle.widget) == NULL)
243	    CreateRadioGroup(cnew, tw->toggle.widget);
244	else
245	    AddToRadioGroup(GetRadioGroup(tw->toggle.widget), cnew);
246    }
247    XtAddCallback(cnew, XtNdestroyCallback, XawToggleDestroy, NULL);
248
249    /*
250     * Command widget assumes that the widget is unset, so we only
251     * have to handle the case where it needs to be set
252     *
253     * If this widget is in a radio group then it may cause another
254     * widget to be unset, thus calling the notify procedure
255     *
256     * I want to set the toggle if the user set the state to "On" in
257     * the resource group, regardless of what my ancestors did
258     */
259    if (tw_req->command.set)
260	ToggleSet(cnew, NULL, NULL, NULL);
261}
262
263/*ARGSUSED*/
264static void
265ToggleSet(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
266{
267    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
268
269    TurnOffRadioSiblings(w);
270    cclass->toggle_class.Set(w, event, NULL, NULL);
271}
272
273static void
274Toggle(Widget w, XEvent *event, String *params, Cardinal *num_params)
275{
276    ToggleWidget tw = (ToggleWidget)w;
277    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
278
279    if (tw->command.set)
280	cclass->toggle_class.Unset(w, event, NULL, NULL);
281    else
282	ToggleSet(w, event, params, num_params);
283}
284
285/*ARGSUSED*/
286static void
287Notify(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
288{
289    ToggleWidget tw = (ToggleWidget)w;
290    long antilint = tw->command.set;
291
292    XtCallCallbacks(w, XtNcallback, (XtPointer)antilint);
293}
294
295/*ARGSUSED*/
296static Boolean
297XawToggleSetValues(Widget current, Widget request, Widget cnew,
298		   ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
299{
300    ToggleWidget oldtw = (ToggleWidget)current;
301    ToggleWidget tw = (ToggleWidget)cnew;
302    ToggleWidget rtw = (ToggleWidget)request;
303
304    if (oldtw->toggle.widget != tw->toggle.widget)
305	XawToggleChangeRadioGroup(cnew, tw->toggle.widget);
306
307    if (!tw->core.sensitive && oldtw->core.sensitive && rtw->command.set)
308	tw->command.set = True;
309
310    if (oldtw->command.set != tw->command.set) {
311	tw->command.set = oldtw->command.set;
312	Toggle(cnew, NULL, NULL, NULL);
313    }
314
315    return (False);
316}
317
318/*
319 * Function:
320 *	XawToggleDestroy
321 *
322 * Parameters:
323 *	w     - toggle widget that is being destroyed
324 *	temp1 - not used
325 *	temp2 - ""
326 *
327 * Description:
328 *	Destroy Callback for toggle widget.
329 */
330/*ARGSUSED*/
331static void
332XawToggleDestroy(Widget w, XtPointer temp1 _X_UNUSED, XtPointer temp2 _X_UNUSED)
333{
334    RemoveFromRadioGroup(w);
335}
336
337/*
338 * Function:
339 *	GetRadioGroup
340 *
341 * Parameters:
342 *	w - toggle widget who's radio group we are getting
343 *
344 * Description:
345 *	Gets the radio group associated with a give toggle widget.
346 *
347 * Returns:
348 *	The radio group associated with this toggle group
349 */
350static RadioGroup *
351GetRadioGroup(Widget w)
352{
353    ToggleWidget tw = (ToggleWidget)w;
354
355    if (tw == NULL)
356	return (NULL);
357
358    return (tw->toggle.radio_group);
359}
360
361/*
362 * Function:
363 *	CreateRadioGroup
364 *
365 * Parameters:
366 *	w1 - toggle widgets to add to the radio group
367 *	w2 - ""
368 *
369 * Description:
370 *	Creates a radio group. give two widgets.
371 *
372 * Note:
373 *	A pointer to the group is added to each widget's radio_group field.
374 */
375static void
376CreateRadioGroup(Widget w1, Widget w2)
377{
378    ToggleWidget tw1 = (ToggleWidget)w1;
379    ToggleWidget tw2 = (ToggleWidget) w2;
380
381    if (tw1->toggle.radio_group != NULL || tw2->toggle.radio_group != NULL)
382	XtAppWarning(XtWidgetToApplicationContext(w1),
383		     "Toggle Widget Error - Attempting to create a "
384		     "new toggle group, when one already exists.");
385
386    AddToRadioGroup(NULL, w1);
387    AddToRadioGroup(GetRadioGroup(w1), w2);
388}
389
390/*
391 * Function:
392 *	AddToRadioGroup
393 *
394 * Parameters:
395 *	group - element of the radio group the we are adding to
396 *	w     - new toggle widget to add to the group
397 *
398 * Description:
399 *	Adds a toggle to the radio group.
400 */
401static void
402AddToRadioGroup(RadioGroup *group, Widget w)
403{
404    ToggleWidget tw = (ToggleWidget)w;
405    RadioGroup *local;
406
407    local = (RadioGroup *)XtMalloc(sizeof(RadioGroup));
408    local->widget = w;
409    tw->toggle.radio_group = local;
410
411    if (group == NULL) {		  /* Creating new group */
412	group = local;
413	group->next = NULL;
414	group->prev = NULL;
415	return;
416    }
417    local->prev = group;	  /* Adding to previous group */
418    if ((local->next = group->next) != NULL)
419	local->next->prev = local;
420    group->next = local;
421}
422
423/*
424 * Function:
425 *	TurnOffRadioSiblings
426 *
427 * Parameters:
428 *	widget - toggle widget
429 *
430 * Description:
431 *	Deactivates all radio siblings.
432 */
433static void
434TurnOffRadioSiblings(Widget w)
435{
436    RadioGroup *group;
437    ToggleWidgetClass cclass = (ToggleWidgetClass)w->core.widget_class;
438
439    if ((group = GetRadioGroup(w)) == NULL)	/* Punt if there is no group */
440	return;
441
442    /* Go to the top of the group */
443    for (; group->prev != NULL ; group = group->prev)
444	;
445
446    while (group != NULL) {
447	ToggleWidget local_tog = (ToggleWidget)group->widget;
448
449	if (local_tog->command.set) {
450	    cclass->toggle_class.Unset(group->widget, NULL, NULL, NULL);
451	    Notify(group->widget, NULL, NULL, NULL);
452	}
453	group = group->next;
454    }
455}
456
457/*
458 * Function:
459 *	RemoveFromRadioGroup
460 *
461 * Parameters:
462 *	w - toggle widget to remove
463 *
464 * Description:
465 *	Removes a toggle from a RadioGroup.
466 */
467static void
468RemoveFromRadioGroup(Widget w)
469{
470    RadioGroup *group = GetRadioGroup(w);
471    if (group != NULL) {
472	if (group->prev != NULL)
473	    (group->prev)->next = group->next;
474	if (group->next != NULL)
475	    (group->next)->prev = group->prev;
476	XtFree((char *)group);
477    }
478}
479
480/*
481 * Function:
482 *	XawToggleChangeRadioGroup
483 *
484 * Parameters:
485 *	w	    - toggle widget to change groups
486 *	radio_group - any widget in the new group
487 *
488 * Description:
489 *	Allows a toggle widget to change radio groups.
490 */
491void
492XawToggleChangeRadioGroup(Widget w, Widget radio_group)
493{
494    ToggleWidget tw = (ToggleWidget)w;
495
496    RemoveFromRadioGroup(w);
497
498    /*
499     * If the toggle that we are about to add is set then we will
500     * unset all toggles in the new radio group
501     */
502
503    if (tw->command.set && radio_group != NULL)
504	XawToggleUnsetCurrent(radio_group);
505
506    if (radio_group != NULL) {
507	RadioGroup *group = GetRadioGroup(radio_group);
508
509	if (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    ToggleWidget local_tog = (ToggleWidget)radio_group;
619
620    /* Special Case no radio group */
621
622    if (local_tog->command.set) {
623	ToggleWidgetClass cclass;
624
625	cclass = (ToggleWidgetClass)local_tog->core.widget_class;
626	cclass->toggle_class.Unset(radio_group, NULL, NULL, NULL);
627	Notify(radio_group, NULL, NULL, NULL);
628    }
629    if (GetRadioGroup(radio_group) == NULL)
630	return;
631
632    TurnOffRadioSiblings(radio_group);
633}
634