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