1 /* The lwlib interface to Motif widgets.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 This file is part of the Lucid Widget Library.
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
31 #include <X11/StringDefs.h>
32 #include <X11/IntrinsicP.h>
33 #include <X11/ObjectP.h>
34 #include <X11/CoreP.h>
35 #include <X11/CompositeP.h>
38 #include "lwlib-utils.h"
41 #include <Xm/BulletinB.h>
42 #include <Xm/CascadeB.h>
43 #include <Xm/DrawingA.h>
44 #include <Xm/FileSB.h>
47 #include <Xm/MenuShell.h>
48 #include <Xm/MessageB.h>
50 #include <Xm/PushBG.h>
51 #include <Xm/ArrowB.h>
52 #include <Xm/ScrollBar.h>
53 #include <Xm/SelectioB.h>
56 #include <Xm/ToggleB.h>
57 #include <Xm/ToggleBG.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/ScrolledW.h>
60 #include <Xm/Separator.h>
61 #include <Xm/DialogS.h>
63 #ifdef LWLIB_WIDGETS_MOTIF
66 #include <Xm/ComboBoxP.h>
70 #ifdef LWLIB_MENUBARS_MOTIF
71 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
73 static void xm_internal_update_other_instances (Widget, XtPointer,
75 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
76 static void xm_generic_callback (Widget, XtPointer, XtPointer);
77 static void mark_dead_instance_destroyed (Widget widget, XtPointer closure,
79 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
80 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
82 #ifdef LWLIB_SCROLLBARS_MOTIF
83 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
86 #ifdef LWLIB_MENUBARS_MOTIF
88 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
92 \f/* Structures to keep destroyed instances */
93 typedef struct _destroyed_instance
100 struct _destroyed_instance* next;
101 } destroyed_instance;
103 static destroyed_instance*
104 all_destroyed_instances = NULL;
106 /* Utility function. */
108 safe_strdup (char* s)
112 result = (char *) malloc (strlen (s) + 1);
119 static destroyed_instance*
120 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
123 destroyed_instance* instance =
124 (destroyed_instance*) malloc (sizeof (destroyed_instance));
125 instance->name = safe_strdup (name);
126 instance->type = safe_strdup (type);
127 instance->widget = widget;
128 instance->parent = parent;
129 instance->pop_up_p = pop_up_p;
130 instance->next = NULL;
135 free_destroyed_instance (destroyed_instance* instance)
137 free (instance->name);
138 free (instance->type);
142 \f/* motif utility functions */
144 first_child (Widget widget)
146 return ((CompositeWidget)widget)->composite.children [0];
150 lw_motif_widget_p (Widget widget)
153 #ifdef LWLIB_DIALOGS_MOTIF
154 XtClass (widget) == xmDialogShellWidgetClass ||
156 XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
160 resource_string (Widget widget, char *name)
165 resource.resource_name = "labelString";
166 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
167 resource.resource_type = XtRString;
168 resource.resource_size = sizeof (String);
169 resource.resource_offset = 0;
170 resource.default_type = XtRImmediate;
171 resource.default_addr = 0;
173 XtGetSubresources (widget, (XtPointer)&result, name,
174 name, &resource, 1, NULL, 0);
180 #ifdef LWLIB_DIALOGS_MOTIF
183 is_in_dialog_box (Widget w)
187 wmshell = XtParent (w);
188 while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
189 wmshell = XtParent (wmshell);
191 if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
197 #endif /* LWLIB_DIALOGS_MOTIF */
199 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
201 /* update the label of anything subclass of a label */
203 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
205 XmString built_string = NULL;
206 XmString key_string = NULL;
207 XmString val_string = NULL;
208 XmString name_string = NULL;
213 /* Don't clobber pixmap types. */
214 XtSetArg (al [0], XmNlabelType, &type);
215 XtGetValues (widget, al, 1);
217 if (type == XmPIXMAP)
222 /* #### Temporary fix. I though Motif was supposed to grok %_
224 lw_remove_accelerator_spec (val->value);
226 #ifdef LWLIB_DIALOGS_MOTIF
228 * Sigh. The main text of a label is the name field for menubar
229 * entries. The value field is a possible additional field to be
230 * concatenated on to the name field. HOWEVER, with dialog boxes
231 * the value field is the complete text which is supposed to be
232 * displayed as the label. Yuck.
234 if (is_in_dialog_box (widget))
236 char *value_name = NULL;
238 value_name = resource_string (widget, val->value);
240 value_name = val->value;
243 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
246 #endif /* LWLIB_DIALOGS_MOTIF */
248 char *value_name = NULL;
249 char *res_name = NULL;
251 res_name = resource_string (widget, val->name);
252 /* Concatenating the value with itself seems just plain daft. */
256 XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
261 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
263 value_name = XtMalloc (strlen (val->value) + 2);
265 strcat (value_name, " ");
266 strcat (value_name, val->value);
269 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
272 XmStringConcat (name_string, val_string);
278 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
279 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
284 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
285 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
289 XtSetValues (widget, al, ac);
292 XmStringFree (built_string);
295 XmStringFree (key_string);
298 XmStringFree (name_string);
301 XmStringFree (val_string);
304 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
306 \f/* update of list */
308 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
312 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
313 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
315 for (cur = val->contents, i = 0; cur; cur = cur->next)
318 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
320 XmListAddItem (widget, xmstr, 0);
322 XmListSelectPos (widget, i, False);
323 XmStringFree (xmstr);
327 \f/* update of buttons */
329 xm_update_pushbutton (widget_instance* instance, Widget widget,
333 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
334 XtSetValues (widget, al, 1);
335 XtRemoveAllCallbacks (widget, XmNactivateCallback);
336 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
339 #ifdef LWLIB_MENUBARS_MOTIF
342 xm_update_cascadebutton (widget_instance* instance, Widget widget,
345 /* Should also rebuild the menu by calling ...update_menu... */
347 && val->type == CASCADE_TYPE
349 && val->contents->type == INCREMENTAL_TYPE)
351 /* okay, we're now doing a lisp callback to incrementally generate
353 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
354 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
356 XtCallCallbacks ((Widget)widget,
357 XmNcascadingCallback,
358 (XtPointer)val->contents);
361 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
362 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
367 #endif /* LWLIB_MENUBARS_MOTIF */
369 \f/* update toggle and radiobox */
371 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
374 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
375 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
377 XtSetArg (al [0], XmNset, val->selected);
378 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
379 XtSetValues (widget, al, 1);
383 xm_update_radiobox (widget_instance* instance, Widget widget,
389 /* update the callback */
390 XtRemoveAllCallbacks (widget, XmNentryCallback);
391 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
393 /* first update all the toggles */
394 /* Energize kernel interface is currently bad. It sets the selected widget
395 with the selected flag but returns it by its name. So we currently
396 have to support both setting the selection with the selected slot
397 of val contents and setting it with the "value" slot of val. The latter
398 has a higher priority. This to be removed when the kernel is fixed. */
399 for (cur = val->contents; cur; cur = cur->next)
401 toggle = XtNameToWidget (widget, cur->value);
405 XtSetArg (al [0], XmNsensitive, cur->enabled);
406 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
407 XtSetValues (toggle, al, 2);
411 /* The selected was specified by the value slot */
414 toggle = XtNameToWidget (widget, val->value);
418 XtSetArg (al [0], XmNset, True);
419 XtSetValues (toggle, al, 1);
424 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
425 /* update of combo box */
427 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
431 XtRemoveAllCallbacks (widget, XmNselectionCallback);
432 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
434 for (cur = val->contents, i = 0; cur; cur = cur->next)
437 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
439 XmListAddItem (CB_List (widget), xmstr, 0);
441 XmListSelectPos (CB_List (widget), i, False);
442 XmStringFree (xmstr);
447 #ifdef LWLIB_MENUBARS_MOTIF
449 \f/* update a popup menu, pulldown menu or a menubar */
451 make_menu_in_widget (widget_instance* instance, Widget widget,
454 Widget* children = 0;
462 Boolean menubar_p = False;
464 /* Allocate the children array */
465 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
466 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
468 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
469 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
470 XtGetValues (widget, al, 1);
472 /* add the unmap callback for popups and pulldowns */
473 /*** this sounds bogus ***/
474 /* probably because it is -- cet */
477 XtAddCallback (XtParent (widget), XmNpopdownCallback,
478 xm_pop_down_callback, (XtPointer)instance);
482 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
486 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
487 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
488 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
493 /* A pushright marker which is not needed for the real Motif
500 /* #### - xlwmenu.h supports several types that motif does
501 not. Also, motif supports pixmaps w/ type NO_LINE and
502 lwlib provides no way to access that functionality. --Stig */
503 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
505 button = XmCreateSeparator (widget, "separator", al, ac);
508 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
509 make_menu_in_widget (instance, menu, cur->contents);
510 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
511 button = XmCreateCascadeButton (widget, cur->name, al, ac);
513 xm_update_label (instance, button, cur);
515 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
516 (XtPointer)instance);
520 button = XmCreateCascadeButton (widget, cur->name, al, ac);
521 else if (!cur->call_data)
522 button = XmCreateLabel (widget, cur->name, al, ac);
523 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
525 XtSetArg (al [ac], XmNindicatorType,
526 (cur->type == TOGGLE_TYPE ?
527 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
528 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
529 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
532 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
534 xm_update_label (instance, button, cur);
536 /* don't add a callback to a simple label */
537 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
538 xm_update_toggle (instance, button, cur);
539 else if (cur->call_data)
540 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
541 (XtPointer)instance);
542 } /* switch (cur->type) */
545 children [num_children++] = button;
548 /* Last entry is the help button. This used be done after managing
549 the buttons. The comment claimed that it had to be done this way
550 otherwise the menubar ended up only 4 pixels high. That must
551 have been in the Old World. In the New World it stays the proper
552 height if you don't manage them until after you set this and as a
553 bonus the Help menu ends up where it is supposed to. */
557 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
558 XtSetValues (widget, al, ac);
562 XtManageChildren (children, num_children);
564 XtFree ((char *) children);
568 update_one_menu_entry (widget_instance* instance, Widget widget,
569 widget_value* val, Boolean deep_p)
574 widget_value* contents;
576 if (val->change == NO_CHANGE)
579 /* update the sensitivity and userdata */
580 /* Common to all widget types */
581 XtSetArg (al [0], XmNsensitive, val->enabled);
582 XtSetArg (al [1], XmNuserData, val->call_data);
583 XtSetValues (widget, al, 2);
585 /* update the menu button as a label. */
586 if (val->change >= VISIBLE_CHANGE)
588 xm_update_label (instance, widget, val);
589 if (XtClass (widget) == xmToggleButtonWidgetClass
590 || XtClass (widget) == xmToggleButtonGadgetClass)
592 xm_update_toggle (instance, widget, val);
597 /* update the pulldown/pullaside as needed */
599 XtSetArg (al [0], XmNsubMenuId, &menu);
600 XtGetValues (widget, al, 1);
602 contents = val->contents;
608 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
609 make_menu_in_widget (instance, menu, contents);
611 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
612 XtSetValues (widget, al, ac);
618 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
619 XtSetValues (widget, al, ac);
620 XtDestroyWidget (menu);
622 else if (deep_p && contents->change != NO_CHANGE)
623 xm_update_menu (instance, menu, val, 1);
627 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
630 /* Widget is a RowColumn widget whose contents have to be updated
631 * to reflect the list of items in val->contents */
632 if (val->contents->change == STRUCTURAL_CHANGE)
634 destroy_all_children (widget);
635 make_menu_in_widget (instance, widget, val->contents);
639 /* Update all the buttons of the RowColumn in order. */
641 unsigned int num_children;
643 widget_value *cur = 0;
645 children = XtCompositeChildren (widget, &num_children);
648 for (i = 0, cur = val->contents; i < num_children; i++)
652 /* skip if this is a pushright marker or a separator */
653 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
657 /* #### - this could puke if you have a separator as the
658 last item on a pullright menu. */
666 if (children [i]->core.being_destroyed
667 || strcmp (XtName (children [i]), cur->name))
669 update_one_menu_entry (instance, children [i], cur, deep_p);
672 XtFree ((char *) children);
679 #endif /* LWLIB_MENUBARS_MOTIF */
682 /* update text widgets */
685 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
687 XmTextSetString (widget, val->value ? val->value : (char *) "");
688 XtRemoveAllCallbacks (widget, XmNactivateCallback);
689 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
690 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
691 XtAddCallback (widget, XmNvalueChangedCallback,
692 xm_internal_update_other_instances, instance);
696 xm_update_text_field (widget_instance* instance, Widget widget,
699 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
700 XtRemoveAllCallbacks (widget, XmNactivateCallback);
701 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
702 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
703 XtAddCallback (widget, XmNvalueChangedCallback,
704 xm_internal_update_other_instances, instance);
708 #ifdef LWLIB_SCROLLBARS_MOTIF
711 * If this function looks like it does a lot more work than it needs to,
712 * you're right. Blame the Motif scrollbar for not being smart about
713 * updating its appearance.
716 xm_update_scrollbar (widget_instance *instance, Widget widget,
719 if (val->scrollbar_data)
721 scrollbar_values *data = val->scrollbar_data;
722 int widget_sliderSize, widget_val;
723 int new_sliderSize, new_value;
725 double h_water, l_water;
728 /* First size and position the scrollbar widget. */
729 XtSetArg (al [0], XtNx, data->scrollbar_x);
730 XtSetArg (al [1], XtNy, data->scrollbar_y);
731 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
732 XtSetArg (al [3], XtNheight, data->scrollbar_height);
733 XtSetValues (widget, al, 4);
735 /* Now size the scrollbar's slider. */
736 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
737 XtSetArg (al [1], XmNvalue, &widget_val);
738 XtGetValues (widget, al, 2);
740 percent = (double) data->slider_size /
741 (double) (data->maximum - data->minimum);
742 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
744 percent = (double) (data->slider_position - data->minimum) /
745 (double) (data->maximum - data->minimum);
746 new_value = (int) ((double) (INT_MAX - 1) * percent);
748 if (new_sliderSize > (INT_MAX - 1))
749 new_sliderSize = INT_MAX - 1;
750 else if (new_sliderSize < 1)
753 if (new_value > (INT_MAX - new_sliderSize))
754 new_value = INT_MAX - new_sliderSize;
755 else if (new_value < 1)
760 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
762 int force = ((INT_MAX - widget_sliderSize - widget_val)
764 : (INT_MAX - new_sliderSize - new_value));
767 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
768 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
769 || (double)new_value < (l_water * (double)widget_val)
770 || (double)new_value > (h_water * (double)widget_val))
772 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
779 #endif /* LWLIB_SCROLLBARS_MOTIF */
782 /* update a motif widget */
785 xm_update_one_widget (widget_instance* instance, Widget widget,
786 widget_value* val, Boolean deep_p)
792 /* Mark as not edited */
795 /* Common to all widget types */
796 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
797 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
798 XtSetValues (widget, al, ac);
800 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
801 /* Common to all label like widgets */
802 if (XtIsSubclass (widget, xmLabelWidgetClass))
803 xm_update_label (instance, widget, val);
805 class = XtClass (widget);
806 /* Class specific things */
807 if (class == xmPushButtonWidgetClass ||
808 class == xmArrowButtonWidgetClass)
810 xm_update_pushbutton (instance, widget, val);
812 #ifdef LWLIB_MENUBARS_MOTIF
813 else if (class == xmCascadeButtonWidgetClass)
815 xm_update_cascadebutton (instance, widget, val);
818 else if (class == xmToggleButtonWidgetClass
819 || class == xmToggleButtonGadgetClass)
821 xm_update_toggle (instance, widget, val);
823 else if (class == xmRowColumnWidgetClass)
825 Boolean radiobox = 0;
827 XtSetArg (al [0], XmNradioBehavior, &radiobox);
828 XtGetValues (widget, al, 1);
831 xm_update_radiobox (instance, widget, val);
832 #ifdef LWLIB_MENUBARS_MOTIF
834 xm_update_menu (instance, widget, val, deep_p);
837 else if (class == xmTextWidgetClass)
839 xm_update_text (instance, widget, val);
841 else if (class == xmTextFieldWidgetClass)
843 xm_update_text_field (instance, widget, val);
845 else if (class == xmListWidgetClass)
847 xm_update_list (instance, widget, val);
849 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
850 else if (class == xmComboBoxWidgetClass)
852 xm_update_combo_box (instance, widget, val);
855 #ifdef LWLIB_SCROLLBARS_MOTIF
856 else if (class == xmScrollBarWidgetClass)
858 xm_update_scrollbar (instance, widget, val);
861 /* Lastly update our global arg values. */
862 if (val->args && val->args->nargs)
863 XtSetValues (widget, val->args->args, val->args->nargs);
866 \f/* getting the value back */
868 xm_update_one_value (widget_instance* instance, Widget widget,
871 WidgetClass class = XtClass (widget);
872 widget_value *old_wv;
874 /* copy the call_data slot into the "return" widget_value */
875 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
876 if (!strcmp (val->name, old_wv->name))
878 val->call_data = old_wv->call_data;
882 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
885 XtSetArg (al [0], XmNset, &val->selected);
886 XtGetValues (widget, al, 1);
889 else if (class == xmTextWidgetClass)
893 val->value = XmTextGetString (widget);
896 else if (class == xmTextFieldWidgetClass)
900 val->value = XmTextFieldGetString (widget);
903 else if (class == xmRowColumnWidgetClass)
905 Boolean radiobox = 0;
908 XtSetArg (al [0], XmNradioBehavior, &radiobox);
909 XtGetValues (widget, al, 1);
914 CompositeWidget radio = (CompositeWidget)widget;
916 for (i = 0; i < radio->composite.num_children; i++)
919 Widget toggle = radio->composite.children [i];
922 XtSetArg (al [0], XmNset, &set);
923 XtGetValues (toggle, al, 1);
928 val->value = safe_strdup (XtName (toggle));
934 else if (class == xmListWidgetClass
935 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
936 || class == xmComboBoxWidgetClass
942 Widget list = widget;
943 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
944 if (class == xmComboBoxWidgetClass)
945 list = CB_List (widget);
947 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
951 for (cur = val->contents, i = 0; cur; cur = cur->next)
955 cur->selected = False;
957 for (j = 0; j < pos_cnt; j++)
958 if (pos_list [j] == i)
960 cur->selected = True;
961 val->value = safe_strdup (cur->name);
965 XtFree ((char *) pos_list);
968 #ifdef LWLIB_SCROLLBARS_MOTIF
969 else if (class == xmScrollBarWidgetClass)
971 /* This function is not used by the scrollbar. */
978 /* This function is for activating a button from a program. It's wrong because
979 we pass a NULL argument in the call_data which is not Motif compatible.
980 This is used from the XmNdefaultAction callback of the List widgets to
981 have a double-click put down a dialog box like the button would do.
982 I could not find a way to do that with accelerators.
985 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
987 Widget button = (Widget)closure;
988 XtCallCallbacks (button, XmNactivateCallback, NULL);
991 /* creation functions */
993 #ifdef LWLIB_DIALOGS_MOTIF
997 #if (XmVersion >= 1002)
998 # define ARMANDACTIVATE_KLUDGE
1002 #ifdef ARMANDACTIVATE_KLUDGE
1003 /* We want typing Return at a dialog box to select the default button; but
1004 we're satisfied with having it select the leftmost button instead.
1006 In Motif 1.1.5 we could do this by putting this resource in the
1009 *dialog*button1.accelerators:#override\
1010 <KeyPress>Return: ArmAndActivate()\n\
1011 <KeyPress>KP_Enter: ArmAndActivate()\n\
1012 Ctrl<KeyPress>m: ArmAndActivate()\n
1014 but that doesn't work with 1.2.1 and I don't understand why. However,
1015 doing the equivalent C code does work, with the notable disadvantage that
1016 the user can't override it. So that's what we do until we figure out
1017 something better....
1019 static char button_trans[] = "\
1020 <KeyPress>Return: ArmAndActivate()\n\
1021 <KeyPress>KP_Enter: ArmAndActivate()\n\
1022 Ctrl<KeyPress>m: ArmAndActivate()\n";
1024 #endif /* ARMANDACTIVATE_KLUDGE */
1028 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1029 was a segv down in libXm somewhere if you used the middle button on a
1030 dialog box to begin a drag; when you released the button to make a drop
1031 things would lose if you were not over the button where you started the
1032 drag (canceling the operation). This was probably due to the fact that
1033 the dialog boxes were not set up to handle a drag but were trying to do
1034 so anyway for some reason.
1036 So we disable drag-and-drop in dialog boxes by turning off the binding for
1037 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1038 solution as it only works in default configurations, but...
1040 static char disable_dnd_trans[] = "<Btn2Down>: ";
1041 #endif /* DND_KLUDGE */
1045 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1046 const char* shell_title, const char* icon_name,
1047 Boolean text_input_slot, Boolean radio_box, Boolean list,
1048 int left_buttons, int right_buttons)
1054 Widget icon_separator;
1059 Widget children [16]; /* for the final XtManageChildren */
1061 Arg al[64]; /* Arg List */
1062 int ac; /* Arg Count */
1066 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1067 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1068 #else /* ! DND_KLUDGE */
1069 # define DO_DND_KLUDGE(widget)
1070 #endif /* ! DND_KLUDGE */
1075 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1076 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1077 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1078 result = XmCreateDialogShell (parent, "dialog", al, ac);
1080 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1081 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1082 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1083 form = XmCreateForm (result, (char *) shell_title, al, ac);
1088 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1089 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1090 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1095 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1096 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1097 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1098 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1099 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1100 XtSetArg(al[ac], XmNspacing, 13); ac++;
1101 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1102 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1103 XtSetArg(al[ac], XmNisAligned, True); ac++;
1104 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1105 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1106 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1107 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1108 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1109 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1110 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1111 row = XmCreateRowColumn (form, "row", al, ac);
1114 for (i = 0; i < left_buttons; i++)
1116 char button_name [16];
1117 sprintf (button_name, "button%d", i + 1);
1121 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1122 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1124 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1125 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1126 DO_DND_KLUDGE (children [n_children]);
1130 button = children [n_children];
1132 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1133 XtSetValues (row, al, ac);
1135 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1137 XtTranslations losers = XtParseTranslationTable (button_trans);
1138 XtOverrideTranslations (button, losers);
1139 XtFree ((char *) losers);
1141 #endif /* ARMANDACTIVATE_KLUDGE */
1147 /* invisible separator button */
1149 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1150 children [n_children] = XmCreateLabel (row, "separator_button",
1152 DO_DND_KLUDGE (children [n_children]);
1155 for (i = 0; i < right_buttons; i++)
1157 char button_name [16];
1158 sprintf (button_name, "button%d", left_buttons + i + 1);
1160 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1161 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1162 DO_DND_KLUDGE (children [n_children]);
1163 if (! button) button = children [n_children];
1167 XtManageChildren (children, n_children);
1170 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1171 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1172 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1173 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1174 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1175 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1176 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1177 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1178 separator = XmCreateSeparator (form, "", al, ac);
1181 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1182 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1183 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1184 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1185 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1186 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1187 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1188 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1189 DO_DND_KLUDGE (icon);
1192 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1193 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1194 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1195 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1196 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1197 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1198 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1199 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1200 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1201 icon_separator = XmCreateLabel (form, "", al, ac);
1202 DO_DND_KLUDGE (icon_separator);
1204 if (text_input_slot)
1207 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1208 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1209 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1210 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1211 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1212 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1213 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1214 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1215 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1216 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1217 value = XmCreateTextField (form, "value", al, ac);
1218 DO_DND_KLUDGE (value);
1224 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1225 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1226 XtSetArg(al[ac], XmNspacing, 13); ac++;
1227 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1228 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1229 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1230 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1231 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1232 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1233 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1234 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1235 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1236 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1237 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1240 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1241 children [i++] = radio_butt;
1242 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1243 children [i++] = radio_butt;
1244 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1245 children [i++] = radio_butt;
1246 XtManageChildren (children, i);
1251 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1252 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1253 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1254 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1255 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1256 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1257 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1258 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1259 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1260 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1261 value = XmCreateScrolledList (form, "list", al, ac);
1263 /* this is the easiest way I found to have the double click in the
1264 list activate the default button */
1265 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1269 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1270 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1271 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1272 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1273 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1274 XtSetArg(al[ac], XmNbottomWidget,
1275 text_input_slot || radio_box || list ? value : separator); ac++;
1276 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1277 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1278 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1279 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1280 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1281 message = XmCreateLabel (form, "message", al, ac);
1282 DO_DND_KLUDGE (message);
1285 XtManageChild (value);
1288 children [i] = row; i++;
1289 children [i] = separator; i++;
1290 if (text_input_slot || radio_box)
1292 children [i] = value; i++;
1294 children [i] = message; i++;
1295 children [i] = icon; i++;
1296 children [i] = icon_separator; i++;
1297 XtManageChildren (children, i);
1299 if (text_input_slot || list)
1301 XtInstallAccelerators (value, button);
1302 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1306 XtInstallAccelerators (form, button);
1307 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1311 XtFree ((char *) dnd_override);
1313 #undef DO_DND_KLUDGE
1318 static destroyed_instance*
1319 find_matching_instance (widget_instance* instance)
1321 destroyed_instance* cur;
1322 destroyed_instance* prev;
1323 char* type = instance->info->type;
1324 char* name = instance->info->name;
1326 for (prev = NULL, cur = all_destroyed_instances;
1328 prev = cur, cur = cur->next)
1330 if (!strcmp (cur->name, name)
1331 && !strcmp (cur->type, type)
1332 && cur->parent == instance->parent
1333 && cur->pop_up_p == instance->pop_up_p)
1336 prev->next = cur->next;
1338 all_destroyed_instances = cur->next;
1341 /* do some cleanup */
1342 else if (!cur->widget)
1345 prev->next = cur->next;
1347 all_destroyed_instances = cur->next;
1348 free_destroyed_instance (cur);
1349 cur = prev ? prev : all_destroyed_instances;
1356 recenter_widget (Widget widget)
1358 Widget parent = XtParent (widget);
1359 Screen* screen = XtScreen (widget);
1360 Dimension screen_width = WidthOfScreen (screen);
1361 Dimension screen_height = HeightOfScreen (screen);
1362 Dimension parent_width = 0;
1363 Dimension parent_height = 0;
1364 Dimension child_width = 0;
1365 Dimension child_height = 0;
1370 XtSetArg (al [0], XtNwidth, &child_width);
1371 XtSetArg (al [1], XtNheight, &child_height);
1372 XtGetValues (widget, al, 2);
1374 XtSetArg (al [0], XtNwidth, &parent_width);
1375 XtSetArg (al [1], XtNheight, &parent_height);
1376 XtGetValues (parent, al, 2);
1378 x = (Position) ((parent_width - child_width) / 2);
1379 y = (Position) ((parent_height - child_height) / 2);
1381 XtTranslateCoords (parent, x, y, &x, &y);
1383 if ((Dimension) (x + child_width) > screen_width)
1384 x = screen_width - child_width;
1388 if ((Dimension) (y + child_height) > screen_height)
1389 y = screen_height - child_height;
1393 XtSetArg (al [0], XtNx, x);
1394 XtSetArg (al [1], XtNy, y);
1395 XtSetValues (widget, al, 2);
1399 recycle_instance (destroyed_instance* instance)
1401 Widget widget = instance->widget;
1403 /* widget is NULL if the parent was destroyed. */
1409 /* Remove the destroy callback as the instance is not in the list
1411 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1412 mark_dead_instance_destroyed,
1413 (XtPointer)instance);
1415 /* Give the focus to the initial item */
1416 focus = XtNameToWidget (widget, "*value");
1418 focus = XtNameToWidget (widget, "*button1");
1420 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1422 /* shrink the separator label back to their original size */
1423 separator = XtNameToWidget (widget, "*separator_button");
1427 XtSetArg (al [0], XtNwidth, 5);
1428 XtSetArg (al [1], XtNheight, 5);
1429 XtSetValues (separator, al, 2);
1432 /* Center the dialog in its parent */
1433 recenter_widget (widget);
1435 free_destroyed_instance (instance);
1440 xm_create_dialog (widget_instance* instance)
1442 char* name = instance->info->type;
1443 Widget parent = instance->parent;
1445 Boolean pop_up_p = instance->pop_up_p;
1446 const char* shell_name = 0;
1447 const char* icon_name = 0;
1448 Boolean text_input_slot = False;
1449 Boolean radio_box = False;
1450 Boolean list = False;
1452 int left_buttons = 0;
1453 int right_buttons = 1;
1454 destroyed_instance* dead_one;
1456 /* try to find a widget to recycle */
1457 dead_one = find_matching_instance (instance);
1460 Widget recycled_widget = recycle_instance (dead_one);
1461 if (recycled_widget)
1462 return recycled_widget;
1467 icon_name = "dbox-error";
1468 shell_name = "Error";
1472 icon_name = "dbox-info";
1473 shell_name = "Information";
1478 icon_name = "dbox-question";
1479 shell_name = "Prompt";
1483 text_input_slot = True;
1484 icon_name = "dbox-question";
1485 shell_name = "Prompt";
1489 icon_name = "dbox-question";
1490 shell_name = "Question";
1494 total_buttons = name [1] - '0';
1496 if (name [3] == 'T' || name [3] == 't')
1498 text_input_slot = False;
1502 right_buttons = name [4] - '0';
1504 left_buttons = total_buttons - right_buttons;
1506 widget = make_dialog (name, parent, pop_up_p,
1507 shell_name, icon_name, text_input_slot, radio_box,
1508 list, left_buttons, right_buttons);
1510 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1511 (XtPointer) instance);
1515 #endif /* LWLIB_DIALOGS_MOTIF */
1517 #ifdef LWLIB_MENUBARS_MOTIF
1519 make_menubar (widget_instance* instance)
1524 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1525 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1527 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1531 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1533 Widget menu = (Widget) closure;
1534 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1538 make_popup_menu (widget_instance* instance)
1540 Widget parent = instance->parent;
1541 Window parent_window = parent->core.window;
1544 /* sets the parent window to 0 to fool Motif into not generating a grab */
1545 parent->core.window = 0;
1546 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1547 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1549 parent->core.window = parent_window;
1552 #endif /* LWLIB_MENUBARS_MOTIF */
1554 #ifdef LWLIB_SCROLLBARS_MOTIF
1556 make_scrollbar (widget_instance *instance, int vertical)
1560 static XtCallbackRec callbacks[2] =
1561 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1563 callbacks[0].closure = (XtPointer) instance;
1565 XtSetArg (al[ac], XmNminimum, 1); ac++;
1566 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1567 XtSetArg (al[ac], XmNincrement, 1); ac++;
1568 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1569 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1570 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1572 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1573 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1574 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1575 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1576 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1577 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1578 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1579 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1581 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1585 make_vertical_scrollbar (widget_instance *instance)
1587 return make_scrollbar (instance, 1);
1591 make_horizontal_scrollbar (widget_instance *instance)
1593 return make_scrollbar (instance, 0);
1596 #endif /* LWLIB_SCROLLBARS_MOTIF */
1598 #ifdef LWLIB_WIDGETS_MOTIF
1601 xm_create_button (widget_instance *instance)
1606 widget_value* val = instance->info->val;
1608 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1609 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1610 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1611 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1612 /* The highlight doesn't appear to be dynamically set which makes it
1613 look ugly. I think this may be a LessTif bug but for now we just
1615 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1617 /* add any args the user supplied for creation time */
1618 lw_add_value_args_to_args (val, al, &ac);
1620 if (!val->call_data)
1621 button = XmCreateLabel (instance->parent, val->name, al, ac);
1623 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1625 XtSetArg (al [ac], XmNset, val->selected); ac++;
1626 XtSetArg (al [ac], XmNindicatorType,
1627 (val->type == TOGGLE_TYPE ?
1628 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1629 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1630 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1631 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1632 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1633 (XtPointer)instance);
1637 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1638 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1639 (XtPointer)instance);
1642 XtManageChild (button);
1648 xm_create_progress (widget_instance *instance)
1653 widget_value* val = instance->info->val;
1654 #if 0 /* This looks too awful, although more correct. */
1655 if (!val->call_data)
1657 XtSetArg (al [ac], XmNsensitive, False); ac++;
1661 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1664 XtSetArg (al [ac], XmNsensitive, True); ac++;
1666 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1667 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1668 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1669 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1670 /* The highlight doesn't appear to be dynamically set which makes it
1671 look ugly. I think this may be a LessTif bug but for now we just
1673 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1674 /* add any args the user supplied for creation time */
1675 lw_add_value_args_to_args (val, al, &ac);
1677 scale = XmCreateScale (instance->parent, val->name, al, ac);
1679 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1680 (XtPointer)instance);
1682 XtManageChild (scale);
1688 xm_create_text_field (widget_instance *instance)
1693 widget_value* val = instance->info->val;
1695 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1696 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1697 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1698 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1699 /* The highlight doesn't appear to be dynamically set which makes it
1700 look ugly. I think this may be a LessTif bug but for now we just
1702 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1704 /* add any args the user supplied for creation time */
1705 lw_add_value_args_to_args (val, al, &ac);
1707 text = XmCreateTextField (instance->parent, val->name, al, ac);
1709 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1710 (XtPointer)instance);
1712 XtManageChild (text);
1718 xm_create_label_field (widget_instance *instance)
1720 return xm_create_label (instance->parent, instance->info->val);
1724 xm_create_label (Widget parent, widget_value* val)
1730 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1731 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1732 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1733 /* The highlight doesn't appear to be dynamically set which makes it
1734 look ugly. I think this may be a LessTif bug but for now we just
1736 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1738 /* add any args the user supplied for creation time */
1739 lw_add_value_args_to_args (val, al, &ac);
1741 label = XmCreateLabel (parent, val->name, al, ac);
1743 XtManageChild (label);
1745 /* Do it again for arguments that have no effect until the widget is realized. */
1747 lw_add_value_args_to_args (val, al, &ac);
1748 XtSetValues (label, al, ac);
1755 xm_create_combo_box (widget_instance *instance)
1760 widget_value* val = instance->info->val;
1762 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1763 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1764 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1765 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1766 /* The highlight doesn't appear to be dynamically set which makes it
1767 look ugly. I think this may be a LessTif bug but for now we just
1769 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1771 /* add any args the user supplied for creation time */
1772 lw_add_value_args_to_args (val, al, &ac);
1774 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1776 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1777 (XtPointer)instance);
1779 XtManageChild (combo);
1784 #endif /* LWLIB_WIDGETS_MOTIF */
1787 /* Table of functions to create widgets */
1789 const widget_creation_entry
1790 xm_creation_table [] =
1792 #ifdef LWLIB_MENUBARS_MOTIF
1793 {"menubar", make_menubar},
1794 {"popup", make_popup_menu},
1796 #ifdef LWLIB_SCROLLBARS_MOTIF
1797 {"vertical-scrollbar", make_vertical_scrollbar},
1798 {"horizontal-scrollbar", make_horizontal_scrollbar},
1800 #ifdef LWLIB_WIDGETS_MOTIF
1801 {"button", xm_create_button},
1802 {"progress", xm_create_progress},
1803 {"text-field", xm_create_text_field},
1804 {"label", xm_create_label_field},
1806 {"combo-box", xm_create_combo_box},
1812 \f/* Destruction of instances */
1814 xm_destroy_instance (widget_instance* instance)
1816 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1817 /* It appears that this is used only for dialog boxes. */
1818 Widget widget = instance->widget;
1819 /* recycle the dialog boxes */
1820 /* Disable the recycling until we can find a way to have the dialog box
1821 get reasonable layout after we modify its contents. */
1823 && XtClass (widget) == xmDialogShellWidgetClass)
1825 destroyed_instance* dead_instance =
1826 make_destroyed_instance (instance->info->name,
1827 instance->info->type,
1830 instance->pop_up_p);
1831 dead_instance->next = all_destroyed_instances;
1832 all_destroyed_instances = dead_instance;
1833 XtUnmanageChild (first_child (instance->widget));
1834 XFlush (XtDisplay (instance->widget));
1835 XtAddCallback (instance->parent, XtNdestroyCallback,
1836 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1840 /* This might not be necessary now that the nosel is attached to
1841 popdown instead of destroy, but it can't hurt. */
1842 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1843 xm_nosel_callback, (XtPointer)instance);
1845 XtDestroyWidget (instance->widget);
1847 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1850 \f/* popup utility */
1851 #ifdef LWLIB_MENUBARS_MOTIF
1854 xm_popup_menu (Widget widget, XEvent *event)
1856 if (event->type == ButtonPress || event->type == ButtonRelease)
1858 /* This is so totally ridiculous: there's NO WAY to tell Motif
1859 that *any* button can select a menu item. Only one button
1860 can have that honor.
1863 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1864 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1865 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1866 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1867 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1871 XtSetArg (al [0], XmNmenuPost, trans);
1872 XtSetValues (widget, al, 1);
1874 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1876 XtManageChild (widget);
1881 #ifdef LWLIB_DIALOGS_MOTIF
1884 set_min_dialog_size (Widget w)
1890 XtSetArg (al [0], XmNwidth, &width);
1891 XtSetArg (al [1], XmNheight, &height);
1892 XtGetValues (w, al, 2);
1894 XtSetArg (al [0], XmNminWidth, width);
1895 XtSetArg (al [1], XmNminHeight, height);
1896 XtSetValues (w, al, 2);
1902 xm_pop_instance (widget_instance* instance, Boolean up)
1904 Widget widget = instance->widget;
1906 #ifdef LWLIB_DIALOGS_MOTIF
1907 if (XtClass (widget) == xmDialogShellWidgetClass)
1909 Widget widget_to_manage = first_child (widget);
1912 XtManageChild (widget_to_manage);
1913 set_min_dialog_size (widget);
1914 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1917 XtUnmanageChild (widget_to_manage);
1923 XtManageChild (widget);
1925 XtUnmanageChild (widget);
1930 /* motif callback */
1932 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1935 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1937 XtPointer user_data;
1938 widget_instance* instance = (widget_instance*)closure;
1939 Widget instance_widget;
1945 if (widget->core.being_destroyed)
1948 instance_widget = instance->widget;
1949 if (!instance_widget)
1952 id = instance->info->id;
1954 XtSetArg(al [0], XmNuserData, &user_data);
1955 XtGetValues (widget, al, 1);
1959 if (instance->info->pre_activate_cb)
1960 instance->info->pre_activate_cb (widget, id, user_data);
1963 if (instance->info->selection_cb)
1964 instance->info->selection_cb (widget, id, user_data);
1967 if (instance->info->selection_cb)
1968 instance->info->selection_cb (widget, id, (XtPointer) -1);
1971 if (instance->info->post_activate_cb)
1972 instance->info->post_activate_cb (widget, id, user_data);
1979 /* Like lw_internal_update_other_instances except that it does not do
1980 anything if its shell parent is not managed. This is to protect
1981 lw_internal_update_other_instances to dereference freed memory
1982 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1985 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1986 XtPointer call_data)
1989 for (parent = widget; parent; parent = XtParent (parent))
1990 if (XtIsShell (parent))
1992 else if (!XtIsManaged (parent))
1994 lw_internal_update_other_instances (widget, closure, call_data);
1998 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
2000 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2001 /* We want the selected status to change only when we decide it
2002 should change. Yuck but correct. */
2003 if (XtClass (widget) == xmToggleButtonWidgetClass
2004 || XtClass (widget) == xmToggleButtonGadgetClass)
2009 XtSetArg (al [0], XmNset, &check);
2010 XtGetValues (widget, al, 1);
2012 XtSetArg (al [0], XmNset, !check);
2013 XtSetValues (widget, al, 1);
2016 lw_internal_update_other_instances (widget, closure, call_data);
2017 do_call (widget, closure, selection);
2021 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2023 do_call (widget, closure, post_activate);
2026 #ifdef LWLIB_MENUBARS_MOTIF
2029 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2034 /* new behavior for incremental menu construction */
2039 do_call (widget, closure, pre_activate);
2042 #endif /* LWLIB_MENUBARS_MOTIF */
2044 #ifdef LWLIB_SCROLLBARS_MOTIF
2046 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2048 widget_instance *instance = (widget_instance *) closure;
2050 XmScrollBarCallbackStruct *data =
2051 (XmScrollBarCallbackStruct *) call_data;
2052 scroll_event event_data;
2053 scrollbar_values *val =
2054 (scrollbar_values *) instance->info->val->scrollbar_data;
2057 if (!instance || widget->core.being_destroyed)
2060 id = instance->info->id;
2062 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2063 event_data.slider_value =
2064 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2066 if (event_data.slider_value > (val->maximum - val->slider_size))
2067 event_data.slider_value = val->maximum - val->slider_size;
2068 else if (event_data.slider_value < 1)
2069 event_data.slider_value = 1;
2073 switch (data->event->xany.type)
2077 event_data.time = data->event->xkey.time;
2081 event_data.time = data->event->xbutton.time;
2084 event_data.time = data->event->xmotion.time;
2088 event_data.time = data->event->xcrossing.time;
2091 event_data.time = 0;
2096 event_data.time = 0;
2098 switch (data->reason)
2100 case XmCR_DECREMENT:
2101 event_data.action = SCROLLBAR_LINE_UP;
2103 case XmCR_INCREMENT:
2104 event_data.action = SCROLLBAR_LINE_DOWN;
2106 case XmCR_PAGE_DECREMENT:
2107 event_data.action = SCROLLBAR_PAGE_UP;
2109 case XmCR_PAGE_INCREMENT:
2110 event_data.action = SCROLLBAR_PAGE_DOWN;
2113 event_data.action = SCROLLBAR_TOP;
2115 case XmCR_TO_BOTTOM:
2116 event_data.action = SCROLLBAR_BOTTOM;
2119 event_data.action = SCROLLBAR_DRAG;
2121 case XmCR_VALUE_CHANGED:
2122 event_data.action = SCROLLBAR_CHANGE;
2125 event_data.action = SCROLLBAR_CHANGE;
2129 if (instance->info->pre_activate_cb)
2130 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2132 #endif /* LWLIB_SCROLLBARS_MOTIF */
2134 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2136 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2137 XtPointer call_data)
2139 destroyed_instance* instance = (destroyed_instance*)closure;
2140 instance->widget = NULL;
2144 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2146 /* This callback is only called when a dialog box is dismissed with the wm's
2147 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2148 in that case, not just unmapped, so that it releases its keyboard grabs.
2149 But there are problems with running our callbacks while the widget is in
2150 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2151 instead of XmDESTROY and then destroy it ourself after having run the
2154 do_call (widget, closure, no_selection);
2155 XtDestroyWidget (widget);
2160 /* set the keyboard focus */
2162 xm_set_keyboard_focus (Widget parent, Widget w)
2164 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2165 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2166 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2167 Presumably the problem was elsewhere, and is now gone...