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>
64 #ifdef LWLIB_MENUBARS_MOTIF
65 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
67 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
70 static void xm_internal_update_other_instances (Widget, XtPointer,
72 static void xm_generic_callback (Widget, XtPointer, XtPointer);
73 #ifdef LWLIB_DIALOGS_MOTIF
74 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
76 #ifdef LWLIB_SCROLLBARS_MOTIF
77 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
80 #ifdef LWLIB_MENUBARS_MOTIF
82 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
86 \f/* Structures to keep destroyed instances */
87 typedef struct _destroyed_instance
94 struct _destroyed_instance* next;
97 static destroyed_instance*
98 all_destroyed_instances = NULL;
100 /* Utility function. */
102 safe_strdup (char* s)
106 result = (char *) malloc (strlen (s) + 1);
113 static destroyed_instance*
114 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
117 destroyed_instance* instance =
118 (destroyed_instance*) malloc (sizeof (destroyed_instance));
119 instance->name = safe_strdup (name);
120 instance->type = safe_strdup (type);
121 instance->widget = widget;
122 instance->parent = parent;
123 instance->pop_up_p = pop_up_p;
124 instance->next = NULL;
129 free_destroyed_instance (destroyed_instance* instance)
131 free (instance->name);
132 free (instance->type);
136 \f/* motif utility functions */
138 first_child (Widget widget)
140 return ((CompositeWidget)widget)->composite.children [0];
144 lw_motif_widget_p (Widget widget)
147 #ifdef LWLIB_DIALOGS_MOTIF
148 XtClass (widget) == xmDialogShellWidgetClass ||
150 XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
154 resource_string (Widget widget, char *name)
159 resource.resource_name = "labelString";
160 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
161 resource.resource_type = XtRString;
162 resource.resource_size = sizeof (String);
163 resource.resource_offset = 0;
164 resource.default_type = XtRImmediate;
165 resource.default_addr = 0;
167 XtGetSubresources (widget, (XtPointer)&result, name,
168 name, &resource, 1, NULL, 0);
172 #ifdef LWLIB_MENUBARS_MOTIF
175 destroy_all_children (Widget widget)
181 children = XtCompositeChildren (widget, &number);
184 /* Unmanage all children and destroy them. They will only be
185 * really destroyed when we get out of DispatchEvent. */
186 for (i = 0; i < number; i++)
188 Widget child = children [i];
189 if (!child->core.being_destroyed)
191 XtUnmanageChild (child);
192 XtDestroyWidget (child);
195 XtFree ((char *) children);
199 #endif /* LWLIB_MENUBARS_MOTIF */
203 #ifdef LWLIB_DIALOGS_MOTIF
206 is_in_dialog_box (Widget w)
210 wmshell = XtParent (w);
211 while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
212 wmshell = XtParent (wmshell);
214 if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
220 #endif /* LWLIB_DIALOGS_MOTIF */
222 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF)
224 /* update the label of anything subclass of a label */
226 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
228 XmString built_string = NULL;
229 XmString key_string = NULL;
230 XmString val_string = NULL;
231 XmString name_string = NULL;
237 #ifdef LWLIB_DIALOGS_MOTIF
239 * Sigh. The main text of a label is the name field for menubar
240 * entries. The value field is a possible additional field to be
241 * concatenated on to the name field. HOWEVER, with dialog boxes
242 * the value field is the complete text which is supposed to be
243 * displayed as the label. Yuck.
245 if (is_in_dialog_box (widget))
247 char *value_name = NULL;
249 value_name = resource_string (widget, val->value);
251 value_name = val->value;
254 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
257 #endif /* LWLIB_DIALOGS_MOTIF */
259 char *value_name = NULL;
260 char *res_name = NULL;
262 res_name = resource_string (widget, val->name);
264 res_name = val->name;
267 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
269 value_name = XtMalloc (strlen (val->value) + 2);
271 strcat (value_name, " ");
272 strcat (value_name, val->value);
275 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
278 XmStringConcat (name_string, val_string);
283 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
284 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
289 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
290 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
294 XtSetValues (widget, al, ac);
297 XmStringFree (built_string);
300 XmStringFree (key_string);
303 XmStringFree (name_string);
306 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
308 \f/* update of list */
310 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
314 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
315 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
317 for (cur = val->contents, i = 0; cur; cur = cur->next)
320 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
322 XmListAddItem (widget, xmstr, 0);
324 XmListSelectPos (widget, i, False);
325 XmStringFree (xmstr);
329 \f/* update of buttons */
331 xm_update_pushbutton (widget_instance* instance, Widget widget,
335 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
336 XtSetValues (widget, al, 1);
337 XtRemoveAllCallbacks (widget, XmNactivateCallback);
338 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
341 #ifdef LWLIB_MENUBARS_MOTIF
344 xm_update_cascadebutton (widget_instance* instance, Widget widget,
347 /* Should also rebuild the menu by calling ...update_menu... */
349 && val->type == CASCADE_TYPE
351 && val->contents->type == INCREMENTAL_TYPE)
353 /* okay, we're now doing a lisp callback to incrementally generate
355 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
356 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
358 XtCallCallbacks ((Widget)widget,
359 XmNcascadingCallback,
360 (XtPointer)val->contents);
363 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
364 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
369 #endif /* LWLIB_MENUBARS_MOTIF */
371 \f/* update toggle and radiobox */
373 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
376 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
377 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
379 XtSetArg (al [0], XmNset, val->selected);
380 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
381 XtSetValues (widget, al, 2);
385 xm_update_radiobox (widget_instance* instance, Widget widget,
391 /* update the callback */
392 XtRemoveAllCallbacks (widget, XmNentryCallback);
393 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
395 /* first update all the toggles */
396 /* Energize kernel interface is currently bad. It sets the selected widget
397 with the selected flag but returns it by its name. So we currently
398 have to support both setting the selection with the selected slot
399 of val contents and setting it with the "value" slot of val. The latter
400 has a higher priority. This to be removed when the kernel is fixed. */
401 for (cur = val->contents; cur; cur = cur->next)
403 toggle = XtNameToWidget (widget, cur->value);
407 XtSetArg (al [0], XmNsensitive, cur->enabled);
408 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
409 XtSetValues (toggle, al, 2);
413 /* The selected was specified by the value slot */
416 toggle = XtNameToWidget (widget, val->value);
420 XtSetArg (al [0], XmNset, True);
421 XtSetValues (toggle, al, 1);
426 #ifdef LWLIB_MENUBARS_MOTIF
428 \f/* update a popup menu, pulldown menu or a menubar */
430 make_menu_in_widget (widget_instance* instance, Widget widget,
433 Widget* children = 0;
441 Boolean menubar_p = False;
443 /* Allocate the children array */
444 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
445 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
447 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
448 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
449 XtGetValues (widget, al, 1);
451 /* add the unmap callback for popups and pulldowns */
452 /*** this sounds bogus ***/
453 /* probably because it is -- cet */
456 XtAddCallback (XtParent (widget), XmNpopdownCallback,
457 xm_pop_down_callback, (XtPointer)instance);
461 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
465 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
466 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
467 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
472 /* A pushright marker which is not needed for the real Motif
479 /* #### - xlwmenu.h supports several types that motif does
480 not. Also, motif supports pixmaps w/ type NO_LINE and
481 lwlib provides no way to access that functionality. --Stig */
482 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
484 button = XmCreateSeparator (widget, "separator", al, ac);
487 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
488 make_menu_in_widget (instance, menu, cur->contents);
489 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
490 button = XmCreateCascadeButton (widget, cur->name, al, ac);
492 xm_update_label (instance, button, cur);
494 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
495 (XtPointer)instance);
499 button = XmCreateCascadeButton (widget, cur->name, al, ac);
500 else if (!cur->call_data)
501 button = XmCreateLabel (widget, cur->name, al, ac);
502 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
504 XtSetArg (al [ac], XmNindicatorType,
505 (cur->type == TOGGLE_TYPE ?
506 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
507 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
508 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
511 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
513 xm_update_label (instance, button, cur);
515 /* don't add a callback to a simple label */
516 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
517 xm_update_toggle (instance, button, cur);
518 else if (cur->call_data)
519 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
520 (XtPointer)instance);
521 } /* switch (cur->type) */
524 children [num_children++] = button;
527 /* Last entry is the help button. This used be done after managing
528 the buttons. The comment claimed that it had to be done this way
529 otherwise the menubar ended up only 4 pixels high. That must
530 have been in the Old World. In the New World it stays the proper
531 height if you don't manage them until after you set this and as a
532 bonus the Help menu ends up where it is supposed to. */
536 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
537 XtSetValues (widget, al, ac);
541 XtManageChildren (children, num_children);
543 XtFree ((char *) children);
547 update_one_menu_entry (widget_instance* instance, Widget widget,
548 widget_value* val, Boolean deep_p)
553 widget_value* contents;
555 if (val->change == NO_CHANGE)
558 /* update the sensitivity and userdata */
559 /* Common to all widget types */
560 XtSetArg (al [0], XmNsensitive, val->enabled);
561 XtSetArg (al [1], XmNuserData, val->call_data);
562 XtSetValues (widget, al, 2);
564 /* update the menu button as a label. */
565 if (val->change >= VISIBLE_CHANGE)
567 xm_update_label (instance, widget, val);
568 if (XtClass (widget) == xmToggleButtonWidgetClass
569 || XtClass (widget) == xmToggleButtonGadgetClass)
571 xm_update_toggle (instance, widget, val);
576 /* update the pulldown/pullaside as needed */
578 XtSetArg (al [0], XmNsubMenuId, &menu);
579 XtGetValues (widget, al, 1);
581 contents = val->contents;
587 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
588 make_menu_in_widget (instance, menu, contents);
590 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
591 XtSetValues (widget, al, ac);
597 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
598 XtSetValues (widget, al, ac);
599 XtDestroyWidget (menu);
601 else if (deep_p && contents->change != NO_CHANGE)
602 xm_update_menu (instance, menu, val, 1);
606 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
609 /* Widget is a RowColumn widget whose contents have to be updated
610 * to reflect the list of items in val->contents */
611 if (val->contents->change == STRUCTURAL_CHANGE)
613 destroy_all_children (widget);
614 make_menu_in_widget (instance, widget, val->contents);
618 /* Update all the buttons of the RowColumn in order. */
620 unsigned int num_children;
622 widget_value *cur = 0;
624 children = XtCompositeChildren (widget, &num_children);
627 for (i = 0, cur = val->contents; i < num_children; i++)
631 /* skip if this is a pushright marker or a separator */
632 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
636 /* #### - this could puke if you have a separator as the
637 last item on a pullright menu. */
645 if (children [i]->core.being_destroyed
646 || strcmp (XtName (children [i]), cur->name))
648 update_one_menu_entry (instance, children [i], cur, deep_p);
651 XtFree ((char *) children);
658 #endif /* LWLIB_MENUBARS_MOTIF */
661 #ifdef LWLIB_DIALOGS_MOTIF
663 /* update text widgets */
666 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
668 XmTextSetString (widget, val->value ? val->value : "");
669 XtRemoveAllCallbacks (widget, XmNactivateCallback);
670 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
671 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
672 XtAddCallback (widget, XmNvalueChangedCallback,
673 xm_internal_update_other_instances, instance);
677 xm_update_text_field (widget_instance* instance, Widget widget,
680 XmTextFieldSetString (widget, val->value ? val->value : "");
681 XtRemoveAllCallbacks (widget, XmNactivateCallback);
682 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
683 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
684 XtAddCallback (widget, XmNvalueChangedCallback,
685 xm_internal_update_other_instances, instance);
688 #endif /* LWLIB_DIALOGS_MOTIF */
690 #ifdef LWLIB_SCROLLBARS_MOTIF
693 * If this function looks like it does a lot more work than it needs to,
694 * you're right. Blame the Motif scrollbar for not being smart about
695 * updating its appearance.
698 xm_update_scrollbar (widget_instance *instance, Widget widget,
701 if (val->scrollbar_data)
703 scrollbar_values *data = val->scrollbar_data;
704 int widget_sliderSize, widget_val;
705 int new_sliderSize, new_value;
707 double h_water, l_water;
710 /* First size and position the scrollbar widget. */
711 XtSetArg (al [0], XtNx, data->scrollbar_x);
712 XtSetArg (al [1], XtNy, data->scrollbar_y);
713 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
714 XtSetArg (al [3], XtNheight, data->scrollbar_height);
715 XtSetValues (widget, al, 4);
717 /* Now size the scrollbar's slider. */
718 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
719 XtSetArg (al [1], XmNvalue, &widget_val);
720 XtGetValues (widget, al, 2);
722 percent = (double) data->slider_size /
723 (double) (data->maximum - data->minimum);
724 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
726 percent = (double) (data->slider_position - data->minimum) /
727 (double) (data->maximum - data->minimum);
728 new_value = (int) ((double) (INT_MAX - 1) * percent);
730 if (new_sliderSize > (INT_MAX - 1))
731 new_sliderSize = INT_MAX - 1;
732 else if (new_sliderSize < 1)
735 if (new_value > (INT_MAX - new_sliderSize))
736 new_value = INT_MAX - new_sliderSize;
737 else if (new_value < 1)
742 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
744 int force = ((INT_MAX - widget_sliderSize - widget_val)
746 : (INT_MAX - new_sliderSize - new_value));
749 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
750 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
751 || (double)new_value < (l_water * (double)widget_val)
752 || (double)new_value > (h_water * (double)widget_val))
754 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
761 #endif /* LWLIB_SCROLLBARS_MOTIF */
764 /* update a motif widget */
767 xm_update_one_widget (widget_instance* instance, Widget widget,
768 widget_value* val, Boolean deep_p)
773 /* Mark as not edited */
776 /* Common to all widget types */
777 XtSetArg (al [0], XmNsensitive, val->enabled);
778 XtSetArg (al [1], XmNuserData, val->call_data);
779 XtSetValues (widget, al, 2);
781 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF)
782 /* Common to all label like widgets */
783 if (XtIsSubclass (widget, xmLabelWidgetClass))
784 xm_update_label (instance, widget, val);
787 class = XtClass (widget);
788 /* Class specific things */
789 if (class == xmPushButtonWidgetClass ||
790 class == xmArrowButtonWidgetClass)
792 xm_update_pushbutton (instance, widget, val);
794 #ifdef LWLIB_MENUBARS_MOTIF
795 else if (class == xmCascadeButtonWidgetClass)
797 xm_update_cascadebutton (instance, widget, val);
800 else if (class == xmToggleButtonWidgetClass
801 || class == xmToggleButtonGadgetClass)
803 xm_update_toggle (instance, widget, val);
805 else if (class == xmRowColumnWidgetClass)
807 Boolean radiobox = 0;
809 XtSetArg (al [0], XmNradioBehavior, &radiobox);
810 XtGetValues (widget, al, 1);
813 xm_update_radiobox (instance, widget, val);
814 #ifdef LWLIB_MENUBARS_MOTIF
816 xm_update_menu (instance, widget, val, deep_p);
819 #ifdef LWLIB_DIALOGS_MOTIF
820 else if (class == xmTextWidgetClass)
822 xm_update_text (instance, widget, val);
824 else if (class == xmTextFieldWidgetClass)
826 xm_update_text_field (instance, widget, val);
829 else if (class == xmListWidgetClass)
831 xm_update_list (instance, widget, val);
833 #ifdef LWLIB_SCROLLBARS_MOTIF
834 else if (class == xmScrollBarWidgetClass)
836 xm_update_scrollbar (instance, widget, val);
841 \f/* getting the value back */
843 xm_update_one_value (widget_instance* instance, Widget widget,
846 WidgetClass class = XtClass (widget);
847 widget_value *old_wv;
849 /* copy the call_data slot into the "return" widget_value */
850 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
851 if (!strcmp (val->name, old_wv->name))
853 val->call_data = old_wv->call_data;
857 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
860 XtSetArg (al [0], XmNset, &val->selected);
861 XtGetValues (widget, al, 1);
864 #ifdef LWLIB_DIALOGS_MOTIF
865 else if (class == xmTextWidgetClass)
869 val->value = XmTextGetString (widget);
872 else if (class == xmTextFieldWidgetClass)
876 val->value = XmTextFieldGetString (widget);
880 else if (class == xmRowColumnWidgetClass)
882 Boolean radiobox = 0;
885 XtSetArg (al [0], XmNradioBehavior, &radiobox);
886 XtGetValues (widget, al, 1);
891 CompositeWidget radio = (CompositeWidget)widget;
893 for (i = 0; i < radio->composite.num_children; i++)
896 Widget toggle = radio->composite.children [i];
899 XtSetArg (al [0], XmNset, &set);
900 XtGetValues (toggle, al, 1);
905 val->value = safe_strdup (XtName (toggle));
911 else if (class == xmListWidgetClass)
915 if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
919 for (cur = val->contents, i = 0; cur; cur = cur->next)
923 cur->selected = False;
925 for (j = 0; j < pos_cnt; j++)
926 if (pos_list [j] == i)
928 cur->selected = True;
929 val->value = safe_strdup (cur->name);
933 XtFree ((char *) pos_list);
936 #ifdef LWLIB_SCROLLBARS_MOTIF
937 else if (class == xmScrollBarWidgetClass)
939 /* This function is not used by the scrollbar. */
946 /* This function is for activating a button from a program. It's wrong because
947 we pass a NULL argument in the call_data which is not Motif compatible.
948 This is used from the XmNdefaultAction callback of the List widgets to
949 have a dble-click put down a dialog box like the button woudl do.
950 I could not find a way to do that with accelerators.
953 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
955 Widget button = (Widget)closure;
956 XtCallCallbacks (button, XmNactivateCallback, NULL);
959 /* creation functions */
961 #ifdef LWLIB_DIALOGS_MOTIF
965 #if (XmVersion >= 1002)
966 # define ARMANDACTIVATE_KLUDGE
970 #ifdef ARMANDACTIVATE_KLUDGE
971 /* We want typing Return at a dialog box to select the default button; but
972 we're satisfied with having it select the leftmost button instead.
974 In Motif 1.1.5 we could do this by putting this resource in the
977 *dialog*button1.accelerators:#override\
978 <KeyPress>Return: ArmAndActivate()\n\
979 <KeyPress>KP_Enter: ArmAndActivate()\n\
980 Ctrl<KeyPress>m: ArmAndActivate()\n
982 but that doesn't work with 1.2.1 and I don't understand why. However,
983 doing the equivalent C code does work, with the notable disadvantage that
984 the user can't override it. So that's what we do until we figure out
987 static char button_trans[] = "\
988 <KeyPress>Return: ArmAndActivate()\n\
989 <KeyPress>KP_Enter: ArmAndActivate()\n\
990 Ctrl<KeyPress>m: ArmAndActivate()\n";
992 #endif /* ARMANDACTIVATE_KLUDGE */
996 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
997 was a segv down in libXm somewhere if you used the middle button on a
998 dialog box to begin a drag; when you released the button to make a drop
999 things would lose if you were not over the button where you started the
1000 drag (canceling the operation). This was probably due to the fact that
1001 the dialog boxes were not set up to handle a drag but were trying to do
1002 so anyway for some reason.
1004 So we disable drag-and-drop in dialog boxes by turning off the binding for
1005 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1006 solution as it only works in default configurations, but...
1008 static char disable_dnd_trans[] = "<Btn2Down>: ";
1009 #endif /* DND_KLUDGE */
1013 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1014 CONST char* shell_title, CONST char* icon_name,
1015 Boolean text_input_slot, Boolean radio_box, Boolean list,
1016 int left_buttons, int right_buttons)
1022 Widget icon_separator;
1027 Widget children [16]; /* for the final XtManageChildren */
1029 Arg al[64]; /* Arg List */
1030 int ac; /* Arg Count */
1034 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1035 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1036 #else /* ! DND_KLUDGE */
1037 # define DO_DND_KLUDGE(widget)
1038 #endif /* ! DND_KLUDGE */
1043 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1044 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1045 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1046 result = XmCreateDialogShell (parent, "dialog", al, ac);
1048 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1049 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1050 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1051 form = XmCreateForm (result, (char *) shell_title, al, ac);
1056 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1057 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1058 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1063 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1064 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1065 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1066 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1067 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1068 XtSetArg(al[ac], XmNspacing, 13); ac++;
1069 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1070 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1071 XtSetArg(al[ac], XmNisAligned, True); ac++;
1072 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1073 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1074 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1075 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1076 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1077 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1078 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1079 row = XmCreateRowColumn (form, "row", al, ac);
1082 for (i = 0; i < left_buttons; i++)
1084 char button_name [16];
1085 sprintf (button_name, "button%d", i + 1);
1089 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1090 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1092 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1093 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1094 DO_DND_KLUDGE (children [n_children]);
1098 button = children [n_children];
1100 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1101 XtSetValues (row, al, ac);
1103 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1105 XtTranslations losers = XtParseTranslationTable (button_trans);
1106 XtOverrideTranslations (button, losers);
1107 XtFree ((char *) losers);
1109 #endif /* ARMANDACTIVATE_KLUDGE */
1115 /* invisible seperator button */
1117 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1118 children [n_children] = XmCreateLabel (row, "separator_button",
1120 DO_DND_KLUDGE (children [n_children]);
1123 for (i = 0; i < right_buttons; i++)
1125 char button_name [16];
1126 sprintf (button_name, "button%d", left_buttons + i + 1);
1128 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1129 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1130 DO_DND_KLUDGE (children [n_children]);
1131 if (! button) button = children [n_children];
1135 XtManageChildren (children, n_children);
1138 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1139 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1140 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1141 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1142 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1143 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1144 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1145 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1146 separator = XmCreateSeparator (form, "", al, ac);
1149 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1150 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1151 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1152 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1153 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1154 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1155 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1156 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1157 DO_DND_KLUDGE (icon);
1160 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1161 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1162 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1163 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1164 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1165 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1166 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1167 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1168 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1169 icon_separator = XmCreateLabel (form, "", al, ac);
1170 DO_DND_KLUDGE (icon_separator);
1172 if (text_input_slot)
1175 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1176 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1177 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1178 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1179 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1180 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1181 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1182 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1183 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1184 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1185 value = XmCreateTextField (form, "value", al, ac);
1186 DO_DND_KLUDGE (value);
1192 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1193 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1194 XtSetArg(al[ac], XmNspacing, 13); ac++;
1195 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1196 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1197 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1198 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1199 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1200 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1201 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1202 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1203 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1204 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1205 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1208 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1209 children [i++] = radio_butt;
1210 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1211 children [i++] = radio_butt;
1212 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1213 children [i++] = radio_butt;
1214 XtManageChildren (children, i);
1219 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1220 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1221 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1222 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1223 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1224 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1225 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1226 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1227 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1228 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1229 value = XmCreateScrolledList (form, "list", al, ac);
1231 /* this is the easiest way I found to have the dble click in the
1232 list activate the default button */
1233 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1237 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1238 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1239 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1240 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1241 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1242 XtSetArg(al[ac], XmNbottomWidget,
1243 text_input_slot || radio_box || list ? value : separator); ac++;
1244 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1245 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1246 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1247 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1248 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1249 message = XmCreateLabel (form, "message", al, ac);
1250 DO_DND_KLUDGE (message);
1253 XtManageChild (value);
1256 children [i] = row; i++;
1257 children [i] = separator; i++;
1258 if (text_input_slot || radio_box)
1260 children [i] = value; i++;
1262 children [i] = message; i++;
1263 children [i] = icon; i++;
1264 children [i] = icon_separator; i++;
1265 XtManageChildren (children, i);
1267 if (text_input_slot || list)
1269 XtInstallAccelerators (value, button);
1270 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1274 XtInstallAccelerators (form, button);
1275 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1279 XtFree ((char *) dnd_override);
1281 #undef DO_DND_KLUDGE
1286 static destroyed_instance*
1287 find_matching_instance (widget_instance* instance)
1289 destroyed_instance* cur;
1290 destroyed_instance* prev;
1291 char* type = instance->info->type;
1292 char* name = instance->info->name;
1294 for (prev = NULL, cur = all_destroyed_instances;
1296 prev = cur, cur = cur->next)
1298 if (!strcmp (cur->name, name)
1299 && !strcmp (cur->type, type)
1300 && cur->parent == instance->parent
1301 && cur->pop_up_p == instance->pop_up_p)
1304 prev->next = cur->next;
1306 all_destroyed_instances = cur->next;
1309 /* do some cleanup */
1310 else if (!cur->widget)
1313 prev->next = cur->next;
1315 all_destroyed_instances = cur->next;
1316 free_destroyed_instance (cur);
1317 cur = prev ? prev : all_destroyed_instances;
1324 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
1325 XtPointer call_data)
1327 destroyed_instance* instance = (destroyed_instance*)closure;
1328 instance->widget = NULL;
1332 recenter_widget (Widget widget)
1334 Widget parent = XtParent (widget);
1335 Screen* screen = XtScreen (widget);
1336 Dimension screen_width = WidthOfScreen (screen);
1337 Dimension screen_height = HeightOfScreen (screen);
1338 Dimension parent_width = 0;
1339 Dimension parent_height = 0;
1340 Dimension child_width = 0;
1341 Dimension child_height = 0;
1346 XtSetArg (al [0], XtNwidth, &child_width);
1347 XtSetArg (al [1], XtNheight, &child_height);
1348 XtGetValues (widget, al, 2);
1350 XtSetArg (al [0], XtNwidth, &parent_width);
1351 XtSetArg (al [1], XtNheight, &parent_height);
1352 XtGetValues (parent, al, 2);
1354 x = (Position) ((parent_width - child_width) / 2);
1355 y = (Position) ((parent_height - child_height) / 2);
1357 XtTranslateCoords (parent, x, y, &x, &y);
1359 if ((Dimension) (x + child_width) > screen_width)
1360 x = screen_width - child_width;
1364 if ((Dimension) (y + child_height) > screen_height)
1365 y = screen_height - child_height;
1369 XtSetArg (al [0], XtNx, x);
1370 XtSetArg (al [1], XtNy, y);
1371 XtSetValues (widget, al, 2);
1375 recycle_instance (destroyed_instance* instance)
1377 Widget widget = instance->widget;
1379 /* widget is NULL if the parent was destroyed. */
1385 /* Remove the destroy callback as the instance is not in the list
1387 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1388 mark_dead_instance_destroyed,
1389 (XtPointer)instance);
1391 /* Give the focus to the initial item */
1392 focus = XtNameToWidget (widget, "*value");
1394 focus = XtNameToWidget (widget, "*button1");
1396 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1398 /* shrink the separator label back to their original size */
1399 separator = XtNameToWidget (widget, "*separator_button");
1403 XtSetArg (al [0], XtNwidth, 5);
1404 XtSetArg (al [1], XtNheight, 5);
1405 XtSetValues (separator, al, 2);
1408 /* Center the dialog in its parent */
1409 recenter_widget (widget);
1411 free_destroyed_instance (instance);
1416 xm_create_dialog (widget_instance* instance)
1418 char* name = instance->info->type;
1419 Widget parent = instance->parent;
1421 Boolean pop_up_p = instance->pop_up_p;
1422 CONST char* shell_name = 0;
1423 CONST char* icon_name = 0;
1424 Boolean text_input_slot = False;
1425 Boolean radio_box = False;
1426 Boolean list = False;
1428 int left_buttons = 0;
1429 int right_buttons = 1;
1430 destroyed_instance* dead_one;
1432 /* try to find a widget to recycle */
1433 dead_one = find_matching_instance (instance);
1436 Widget recycled_widget = recycle_instance (dead_one);
1437 if (recycled_widget)
1438 return recycled_widget;
1443 icon_name = "dbox-error";
1444 shell_name = "Error";
1448 icon_name = "dbox-info";
1449 shell_name = "Information";
1454 icon_name = "dbox-question";
1455 shell_name = "Prompt";
1459 text_input_slot = True;
1460 icon_name = "dbox-question";
1461 shell_name = "Prompt";
1465 icon_name = "dbox-question";
1466 shell_name = "Question";
1470 total_buttons = name [1] - '0';
1472 if (name [3] == 'T' || name [3] == 't')
1474 text_input_slot = False;
1478 right_buttons = name [4] - '0';
1480 left_buttons = total_buttons - right_buttons;
1482 widget = make_dialog (name, parent, pop_up_p,
1483 shell_name, icon_name, text_input_slot, radio_box,
1484 list, left_buttons, right_buttons);
1486 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1487 (XtPointer) instance);
1491 #endif /* LWLIB_DIALOGS_MOTIF */
1493 #ifdef LWLIB_MENUBARS_MOTIF
1495 make_menubar (widget_instance* instance)
1500 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1501 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1503 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1507 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1509 Widget menu = (Widget) closure;
1510 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1514 make_popup_menu (widget_instance* instance)
1516 Widget parent = instance->parent;
1517 Window parent_window = parent->core.window;
1520 /* sets the parent window to 0 to fool Motif into not generating a grab */
1521 parent->core.window = 0;
1522 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1523 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1525 parent->core.window = parent_window;
1528 #endif /* LWLIB_MENUBARS_MOTIF */
1530 #ifdef LWLIB_SCROLLBARS_MOTIF
1532 make_scrollbar (widget_instance *instance, int vertical)
1536 static XtCallbackRec callbacks[2] =
1537 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1539 callbacks[0].closure = (XtPointer) instance;
1541 XtSetArg (al[ac], XmNminimum, 1); ac++;
1542 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1543 XtSetArg (al[ac], XmNincrement, 1); ac++;
1544 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1545 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1546 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1548 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1549 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1550 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1551 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1552 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1553 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1554 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1555 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1557 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1561 make_vertical_scrollbar (widget_instance *instance)
1563 return make_scrollbar (instance, 1);
1567 make_horizontal_scrollbar (widget_instance *instance)
1569 return make_scrollbar (instance, 0);
1572 #endif /* LWLIB_SCROLLBARS_MOTIF */
1574 \f/* Table of functions to create widgets */
1576 widget_creation_entry
1577 xm_creation_table [] =
1579 #ifdef LWLIB_MENUBARS_MOTIF
1580 {"menubar", make_menubar},
1581 {"popup", make_popup_menu},
1583 #ifdef LWLIB_SCROLLBARS_MOTIF
1584 {"vertical-scrollbar", make_vertical_scrollbar},
1585 {"horizontal-scrollbar", make_horizontal_scrollbar},
1590 \f/* Destruction of instances */
1592 xm_destroy_instance (widget_instance* instance)
1594 #ifdef LWLIB_DIALOGS_MOTIF
1595 /* It appears that this is used only for dialog boxes. */
1596 Widget widget = instance->widget;
1597 /* recycle the dialog boxes */
1598 /* Disable the recycling until we can find a way to have the dialog box
1599 get reasonable layout after we modify its contents. */
1601 && XtClass (widget) == xmDialogShellWidgetClass)
1603 destroyed_instance* dead_instance =
1604 make_destroyed_instance (instance->info->name,
1605 instance->info->type,
1608 instance->pop_up_p);
1609 dead_instance->next = all_destroyed_instances;
1610 all_destroyed_instances = dead_instance;
1611 XtUnmanageChild (first_child (instance->widget));
1612 XFlush (XtDisplay (instance->widget));
1613 XtAddCallback (instance->parent, XtNdestroyCallback,
1614 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1618 /* This might not be necessary now that the nosel is attached to
1619 popdown instead of destroy, but it can't hurt. */
1620 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1621 xm_nosel_callback, (XtPointer)instance);
1623 XtDestroyWidget (instance->widget);
1625 #endif /* LWLIB_DIALOGS_MOTIF */
1628 \f/* popup utility */
1629 #ifdef LWLIB_MENUBARS_MOTIF
1632 xm_popup_menu (Widget widget, XEvent *event)
1634 if (event->type == ButtonPress || event->type == ButtonRelease)
1636 /* This is so totally ridiculous: there's NO WAY to tell Motif
1637 that *any* button can select a menu item. Only one button
1638 can have that honor.
1641 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1642 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1643 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1644 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1645 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1649 XtSetArg (al [0], XmNmenuPost, trans);
1650 XtSetValues (widget, al, 1);
1652 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1654 XtManageChild (widget);
1659 #ifdef LWLIB_DIALOGS_MOTIF
1662 set_min_dialog_size (Widget w)
1668 XtSetArg (al [0], XmNwidth, &width);
1669 XtSetArg (al [1], XmNheight, &height);
1670 XtGetValues (w, al, 2);
1672 XtSetArg (al [0], XmNminWidth, width);
1673 XtSetArg (al [1], XmNminHeight, height);
1674 XtSetValues (w, al, 2);
1680 xm_pop_instance (widget_instance* instance, Boolean up)
1682 Widget widget = instance->widget;
1684 #ifdef LWLIB_DIALOGS_MOTIF
1685 if (XtClass (widget) == xmDialogShellWidgetClass)
1687 Widget widget_to_manage = first_child (widget);
1690 XtManageChild (widget_to_manage);
1691 set_min_dialog_size (widget);
1692 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1695 XtUnmanageChild (widget_to_manage);
1701 XtManageChild (widget);
1703 XtUnmanageChild (widget);
1708 /* motif callback */
1710 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1713 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1715 XtPointer user_data;
1716 widget_instance* instance = (widget_instance*)closure;
1717 Widget instance_widget;
1723 if (widget->core.being_destroyed)
1726 instance_widget = instance->widget;
1727 if (!instance_widget)
1730 id = instance->info->id;
1732 XtSetArg(al [0], XmNuserData, &user_data);
1733 XtGetValues (widget, al, 1);
1737 if (instance->info->pre_activate_cb)
1738 instance->info->pre_activate_cb (widget, id, user_data);
1741 if (instance->info->selection_cb)
1742 instance->info->selection_cb (widget, id, user_data);
1745 if (instance->info->selection_cb)
1746 instance->info->selection_cb (widget, id, (XtPointer) -1);
1749 if (instance->info->post_activate_cb)
1750 instance->info->post_activate_cb (widget, id, user_data);
1757 /* Like lw_internal_update_other_instances except that it does not do
1758 anything if its shell parent is not managed. This is to protect
1759 lw_internal_update_other_instances to dereference freed memory
1760 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1763 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1764 XtPointer call_data)
1767 for (parent = widget; parent; parent = XtParent (parent))
1768 if (XtIsShell (parent))
1770 else if (!XtIsManaged (parent))
1772 lw_internal_update_other_instances (widget, closure, call_data);
1776 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1778 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF))
1779 /* We want the selected status to change only when we decide it
1780 should change. Yuck but correct. */
1781 if (XtClass (widget) == xmToggleButtonWidgetClass
1782 || XtClass (widget) == xmToggleButtonGadgetClass)
1787 XtSetArg (al [0], XmNset, &check);
1788 XtGetValues (widget, al, 1);
1790 XtSetArg (al [0], XmNset, !check);
1791 XtSetValues (widget, al, 1);
1794 lw_internal_update_other_instances (widget, closure, call_data);
1795 do_call (widget, closure, selection);
1798 #ifdef LWLIB_DIALOGS_MOTIF
1801 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
1803 /* This callback is only called when a dialog box is dismissed with the wm's
1804 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
1805 in that case, not just unmapped, so that it releases its keyboard grabs.
1806 But there are problems with running our callbacks while the widget is in
1807 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
1808 instead of XmDESTROY and then destroy it ourself after having run the
1811 do_call (widget, closure, no_selection);
1812 XtDestroyWidget (widget);
1817 #ifdef LWLIB_MENUBARS_MOTIF
1820 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1825 /* new behavior for incremental menu construction */
1830 do_call (widget, closure, pre_activate);
1835 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1837 do_call (widget, closure, post_activate);
1841 #endif /* LWLIB_MENUBARS_MOTIF */
1843 #ifdef LWLIB_SCROLLBARS_MOTIF
1845 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
1847 widget_instance *instance = (widget_instance *) closure;
1849 XmScrollBarCallbackStruct *data =
1850 (XmScrollBarCallbackStruct *) call_data;
1851 scroll_event event_data;
1852 scrollbar_values *val =
1853 (scrollbar_values *) instance->info->val->scrollbar_data;
1856 if (!instance || widget->core.being_destroyed)
1859 id = instance->info->id;
1861 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
1862 event_data.slider_value =
1863 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
1865 if (event_data.slider_value > (val->maximum - val->slider_size))
1866 event_data.slider_value = val->maximum - val->slider_size;
1867 else if (event_data.slider_value < 1)
1868 event_data.slider_value = 1;
1872 switch (data->event->xany.type)
1876 event_data.time = data->event->xkey.time;
1880 event_data.time = data->event->xbutton.time;
1883 event_data.time = data->event->xmotion.time;
1887 event_data.time = data->event->xcrossing.time;
1890 event_data.time = 0;
1895 event_data.time = 0;
1897 switch (data->reason)
1899 case XmCR_DECREMENT:
1900 event_data.action = SCROLLBAR_LINE_UP;
1902 case XmCR_INCREMENT:
1903 event_data.action = SCROLLBAR_LINE_DOWN;
1905 case XmCR_PAGE_DECREMENT:
1906 event_data.action = SCROLLBAR_PAGE_UP;
1908 case XmCR_PAGE_INCREMENT:
1909 event_data.action = SCROLLBAR_PAGE_DOWN;
1912 event_data.action = SCROLLBAR_TOP;
1914 case XmCR_TO_BOTTOM:
1915 event_data.action = SCROLLBAR_BOTTOM;
1918 event_data.action = SCROLLBAR_DRAG;
1920 case XmCR_VALUE_CHANGED:
1921 event_data.action = SCROLLBAR_CHANGE;
1924 event_data.action = SCROLLBAR_CHANGE;
1928 if (instance->info->pre_activate_cb)
1929 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
1931 #endif /* LWLIB_SCROLLBARS_MOTIF */
1934 /* set the keyboard focus */
1936 xm_set_keyboard_focus (Widget parent, Widget w)
1938 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
1939 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
1940 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
1941 Presumably the problem was elsewhere, and is now gone...