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>
65 #include <Xm/ComboBox.h>
68 #ifdef LWLIB_MENUBARS_MOTIF
69 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
71 static void xm_internal_update_other_instances (Widget, XtPointer,
73 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
74 static void xm_generic_callback (Widget, XtPointer, XtPointer);
75 #ifdef LWLIB_DIALOGS_MOTIF
76 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
78 #ifdef LWLIB_SCROLLBARS_MOTIF
79 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
82 #ifdef LWLIB_MENUBARS_MOTIF
84 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
88 \f/* Structures to keep destroyed instances */
89 typedef struct _destroyed_instance
96 struct _destroyed_instance* next;
99 static destroyed_instance*
100 all_destroyed_instances = NULL;
102 /* Utility function. */
104 safe_strdup (char* s)
108 result = (char *) malloc (strlen (s) + 1);
115 static destroyed_instance*
116 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
119 destroyed_instance* instance =
120 (destroyed_instance*) malloc (sizeof (destroyed_instance));
121 instance->name = safe_strdup (name);
122 instance->type = safe_strdup (type);
123 instance->widget = widget;
124 instance->parent = parent;
125 instance->pop_up_p = pop_up_p;
126 instance->next = NULL;
131 free_destroyed_instance (destroyed_instance* instance)
133 free (instance->name);
134 free (instance->type);
138 \f/* motif utility functions */
140 first_child (Widget widget)
142 return ((CompositeWidget)widget)->composite.children [0];
146 lw_motif_widget_p (Widget widget)
149 #ifdef LWLIB_DIALOGS_MOTIF
150 XtClass (widget) == xmDialogShellWidgetClass ||
152 XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
156 resource_string (Widget widget, char *name)
161 resource.resource_name = "labelString";
162 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
163 resource.resource_type = XtRString;
164 resource.resource_size = sizeof (String);
165 resource.resource_offset = 0;
166 resource.default_type = XtRImmediate;
167 resource.default_addr = 0;
169 XtGetSubresources (widget, (XtPointer)&result, name,
170 name, &resource, 1, NULL, 0);
174 #ifdef LWLIB_MENUBARS_MOTIF
177 destroy_all_children (Widget widget)
183 children = XtCompositeChildren (widget, &number);
186 /* Unmanage all children and destroy them. They will only be
187 * really destroyed when we get out of DispatchEvent. */
188 for (i = 0; i < number; i++)
190 Widget child = children [i];
191 if (!child->core.being_destroyed)
193 XtUnmanageChild (child);
194 XtDestroyWidget (child);
197 XtFree ((char *) children);
201 #endif /* LWLIB_MENUBARS_MOTIF */
205 #ifdef LWLIB_DIALOGS_MOTIF
208 is_in_dialog_box (Widget w)
212 wmshell = XtParent (w);
213 while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
214 wmshell = XtParent (wmshell);
216 if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
222 #endif /* LWLIB_DIALOGS_MOTIF */
224 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF)
226 /* update the label of anything subclass of a label */
228 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
230 XmString built_string = NULL;
231 XmString key_string = NULL;
232 XmString val_string = NULL;
233 XmString name_string = NULL;
239 #ifdef LWLIB_DIALOGS_MOTIF
241 * Sigh. The main text of a label is the name field for menubar
242 * entries. The value field is a possible additional field to be
243 * concatenated on to the name field. HOWEVER, with dialog boxes
244 * the value field is the complete text which is supposed to be
245 * displayed as the label. Yuck.
247 if (is_in_dialog_box (widget))
249 char *value_name = NULL;
251 value_name = resource_string (widget, val->value);
253 value_name = val->value;
256 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
259 #endif /* LWLIB_DIALOGS_MOTIF */
261 char *value_name = NULL;
262 char *res_name = NULL;
264 res_name = resource_string (widget, val->name);
266 res_name = val->name;
269 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
271 value_name = XtMalloc (strlen (val->value) + 2);
273 strcat (value_name, " ");
274 strcat (value_name, val->value);
277 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
280 XmStringConcat (name_string, val_string);
285 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
286 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
291 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
292 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
296 XtSetValues (widget, al, ac);
299 XmStringFree (built_string);
302 XmStringFree (key_string);
305 XmStringFree (name_string);
308 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
310 \f/* update of list */
312 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
316 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
317 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
319 for (cur = val->contents, i = 0; cur; cur = cur->next)
322 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
324 XmListAddItem (widget, xmstr, 0);
326 XmListSelectPos (widget, i, False);
327 XmStringFree (xmstr);
331 \f/* update of buttons */
333 xm_update_pushbutton (widget_instance* instance, Widget widget,
337 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
338 XtSetValues (widget, al, 1);
339 XtRemoveAllCallbacks (widget, XmNactivateCallback);
340 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
343 #ifdef LWLIB_MENUBARS_MOTIF
346 xm_update_cascadebutton (widget_instance* instance, Widget widget,
349 /* Should also rebuild the menu by calling ...update_menu... */
351 && val->type == CASCADE_TYPE
353 && val->contents->type == INCREMENTAL_TYPE)
355 /* okay, we're now doing a lisp callback to incrementally generate
357 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
358 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
360 XtCallCallbacks ((Widget)widget,
361 XmNcascadingCallback,
362 (XtPointer)val->contents);
365 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
366 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
371 #endif /* LWLIB_MENUBARS_MOTIF */
373 \f/* update toggle and radiobox */
375 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
378 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
379 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
381 XtSetArg (al [0], XmNset, val->selected);
382 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
383 XtSetValues (widget, al, 2);
387 xm_update_radiobox (widget_instance* instance, Widget widget,
393 /* update the callback */
394 XtRemoveAllCallbacks (widget, XmNentryCallback);
395 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
397 /* first update all the toggles */
398 /* Energize kernel interface is currently bad. It sets the selected widget
399 with the selected flag but returns it by its name. So we currently
400 have to support both setting the selection with the selected slot
401 of val contents and setting it with the "value" slot of val. The latter
402 has a higher priority. This to be removed when the kernel is fixed. */
403 for (cur = val->contents; cur; cur = cur->next)
405 toggle = XtNameToWidget (widget, cur->value);
409 XtSetArg (al [0], XmNsensitive, cur->enabled);
410 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
411 XtSetValues (toggle, al, 2);
415 /* The selected was specified by the value slot */
418 toggle = XtNameToWidget (widget, val->value);
422 XtSetArg (al [0], XmNset, True);
423 XtSetValues (toggle, al, 1);
428 #ifdef LWLIB_MENUBARS_MOTIF
430 \f/* update a popup menu, pulldown menu or a menubar */
432 make_menu_in_widget (widget_instance* instance, Widget widget,
435 Widget* children = 0;
443 Boolean menubar_p = False;
445 /* Allocate the children array */
446 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
447 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
449 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
450 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
451 XtGetValues (widget, al, 1);
453 /* add the unmap callback for popups and pulldowns */
454 /*** this sounds bogus ***/
455 /* probably because it is -- cet */
458 XtAddCallback (XtParent (widget), XmNpopdownCallback,
459 xm_pop_down_callback, (XtPointer)instance);
463 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
467 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
468 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
469 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
474 /* A pushright marker which is not needed for the real Motif
481 /* #### - xlwmenu.h supports several types that motif does
482 not. Also, motif supports pixmaps w/ type NO_LINE and
483 lwlib provides no way to access that functionality. --Stig */
484 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
486 button = XmCreateSeparator (widget, "separator", al, ac);
489 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
490 make_menu_in_widget (instance, menu, cur->contents);
491 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
492 button = XmCreateCascadeButton (widget, cur->name, al, ac);
494 xm_update_label (instance, button, cur);
496 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
497 (XtPointer)instance);
501 button = XmCreateCascadeButton (widget, cur->name, al, ac);
502 else if (!cur->call_data)
503 button = XmCreateLabel (widget, cur->name, al, ac);
504 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
506 XtSetArg (al [ac], XmNindicatorType,
507 (cur->type == TOGGLE_TYPE ?
508 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
509 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
510 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
513 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
515 xm_update_label (instance, button, cur);
517 /* don't add a callback to a simple label */
518 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
519 xm_update_toggle (instance, button, cur);
520 else if (cur->call_data)
521 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
522 (XtPointer)instance);
523 } /* switch (cur->type) */
526 children [num_children++] = button;
529 /* Last entry is the help button. This used be done after managing
530 the buttons. The comment claimed that it had to be done this way
531 otherwise the menubar ended up only 4 pixels high. That must
532 have been in the Old World. In the New World it stays the proper
533 height if you don't manage them until after you set this and as a
534 bonus the Help menu ends up where it is supposed to. */
538 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
539 XtSetValues (widget, al, ac);
543 XtManageChildren (children, num_children);
545 XtFree ((char *) children);
549 update_one_menu_entry (widget_instance* instance, Widget widget,
550 widget_value* val, Boolean deep_p)
555 widget_value* contents;
557 if (val->change == NO_CHANGE)
560 /* update the sensitivity and userdata */
561 /* Common to all widget types */
562 XtSetArg (al [0], XmNsensitive, val->enabled);
563 XtSetArg (al [1], XmNuserData, val->call_data);
564 XtSetValues (widget, al, 2);
566 /* update the menu button as a label. */
567 if (val->change >= VISIBLE_CHANGE)
569 xm_update_label (instance, widget, val);
570 if (XtClass (widget) == xmToggleButtonWidgetClass
571 || XtClass (widget) == xmToggleButtonGadgetClass)
573 xm_update_toggle (instance, widget, val);
578 /* update the pulldown/pullaside as needed */
580 XtSetArg (al [0], XmNsubMenuId, &menu);
581 XtGetValues (widget, al, 1);
583 contents = val->contents;
589 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
590 make_menu_in_widget (instance, menu, contents);
592 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
593 XtSetValues (widget, al, ac);
599 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
600 XtSetValues (widget, al, ac);
601 XtDestroyWidget (menu);
603 else if (deep_p && contents->change != NO_CHANGE)
604 xm_update_menu (instance, menu, val, 1);
608 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
611 /* Widget is a RowColumn widget whose contents have to be updated
612 * to reflect the list of items in val->contents */
613 if (val->contents->change == STRUCTURAL_CHANGE)
615 destroy_all_children (widget);
616 make_menu_in_widget (instance, widget, val->contents);
620 /* Update all the buttons of the RowColumn in order. */
622 unsigned int num_children;
624 widget_value *cur = 0;
626 children = XtCompositeChildren (widget, &num_children);
629 for (i = 0, cur = val->contents; i < num_children; i++)
633 /* skip if this is a pushright marker or a separator */
634 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
638 /* #### - this could puke if you have a separator as the
639 last item on a pullright menu. */
647 if (children [i]->core.being_destroyed
648 || strcmp (XtName (children [i]), cur->name))
650 update_one_menu_entry (instance, children [i], cur, deep_p);
653 XtFree ((char *) children);
660 #endif /* LWLIB_MENUBARS_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);
689 #ifdef LWLIB_SCROLLBARS_MOTIF
692 * If this function looks like it does a lot more work than it needs to,
693 * you're right. Blame the Motif scrollbar for not being smart about
694 * updating its appearance.
697 xm_update_scrollbar (widget_instance *instance, Widget widget,
700 if (val->scrollbar_data)
702 scrollbar_values *data = val->scrollbar_data;
703 int widget_sliderSize, widget_val;
704 int new_sliderSize, new_value;
706 double h_water, l_water;
709 /* First size and position the scrollbar widget. */
710 XtSetArg (al [0], XtNx, data->scrollbar_x);
711 XtSetArg (al [1], XtNy, data->scrollbar_y);
712 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
713 XtSetArg (al [3], XtNheight, data->scrollbar_height);
714 XtSetValues (widget, al, 4);
716 /* Now size the scrollbar's slider. */
717 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
718 XtSetArg (al [1], XmNvalue, &widget_val);
719 XtGetValues (widget, al, 2);
721 percent = (double) data->slider_size /
722 (double) (data->maximum - data->minimum);
723 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
725 percent = (double) (data->slider_position - data->minimum) /
726 (double) (data->maximum - data->minimum);
727 new_value = (int) ((double) (INT_MAX - 1) * percent);
729 if (new_sliderSize > (INT_MAX - 1))
730 new_sliderSize = INT_MAX - 1;
731 else if (new_sliderSize < 1)
734 if (new_value > (INT_MAX - new_sliderSize))
735 new_value = INT_MAX - new_sliderSize;
736 else if (new_value < 1)
741 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
743 int force = ((INT_MAX - widget_sliderSize - widget_val)
745 : (INT_MAX - new_sliderSize - new_value));
748 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
749 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
750 || (double)new_value < (l_water * (double)widget_val)
751 || (double)new_value > (h_water * (double)widget_val))
753 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
760 #endif /* LWLIB_SCROLLBARS_MOTIF */
763 /* update a motif widget */
766 xm_update_one_widget (widget_instance* instance, Widget widget,
767 widget_value* val, Boolean deep_p)
772 /* Mark as not edited */
775 /* Common to all widget types */
776 XtSetArg (al [0], XmNsensitive, val->enabled);
777 XtSetArg (al [1], XmNuserData, val->call_data);
778 XtSetValues (widget, al, 2);
780 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF)
781 /* Common to all label like widgets */
782 if (XtIsSubclass (widget, xmLabelWidgetClass))
783 xm_update_label (instance, widget, val);
784 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
786 class = XtClass (widget);
787 /* Class specific things */
788 if (class == xmPushButtonWidgetClass ||
789 class == xmArrowButtonWidgetClass)
791 xm_update_pushbutton (instance, widget, val);
793 #ifdef LWLIB_MENUBARS_MOTIF
794 else if (class == xmCascadeButtonWidgetClass)
796 xm_update_cascadebutton (instance, widget, val);
799 else if (class == xmToggleButtonWidgetClass
800 || class == xmToggleButtonGadgetClass)
802 xm_update_toggle (instance, widget, val);
804 else if (class == xmRowColumnWidgetClass)
806 Boolean radiobox = 0;
808 XtSetArg (al [0], XmNradioBehavior, &radiobox);
809 XtGetValues (widget, al, 1);
812 xm_update_radiobox (instance, widget, val);
813 #ifdef LWLIB_MENUBARS_MOTIF
815 xm_update_menu (instance, widget, val, deep_p);
818 else if (class == xmTextWidgetClass)
820 xm_update_text (instance, widget, val);
822 else if (class == xmTextFieldWidgetClass)
824 xm_update_text_field (instance, widget, val);
826 else if (class == xmListWidgetClass)
828 xm_update_list (instance, widget, val);
830 #ifdef LWLIB_SCROLLBARS_MOTIF
831 else if (class == xmScrollBarWidgetClass)
833 xm_update_scrollbar (instance, widget, val);
838 \f/* getting the value back */
840 xm_update_one_value (widget_instance* instance, Widget widget,
843 WidgetClass class = XtClass (widget);
844 widget_value *old_wv;
846 /* copy the call_data slot into the "return" widget_value */
847 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
848 if (!strcmp (val->name, old_wv->name))
850 val->call_data = old_wv->call_data;
854 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
857 XtSetArg (al [0], XmNset, &val->selected);
858 XtGetValues (widget, al, 1);
861 else if (class == xmTextWidgetClass)
865 val->value = XmTextGetString (widget);
868 else if (class == xmTextFieldWidgetClass)
872 val->value = XmTextFieldGetString (widget);
875 else if (class == xmRowColumnWidgetClass)
877 Boolean radiobox = 0;
880 XtSetArg (al [0], XmNradioBehavior, &radiobox);
881 XtGetValues (widget, al, 1);
886 CompositeWidget radio = (CompositeWidget)widget;
888 for (i = 0; i < radio->composite.num_children; i++)
891 Widget toggle = radio->composite.children [i];
894 XtSetArg (al [0], XmNset, &set);
895 XtGetValues (toggle, al, 1);
900 val->value = safe_strdup (XtName (toggle));
906 else if (class == xmListWidgetClass)
910 if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
914 for (cur = val->contents, i = 0; cur; cur = cur->next)
918 cur->selected = False;
920 for (j = 0; j < pos_cnt; j++)
921 if (pos_list [j] == i)
923 cur->selected = True;
924 val->value = safe_strdup (cur->name);
928 XtFree ((char *) pos_list);
931 #ifdef LWLIB_SCROLLBARS_MOTIF
932 else if (class == xmScrollBarWidgetClass)
934 /* This function is not used by the scrollbar. */
941 /* This function is for activating a button from a program. It's wrong because
942 we pass a NULL argument in the call_data which is not Motif compatible.
943 This is used from the XmNdefaultAction callback of the List widgets to
944 have a double-click put down a dialog box like the button would do.
945 I could not find a way to do that with accelerators.
948 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
950 Widget button = (Widget)closure;
951 XtCallCallbacks (button, XmNactivateCallback, NULL);
954 /* creation functions */
956 #ifdef LWLIB_DIALOGS_MOTIF
960 #if (XmVersion >= 1002)
961 # define ARMANDACTIVATE_KLUDGE
965 #ifdef ARMANDACTIVATE_KLUDGE
966 /* We want typing Return at a dialog box to select the default button; but
967 we're satisfied with having it select the leftmost button instead.
969 In Motif 1.1.5 we could do this by putting this resource in the
972 *dialog*button1.accelerators:#override\
973 <KeyPress>Return: ArmAndActivate()\n\
974 <KeyPress>KP_Enter: ArmAndActivate()\n\
975 Ctrl<KeyPress>m: ArmAndActivate()\n
977 but that doesn't work with 1.2.1 and I don't understand why. However,
978 doing the equivalent C code does work, with the notable disadvantage that
979 the user can't override it. So that's what we do until we figure out
982 static char button_trans[] = "\
983 <KeyPress>Return: ArmAndActivate()\n\
984 <KeyPress>KP_Enter: ArmAndActivate()\n\
985 Ctrl<KeyPress>m: ArmAndActivate()\n";
987 #endif /* ARMANDACTIVATE_KLUDGE */
991 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
992 was a segv down in libXm somewhere if you used the middle button on a
993 dialog box to begin a drag; when you released the button to make a drop
994 things would lose if you were not over the button where you started the
995 drag (canceling the operation). This was probably due to the fact that
996 the dialog boxes were not set up to handle a drag but were trying to do
997 so anyway for some reason.
999 So we disable drag-and-drop in dialog boxes by turning off the binding for
1000 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1001 solution as it only works in default configurations, but...
1003 static char disable_dnd_trans[] = "<Btn2Down>: ";
1004 #endif /* DND_KLUDGE */
1008 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1009 CONST char* shell_title, CONST char* icon_name,
1010 Boolean text_input_slot, Boolean radio_box, Boolean list,
1011 int left_buttons, int right_buttons)
1017 Widget icon_separator;
1022 Widget children [16]; /* for the final XtManageChildren */
1024 Arg al[64]; /* Arg List */
1025 int ac; /* Arg Count */
1029 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1030 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1031 #else /* ! DND_KLUDGE */
1032 # define DO_DND_KLUDGE(widget)
1033 #endif /* ! DND_KLUDGE */
1038 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1039 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1040 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1041 result = XmCreateDialogShell (parent, "dialog", al, ac);
1043 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1044 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1045 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1046 form = XmCreateForm (result, (char *) shell_title, al, ac);
1051 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1052 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1053 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1058 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1059 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1060 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1061 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1062 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1063 XtSetArg(al[ac], XmNspacing, 13); ac++;
1064 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1065 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1066 XtSetArg(al[ac], XmNisAligned, True); ac++;
1067 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1068 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1069 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1070 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1071 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1072 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1073 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1074 row = XmCreateRowColumn (form, "row", al, ac);
1077 for (i = 0; i < left_buttons; i++)
1079 char button_name [16];
1080 sprintf (button_name, "button%d", i + 1);
1084 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1085 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1087 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1088 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1089 DO_DND_KLUDGE (children [n_children]);
1093 button = children [n_children];
1095 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1096 XtSetValues (row, al, ac);
1098 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1100 XtTranslations losers = XtParseTranslationTable (button_trans);
1101 XtOverrideTranslations (button, losers);
1102 XtFree ((char *) losers);
1104 #endif /* ARMANDACTIVATE_KLUDGE */
1110 /* invisible seperator button */
1112 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1113 children [n_children] = XmCreateLabel (row, "separator_button",
1115 DO_DND_KLUDGE (children [n_children]);
1118 for (i = 0; i < right_buttons; i++)
1120 char button_name [16];
1121 sprintf (button_name, "button%d", left_buttons + i + 1);
1123 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1124 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1125 DO_DND_KLUDGE (children [n_children]);
1126 if (! button) button = children [n_children];
1130 XtManageChildren (children, n_children);
1133 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1134 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1135 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1136 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1137 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1138 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1139 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1140 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1141 separator = XmCreateSeparator (form, "", al, ac);
1144 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1145 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1146 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1147 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1148 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1149 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1150 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1151 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1152 DO_DND_KLUDGE (icon);
1155 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1156 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1157 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1158 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1159 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1160 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1161 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1162 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1163 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1164 icon_separator = XmCreateLabel (form, "", al, ac);
1165 DO_DND_KLUDGE (icon_separator);
1167 if (text_input_slot)
1170 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1171 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1172 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1173 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1174 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1175 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1176 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1177 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1178 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1179 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1180 value = XmCreateTextField (form, "value", al, ac);
1181 DO_DND_KLUDGE (value);
1187 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1188 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1189 XtSetArg(al[ac], XmNspacing, 13); ac++;
1190 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1191 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1192 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1193 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1194 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1195 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1196 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1197 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1198 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1199 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1200 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1203 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1204 children [i++] = radio_butt;
1205 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1206 children [i++] = radio_butt;
1207 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1208 children [i++] = radio_butt;
1209 XtManageChildren (children, i);
1214 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1215 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1216 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1217 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1218 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1219 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1220 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1221 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1222 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1223 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1224 value = XmCreateScrolledList (form, "list", al, ac);
1226 /* this is the easiest way I found to have the dble click in the
1227 list activate the default button */
1228 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1232 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1233 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1234 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1235 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1236 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1237 XtSetArg(al[ac], XmNbottomWidget,
1238 text_input_slot || radio_box || list ? value : separator); ac++;
1239 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1240 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1241 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1242 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1243 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1244 message = XmCreateLabel (form, "message", al, ac);
1245 DO_DND_KLUDGE (message);
1248 XtManageChild (value);
1251 children [i] = row; i++;
1252 children [i] = separator; i++;
1253 if (text_input_slot || radio_box)
1255 children [i] = value; i++;
1257 children [i] = message; i++;
1258 children [i] = icon; i++;
1259 children [i] = icon_separator; i++;
1260 XtManageChildren (children, i);
1262 if (text_input_slot || list)
1264 XtInstallAccelerators (value, button);
1265 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1269 XtInstallAccelerators (form, button);
1270 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1274 XtFree ((char *) dnd_override);
1276 #undef DO_DND_KLUDGE
1281 static destroyed_instance*
1282 find_matching_instance (widget_instance* instance)
1284 destroyed_instance* cur;
1285 destroyed_instance* prev;
1286 char* type = instance->info->type;
1287 char* name = instance->info->name;
1289 for (prev = NULL, cur = all_destroyed_instances;
1291 prev = cur, cur = cur->next)
1293 if (!strcmp (cur->name, name)
1294 && !strcmp (cur->type, type)
1295 && cur->parent == instance->parent
1296 && cur->pop_up_p == instance->pop_up_p)
1299 prev->next = cur->next;
1301 all_destroyed_instances = cur->next;
1304 /* do some cleanup */
1305 else if (!cur->widget)
1308 prev->next = cur->next;
1310 all_destroyed_instances = cur->next;
1311 free_destroyed_instance (cur);
1312 cur = prev ? prev : all_destroyed_instances;
1319 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
1320 XtPointer call_data)
1322 destroyed_instance* instance = (destroyed_instance*)closure;
1323 instance->widget = NULL;
1327 recenter_widget (Widget widget)
1329 Widget parent = XtParent (widget);
1330 Screen* screen = XtScreen (widget);
1331 Dimension screen_width = WidthOfScreen (screen);
1332 Dimension screen_height = HeightOfScreen (screen);
1333 Dimension parent_width = 0;
1334 Dimension parent_height = 0;
1335 Dimension child_width = 0;
1336 Dimension child_height = 0;
1341 XtSetArg (al [0], XtNwidth, &child_width);
1342 XtSetArg (al [1], XtNheight, &child_height);
1343 XtGetValues (widget, al, 2);
1345 XtSetArg (al [0], XtNwidth, &parent_width);
1346 XtSetArg (al [1], XtNheight, &parent_height);
1347 XtGetValues (parent, al, 2);
1349 x = (Position) ((parent_width - child_width) / 2);
1350 y = (Position) ((parent_height - child_height) / 2);
1352 XtTranslateCoords (parent, x, y, &x, &y);
1354 if ((Dimension) (x + child_width) > screen_width)
1355 x = screen_width - child_width;
1359 if ((Dimension) (y + child_height) > screen_height)
1360 y = screen_height - child_height;
1364 XtSetArg (al [0], XtNx, x);
1365 XtSetArg (al [1], XtNy, y);
1366 XtSetValues (widget, al, 2);
1370 recycle_instance (destroyed_instance* instance)
1372 Widget widget = instance->widget;
1374 /* widget is NULL if the parent was destroyed. */
1380 /* Remove the destroy callback as the instance is not in the list
1382 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1383 mark_dead_instance_destroyed,
1384 (XtPointer)instance);
1386 /* Give the focus to the initial item */
1387 focus = XtNameToWidget (widget, "*value");
1389 focus = XtNameToWidget (widget, "*button1");
1391 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1393 /* shrink the separator label back to their original size */
1394 separator = XtNameToWidget (widget, "*separator_button");
1398 XtSetArg (al [0], XtNwidth, 5);
1399 XtSetArg (al [1], XtNheight, 5);
1400 XtSetValues (separator, al, 2);
1403 /* Center the dialog in its parent */
1404 recenter_widget (widget);
1406 free_destroyed_instance (instance);
1411 xm_create_dialog (widget_instance* instance)
1413 char* name = instance->info->type;
1414 Widget parent = instance->parent;
1416 Boolean pop_up_p = instance->pop_up_p;
1417 CONST char* shell_name = 0;
1418 CONST char* icon_name = 0;
1419 Boolean text_input_slot = False;
1420 Boolean radio_box = False;
1421 Boolean list = False;
1423 int left_buttons = 0;
1424 int right_buttons = 1;
1425 destroyed_instance* dead_one;
1427 /* try to find a widget to recycle */
1428 dead_one = find_matching_instance (instance);
1431 Widget recycled_widget = recycle_instance (dead_one);
1432 if (recycled_widget)
1433 return recycled_widget;
1438 icon_name = "dbox-error";
1439 shell_name = "Error";
1443 icon_name = "dbox-info";
1444 shell_name = "Information";
1449 icon_name = "dbox-question";
1450 shell_name = "Prompt";
1454 text_input_slot = True;
1455 icon_name = "dbox-question";
1456 shell_name = "Prompt";
1460 icon_name = "dbox-question";
1461 shell_name = "Question";
1465 total_buttons = name [1] - '0';
1467 if (name [3] == 'T' || name [3] == 't')
1469 text_input_slot = False;
1473 right_buttons = name [4] - '0';
1475 left_buttons = total_buttons - right_buttons;
1477 widget = make_dialog (name, parent, pop_up_p,
1478 shell_name, icon_name, text_input_slot, radio_box,
1479 list, left_buttons, right_buttons);
1481 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1482 (XtPointer) instance);
1486 #endif /* LWLIB_DIALOGS_MOTIF */
1488 #ifdef LWLIB_MENUBARS_MOTIF
1490 make_menubar (widget_instance* instance)
1495 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1496 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1498 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1502 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1504 Widget menu = (Widget) closure;
1505 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1509 make_popup_menu (widget_instance* instance)
1511 Widget parent = instance->parent;
1512 Window parent_window = parent->core.window;
1515 /* sets the parent window to 0 to fool Motif into not generating a grab */
1516 parent->core.window = 0;
1517 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1518 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1520 parent->core.window = parent_window;
1523 #endif /* LWLIB_MENUBARS_MOTIF */
1525 #ifdef LWLIB_SCROLLBARS_MOTIF
1527 make_scrollbar (widget_instance *instance, int vertical)
1531 static XtCallbackRec callbacks[2] =
1532 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1534 callbacks[0].closure = (XtPointer) instance;
1536 XtSetArg (al[ac], XmNminimum, 1); ac++;
1537 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1538 XtSetArg (al[ac], XmNincrement, 1); ac++;
1539 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1540 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1541 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1543 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1544 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1545 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1546 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1547 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1548 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1549 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1550 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1552 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1556 make_vertical_scrollbar (widget_instance *instance)
1558 return make_scrollbar (instance, 1);
1562 make_horizontal_scrollbar (widget_instance *instance)
1564 return make_scrollbar (instance, 0);
1567 #endif /* LWLIB_SCROLLBARS_MOTIF */
1571 make_button (widget_instance *instance)
1576 widget_value* val = instance->info->val;
1578 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1579 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1580 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1581 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1582 /* The highlight doesn't appear to be dynamically set which makes it
1583 look ugly. I think this may be a LessTif bug but for now we just
1585 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1587 /* add any args the user supplied for creation time */
1588 lw_add_value_args_to_args (val, al, &ac);
1590 if (!val->call_data)
1591 button = XmCreateLabel (instance->parent, val->name, al, ac);
1593 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1595 XtSetArg (al [ac], XmNset, val->selected); ac++;
1596 XtSetArg (al [ac], XmNindicatorType,
1597 (val->type == TOGGLE_TYPE ?
1598 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1599 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1600 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1601 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1602 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1603 (XtPointer)instance);
1607 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1608 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1609 (XtPointer)instance);
1612 XtManageChild (button);
1618 make_progress (widget_instance *instance)
1623 widget_value* val = instance->info->val;
1625 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1626 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1627 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1628 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1629 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1630 /* The highlight doesn't appear to be dynamically set which makes it
1631 look ugly. I think this may be a LessTif bug but for now we just
1633 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1634 if (!val->call_data)
1635 XtSetArg (al [ac], XmNsensitive, False); ac++;
1637 /* add any args the user supplied for creation time */
1638 lw_add_value_args_to_args (val, al, &ac);
1640 scale = XmCreateScale (instance->parent, val->name, al, ac);
1642 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1643 (XtPointer)instance);
1645 XtManageChild (scale);
1651 make_text_field (widget_instance *instance)
1656 widget_value* val = instance->info->val;
1658 XtSetArg (al [ac], XmNsensitive, val->enabled && val->call_data); ac++;
1659 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1660 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1661 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1662 /* The highlight doesn't appear to be dynamically set which makes it
1663 look ugly. I think this may be a LessTif bug but for now we just
1665 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1667 /* add any args the user supplied for creation time */
1668 lw_add_value_args_to_args (val, al, &ac);
1670 text = XmCreateTextField (instance->parent, val->name, al, ac);
1672 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1673 (XtPointer)instance);
1675 XtManageChild (text);
1682 make_combo_box (widget_instance *instance)
1687 widget_value* val = instance->info->val;
1689 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1690 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1691 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1692 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1693 /* The highlight doesn't appear to be dynamically set which makes it
1694 look ugly. I think this may be a LessTif bug but for now we just
1696 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1698 /* add any args the user supplied for creation time */
1699 lw_add_value_args_to_args (val, al, &ac);
1701 combo = XmCreateComboBox (instance->parent, val->name, al, ac);
1703 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1704 (XtPointer)instance);
1706 XtManageChild (combo);
1713 /* Table of functions to create widgets */
1715 widget_creation_entry
1716 xm_creation_table [] =
1718 #ifdef LWLIB_MENUBARS_MOTIF
1719 {"menubar", make_menubar},
1720 {"popup", make_popup_menu},
1722 #ifdef LWLIB_SCROLLBARS_MOTIF
1723 {"vertical-scrollbar", make_vertical_scrollbar},
1724 {"horizontal-scrollbar", make_horizontal_scrollbar},
1726 {"button", make_button},
1727 {"progress", make_progress},
1728 {"text-field", make_text_field},
1730 {"combo-box", make_combo_box},
1735 \f/* Destruction of instances */
1737 xm_destroy_instance (widget_instance* instance)
1739 #ifdef LWLIB_DIALOGS_MOTIF
1740 /* It appears that this is used only for dialog boxes. */
1741 Widget widget = instance->widget;
1742 /* recycle the dialog boxes */
1743 /* Disable the recycling until we can find a way to have the dialog box
1744 get reasonable layout after we modify its contents. */
1746 && XtClass (widget) == xmDialogShellWidgetClass)
1748 destroyed_instance* dead_instance =
1749 make_destroyed_instance (instance->info->name,
1750 instance->info->type,
1753 instance->pop_up_p);
1754 dead_instance->next = all_destroyed_instances;
1755 all_destroyed_instances = dead_instance;
1756 XtUnmanageChild (first_child (instance->widget));
1757 XFlush (XtDisplay (instance->widget));
1758 XtAddCallback (instance->parent, XtNdestroyCallback,
1759 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1763 /* This might not be necessary now that the nosel is attached to
1764 popdown instead of destroy, but it can't hurt. */
1765 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1766 xm_nosel_callback, (XtPointer)instance);
1768 XtDestroyWidget (instance->widget);
1770 #endif /* LWLIB_DIALOGS_MOTIF */
1773 \f/* popup utility */
1774 #ifdef LWLIB_MENUBARS_MOTIF
1777 xm_popup_menu (Widget widget, XEvent *event)
1779 if (event->type == ButtonPress || event->type == ButtonRelease)
1781 /* This is so totally ridiculous: there's NO WAY to tell Motif
1782 that *any* button can select a menu item. Only one button
1783 can have that honor.
1786 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1787 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1788 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1789 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1790 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1794 XtSetArg (al [0], XmNmenuPost, trans);
1795 XtSetValues (widget, al, 1);
1797 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1799 XtManageChild (widget);
1804 #ifdef LWLIB_DIALOGS_MOTIF
1807 set_min_dialog_size (Widget w)
1813 XtSetArg (al [0], XmNwidth, &width);
1814 XtSetArg (al [1], XmNheight, &height);
1815 XtGetValues (w, al, 2);
1817 XtSetArg (al [0], XmNminWidth, width);
1818 XtSetArg (al [1], XmNminHeight, height);
1819 XtSetValues (w, al, 2);
1825 xm_pop_instance (widget_instance* instance, Boolean up)
1827 Widget widget = instance->widget;
1829 #ifdef LWLIB_DIALOGS_MOTIF
1830 if (XtClass (widget) == xmDialogShellWidgetClass)
1832 Widget widget_to_manage = first_child (widget);
1835 XtManageChild (widget_to_manage);
1836 set_min_dialog_size (widget);
1837 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1840 XtUnmanageChild (widget_to_manage);
1846 XtManageChild (widget);
1848 XtUnmanageChild (widget);
1853 /* motif callback */
1855 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1858 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1860 XtPointer user_data;
1861 widget_instance* instance = (widget_instance*)closure;
1862 Widget instance_widget;
1868 if (widget->core.being_destroyed)
1871 instance_widget = instance->widget;
1872 if (!instance_widget)
1875 id = instance->info->id;
1877 XtSetArg(al [0], XmNuserData, &user_data);
1878 XtGetValues (widget, al, 1);
1882 if (instance->info->pre_activate_cb)
1883 instance->info->pre_activate_cb (widget, id, user_data);
1886 if (instance->info->selection_cb)
1887 instance->info->selection_cb (widget, id, user_data);
1890 if (instance->info->selection_cb)
1891 instance->info->selection_cb (widget, id, (XtPointer) -1);
1894 if (instance->info->post_activate_cb)
1895 instance->info->post_activate_cb (widget, id, user_data);
1902 /* Like lw_internal_update_other_instances except that it does not do
1903 anything if its shell parent is not managed. This is to protect
1904 lw_internal_update_other_instances to dereference freed memory
1905 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1908 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1909 XtPointer call_data)
1912 for (parent = widget; parent; parent = XtParent (parent))
1913 if (XtIsShell (parent))
1915 else if (!XtIsManaged (parent))
1917 lw_internal_update_other_instances (widget, closure, call_data);
1921 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1923 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF))
1924 /* We want the selected status to change only when we decide it
1925 should change. Yuck but correct. */
1926 if (XtClass (widget) == xmToggleButtonWidgetClass
1927 || XtClass (widget) == xmToggleButtonGadgetClass)
1932 XtSetArg (al [0], XmNset, &check);
1933 XtGetValues (widget, al, 1);
1935 XtSetArg (al [0], XmNset, !check);
1936 XtSetValues (widget, al, 1);
1939 lw_internal_update_other_instances (widget, closure, call_data);
1940 do_call (widget, closure, selection);
1944 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1946 do_call (widget, closure, post_activate);
1949 #ifdef LWLIB_DIALOGS_MOTIF
1952 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
1954 /* This callback is only called when a dialog box is dismissed with the wm's
1955 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
1956 in that case, not just unmapped, so that it releases its keyboard grabs.
1957 But there are problems with running our callbacks while the widget is in
1958 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
1959 instead of XmDESTROY and then destroy it ourself after having run the
1962 do_call (widget, closure, no_selection);
1963 XtDestroyWidget (widget);
1968 #ifdef LWLIB_MENUBARS_MOTIF
1971 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1976 /* new behavior for incremental menu construction */
1981 do_call (widget, closure, pre_activate);
1984 #endif /* LWLIB_MENUBARS_MOTIF */
1986 #ifdef LWLIB_SCROLLBARS_MOTIF
1988 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
1990 widget_instance *instance = (widget_instance *) closure;
1992 XmScrollBarCallbackStruct *data =
1993 (XmScrollBarCallbackStruct *) call_data;
1994 scroll_event event_data;
1995 scrollbar_values *val =
1996 (scrollbar_values *) instance->info->val->scrollbar_data;
1999 if (!instance || widget->core.being_destroyed)
2002 id = instance->info->id;
2004 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2005 event_data.slider_value =
2006 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2008 if (event_data.slider_value > (val->maximum - val->slider_size))
2009 event_data.slider_value = val->maximum - val->slider_size;
2010 else if (event_data.slider_value < 1)
2011 event_data.slider_value = 1;
2015 switch (data->event->xany.type)
2019 event_data.time = data->event->xkey.time;
2023 event_data.time = data->event->xbutton.time;
2026 event_data.time = data->event->xmotion.time;
2030 event_data.time = data->event->xcrossing.time;
2033 event_data.time = 0;
2038 event_data.time = 0;
2040 switch (data->reason)
2042 case XmCR_DECREMENT:
2043 event_data.action = SCROLLBAR_LINE_UP;
2045 case XmCR_INCREMENT:
2046 event_data.action = SCROLLBAR_LINE_DOWN;
2048 case XmCR_PAGE_DECREMENT:
2049 event_data.action = SCROLLBAR_PAGE_UP;
2051 case XmCR_PAGE_INCREMENT:
2052 event_data.action = SCROLLBAR_PAGE_DOWN;
2055 event_data.action = SCROLLBAR_TOP;
2057 case XmCR_TO_BOTTOM:
2058 event_data.action = SCROLLBAR_BOTTOM;
2061 event_data.action = SCROLLBAR_DRAG;
2063 case XmCR_VALUE_CHANGED:
2064 event_data.action = SCROLLBAR_CHANGE;
2067 event_data.action = SCROLLBAR_CHANGE;
2071 if (instance->info->pre_activate_cb)
2072 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2074 #endif /* LWLIB_SCROLLBARS_MOTIF */
2077 /* set the keyboard focus */
2079 xm_set_keyboard_focus (Widget parent, Widget w)
2081 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2082 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2083 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2084 Presumably the problem was elsewhere, and is now gone...