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 /* Common to all label like widgets */
781 if (XtIsSubclass (widget, xmLabelWidgetClass))
782 xm_update_label (instance, widget, val);
784 class = XtClass (widget);
785 /* Class specific things */
786 if (class == xmPushButtonWidgetClass ||
787 class == xmArrowButtonWidgetClass)
789 xm_update_pushbutton (instance, widget, val);
791 #ifdef LWLIB_MENUBARS_MOTIF
792 else if (class == xmCascadeButtonWidgetClass)
794 xm_update_cascadebutton (instance, widget, val);
797 else if (class == xmToggleButtonWidgetClass
798 || class == xmToggleButtonGadgetClass)
800 xm_update_toggle (instance, widget, val);
802 else if (class == xmRowColumnWidgetClass)
804 Boolean radiobox = 0;
806 XtSetArg (al [0], XmNradioBehavior, &radiobox);
807 XtGetValues (widget, al, 1);
810 xm_update_radiobox (instance, widget, val);
811 #ifdef LWLIB_MENUBARS_MOTIF
813 xm_update_menu (instance, widget, val, deep_p);
816 else if (class == xmTextWidgetClass)
818 xm_update_text (instance, widget, val);
820 else if (class == xmTextFieldWidgetClass)
822 xm_update_text_field (instance, widget, val);
824 else if (class == xmListWidgetClass)
826 xm_update_list (instance, widget, val);
828 #ifdef LWLIB_SCROLLBARS_MOTIF
829 else if (class == xmScrollBarWidgetClass)
831 xm_update_scrollbar (instance, widget, val);
836 \f/* getting the value back */
838 xm_update_one_value (widget_instance* instance, Widget widget,
841 WidgetClass class = XtClass (widget);
842 widget_value *old_wv;
844 /* copy the call_data slot into the "return" widget_value */
845 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
846 if (!strcmp (val->name, old_wv->name))
848 val->call_data = old_wv->call_data;
852 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
855 XtSetArg (al [0], XmNset, &val->selected);
856 XtGetValues (widget, al, 1);
859 else if (class == xmTextWidgetClass)
863 val->value = XmTextGetString (widget);
866 else if (class == xmTextFieldWidgetClass)
870 val->value = XmTextFieldGetString (widget);
873 else if (class == xmRowColumnWidgetClass)
875 Boolean radiobox = 0;
878 XtSetArg (al [0], XmNradioBehavior, &radiobox);
879 XtGetValues (widget, al, 1);
884 CompositeWidget radio = (CompositeWidget)widget;
886 for (i = 0; i < radio->composite.num_children; i++)
889 Widget toggle = radio->composite.children [i];
892 XtSetArg (al [0], XmNset, &set);
893 XtGetValues (toggle, al, 1);
898 val->value = safe_strdup (XtName (toggle));
904 else if (class == xmListWidgetClass)
908 if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
912 for (cur = val->contents, i = 0; cur; cur = cur->next)
916 cur->selected = False;
918 for (j = 0; j < pos_cnt; j++)
919 if (pos_list [j] == i)
921 cur->selected = True;
922 val->value = safe_strdup (cur->name);
926 XtFree ((char *) pos_list);
929 #ifdef LWLIB_SCROLLBARS_MOTIF
930 else if (class == xmScrollBarWidgetClass)
932 /* This function is not used by the scrollbar. */
939 /* This function is for activating a button from a program. It's wrong because
940 we pass a NULL argument in the call_data which is not Motif compatible.
941 This is used from the XmNdefaultAction callback of the List widgets to
942 have a double-click put down a dialog box like the button would do.
943 I could not find a way to do that with accelerators.
946 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
948 Widget button = (Widget)closure;
949 XtCallCallbacks (button, XmNactivateCallback, NULL);
952 /* creation functions */
954 #ifdef LWLIB_DIALOGS_MOTIF
958 #if (XmVersion >= 1002)
959 # define ARMANDACTIVATE_KLUDGE
963 #ifdef ARMANDACTIVATE_KLUDGE
964 /* We want typing Return at a dialog box to select the default button; but
965 we're satisfied with having it select the leftmost button instead.
967 In Motif 1.1.5 we could do this by putting this resource in the
970 *dialog*button1.accelerators:#override\
971 <KeyPress>Return: ArmAndActivate()\n\
972 <KeyPress>KP_Enter: ArmAndActivate()\n\
973 Ctrl<KeyPress>m: ArmAndActivate()\n
975 but that doesn't work with 1.2.1 and I don't understand why. However,
976 doing the equivalent C code does work, with the notable disadvantage that
977 the user can't override it. So that's what we do until we figure out
980 static char button_trans[] = "\
981 <KeyPress>Return: ArmAndActivate()\n\
982 <KeyPress>KP_Enter: ArmAndActivate()\n\
983 Ctrl<KeyPress>m: ArmAndActivate()\n";
985 #endif /* ARMANDACTIVATE_KLUDGE */
989 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
990 was a segv down in libXm somewhere if you used the middle button on a
991 dialog box to begin a drag; when you released the button to make a drop
992 things would lose if you were not over the button where you started the
993 drag (canceling the operation). This was probably due to the fact that
994 the dialog boxes were not set up to handle a drag but were trying to do
995 so anyway for some reason.
997 So we disable drag-and-drop in dialog boxes by turning off the binding for
998 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
999 solution as it only works in default configurations, but...
1001 static char disable_dnd_trans[] = "<Btn2Down>: ";
1002 #endif /* DND_KLUDGE */
1006 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1007 CONST char* shell_title, CONST char* icon_name,
1008 Boolean text_input_slot, Boolean radio_box, Boolean list,
1009 int left_buttons, int right_buttons)
1015 Widget icon_separator;
1020 Widget children [16]; /* for the final XtManageChildren */
1022 Arg al[64]; /* Arg List */
1023 int ac; /* Arg Count */
1027 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1028 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1029 #else /* ! DND_KLUDGE */
1030 # define DO_DND_KLUDGE(widget)
1031 #endif /* ! DND_KLUDGE */
1036 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1037 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1038 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1039 result = XmCreateDialogShell (parent, "dialog", al, ac);
1041 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1042 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1043 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1044 form = XmCreateForm (result, (char *) shell_title, al, ac);
1049 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1050 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1051 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1056 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1057 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1058 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1059 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1060 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1061 XtSetArg(al[ac], XmNspacing, 13); ac++;
1062 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1063 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1064 XtSetArg(al[ac], XmNisAligned, True); ac++;
1065 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1066 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1067 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1068 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1069 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1070 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1071 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1072 row = XmCreateRowColumn (form, "row", al, ac);
1075 for (i = 0; i < left_buttons; i++)
1077 char button_name [16];
1078 sprintf (button_name, "button%d", i + 1);
1082 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1083 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1085 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1086 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1087 DO_DND_KLUDGE (children [n_children]);
1091 button = children [n_children];
1093 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1094 XtSetValues (row, al, ac);
1096 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1098 XtTranslations losers = XtParseTranslationTable (button_trans);
1099 XtOverrideTranslations (button, losers);
1100 XtFree ((char *) losers);
1102 #endif /* ARMANDACTIVATE_KLUDGE */
1108 /* invisible seperator button */
1110 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1111 children [n_children] = XmCreateLabel (row, "separator_button",
1113 DO_DND_KLUDGE (children [n_children]);
1116 for (i = 0; i < right_buttons; i++)
1118 char button_name [16];
1119 sprintf (button_name, "button%d", left_buttons + i + 1);
1121 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1122 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1123 DO_DND_KLUDGE (children [n_children]);
1124 if (! button) button = children [n_children];
1128 XtManageChildren (children, n_children);
1131 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1132 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1133 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1134 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1135 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1136 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1137 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1138 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1139 separator = XmCreateSeparator (form, "", al, ac);
1142 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1143 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1144 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1145 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1146 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1147 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1148 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1149 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1150 DO_DND_KLUDGE (icon);
1153 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1154 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1155 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1156 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1157 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1158 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1159 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1160 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1161 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1162 icon_separator = XmCreateLabel (form, "", al, ac);
1163 DO_DND_KLUDGE (icon_separator);
1165 if (text_input_slot)
1168 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1169 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1170 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1171 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1172 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1173 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1174 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1175 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1176 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1177 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1178 value = XmCreateTextField (form, "value", al, ac);
1179 DO_DND_KLUDGE (value);
1185 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1186 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1187 XtSetArg(al[ac], XmNspacing, 13); ac++;
1188 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1189 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1190 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1191 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1192 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1193 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1194 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1195 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1196 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1197 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1198 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1201 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1202 children [i++] = radio_butt;
1203 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1204 children [i++] = radio_butt;
1205 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1206 children [i++] = radio_butt;
1207 XtManageChildren (children, i);
1212 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1213 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1214 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1215 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1216 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1217 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1218 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1219 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1220 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1221 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1222 value = XmCreateScrolledList (form, "list", al, ac);
1224 /* this is the easiest way I found to have the dble click in the
1225 list activate the default button */
1226 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1230 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1231 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1232 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1233 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1234 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1235 XtSetArg(al[ac], XmNbottomWidget,
1236 text_input_slot || radio_box || list ? value : separator); ac++;
1237 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1238 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1239 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1240 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1241 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1242 message = XmCreateLabel (form, "message", al, ac);
1243 DO_DND_KLUDGE (message);
1246 XtManageChild (value);
1249 children [i] = row; i++;
1250 children [i] = separator; i++;
1251 if (text_input_slot || radio_box)
1253 children [i] = value; i++;
1255 children [i] = message; i++;
1256 children [i] = icon; i++;
1257 children [i] = icon_separator; i++;
1258 XtManageChildren (children, i);
1260 if (text_input_slot || list)
1262 XtInstallAccelerators (value, button);
1263 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1267 XtInstallAccelerators (form, button);
1268 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1272 XtFree ((char *) dnd_override);
1274 #undef DO_DND_KLUDGE
1279 static destroyed_instance*
1280 find_matching_instance (widget_instance* instance)
1282 destroyed_instance* cur;
1283 destroyed_instance* prev;
1284 char* type = instance->info->type;
1285 char* name = instance->info->name;
1287 for (prev = NULL, cur = all_destroyed_instances;
1289 prev = cur, cur = cur->next)
1291 if (!strcmp (cur->name, name)
1292 && !strcmp (cur->type, type)
1293 && cur->parent == instance->parent
1294 && cur->pop_up_p == instance->pop_up_p)
1297 prev->next = cur->next;
1299 all_destroyed_instances = cur->next;
1302 /* do some cleanup */
1303 else if (!cur->widget)
1306 prev->next = cur->next;
1308 all_destroyed_instances = cur->next;
1309 free_destroyed_instance (cur);
1310 cur = prev ? prev : all_destroyed_instances;
1317 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
1318 XtPointer call_data)
1320 destroyed_instance* instance = (destroyed_instance*)closure;
1321 instance->widget = NULL;
1325 recenter_widget (Widget widget)
1327 Widget parent = XtParent (widget);
1328 Screen* screen = XtScreen (widget);
1329 Dimension screen_width = WidthOfScreen (screen);
1330 Dimension screen_height = HeightOfScreen (screen);
1331 Dimension parent_width = 0;
1332 Dimension parent_height = 0;
1333 Dimension child_width = 0;
1334 Dimension child_height = 0;
1339 XtSetArg (al [0], XtNwidth, &child_width);
1340 XtSetArg (al [1], XtNheight, &child_height);
1341 XtGetValues (widget, al, 2);
1343 XtSetArg (al [0], XtNwidth, &parent_width);
1344 XtSetArg (al [1], XtNheight, &parent_height);
1345 XtGetValues (parent, al, 2);
1347 x = (Position) ((parent_width - child_width) / 2);
1348 y = (Position) ((parent_height - child_height) / 2);
1350 XtTranslateCoords (parent, x, y, &x, &y);
1352 if ((Dimension) (x + child_width) > screen_width)
1353 x = screen_width - child_width;
1357 if ((Dimension) (y + child_height) > screen_height)
1358 y = screen_height - child_height;
1362 XtSetArg (al [0], XtNx, x);
1363 XtSetArg (al [1], XtNy, y);
1364 XtSetValues (widget, al, 2);
1368 recycle_instance (destroyed_instance* instance)
1370 Widget widget = instance->widget;
1372 /* widget is NULL if the parent was destroyed. */
1378 /* Remove the destroy callback as the instance is not in the list
1380 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1381 mark_dead_instance_destroyed,
1382 (XtPointer)instance);
1384 /* Give the focus to the initial item */
1385 focus = XtNameToWidget (widget, "*value");
1387 focus = XtNameToWidget (widget, "*button1");
1389 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1391 /* shrink the separator label back to their original size */
1392 separator = XtNameToWidget (widget, "*separator_button");
1396 XtSetArg (al [0], XtNwidth, 5);
1397 XtSetArg (al [1], XtNheight, 5);
1398 XtSetValues (separator, al, 2);
1401 /* Center the dialog in its parent */
1402 recenter_widget (widget);
1404 free_destroyed_instance (instance);
1409 xm_create_dialog (widget_instance* instance)
1411 char* name = instance->info->type;
1412 Widget parent = instance->parent;
1414 Boolean pop_up_p = instance->pop_up_p;
1415 CONST char* shell_name = 0;
1416 CONST char* icon_name = 0;
1417 Boolean text_input_slot = False;
1418 Boolean radio_box = False;
1419 Boolean list = False;
1421 int left_buttons = 0;
1422 int right_buttons = 1;
1423 destroyed_instance* dead_one;
1425 /* try to find a widget to recycle */
1426 dead_one = find_matching_instance (instance);
1429 Widget recycled_widget = recycle_instance (dead_one);
1430 if (recycled_widget)
1431 return recycled_widget;
1436 icon_name = "dbox-error";
1437 shell_name = "Error";
1441 icon_name = "dbox-info";
1442 shell_name = "Information";
1447 icon_name = "dbox-question";
1448 shell_name = "Prompt";
1452 text_input_slot = True;
1453 icon_name = "dbox-question";
1454 shell_name = "Prompt";
1458 icon_name = "dbox-question";
1459 shell_name = "Question";
1463 total_buttons = name [1] - '0';
1465 if (name [3] == 'T' || name [3] == 't')
1467 text_input_slot = False;
1471 right_buttons = name [4] - '0';
1473 left_buttons = total_buttons - right_buttons;
1475 widget = make_dialog (name, parent, pop_up_p,
1476 shell_name, icon_name, text_input_slot, radio_box,
1477 list, left_buttons, right_buttons);
1479 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1480 (XtPointer) instance);
1484 #endif /* LWLIB_DIALOGS_MOTIF */
1486 #ifdef LWLIB_MENUBARS_MOTIF
1488 make_menubar (widget_instance* instance)
1493 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1494 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1496 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1500 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1502 Widget menu = (Widget) closure;
1503 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1507 make_popup_menu (widget_instance* instance)
1509 Widget parent = instance->parent;
1510 Window parent_window = parent->core.window;
1513 /* sets the parent window to 0 to fool Motif into not generating a grab */
1514 parent->core.window = 0;
1515 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1516 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1518 parent->core.window = parent_window;
1521 #endif /* LWLIB_MENUBARS_MOTIF */
1523 #ifdef LWLIB_SCROLLBARS_MOTIF
1525 make_scrollbar (widget_instance *instance, int vertical)
1529 static XtCallbackRec callbacks[2] =
1530 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1532 callbacks[0].closure = (XtPointer) instance;
1534 XtSetArg (al[ac], XmNminimum, 1); ac++;
1535 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1536 XtSetArg (al[ac], XmNincrement, 1); ac++;
1537 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1538 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1539 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1541 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1542 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1543 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1544 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1545 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1546 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1547 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1548 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1550 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1554 make_vertical_scrollbar (widget_instance *instance)
1556 return make_scrollbar (instance, 1);
1560 make_horizontal_scrollbar (widget_instance *instance)
1562 return make_scrollbar (instance, 0);
1565 #endif /* LWLIB_SCROLLBARS_MOTIF */
1569 make_button (widget_instance *instance)
1574 widget_value* val = instance->info->val;
1576 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1577 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1578 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1579 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1580 /* The highlight doesn't appear to be dynamically set which makes it
1581 look ugly. I think this may be a LessTif bug but for now we just
1583 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1585 /* add any args the user supplied for creation time */
1586 lw_add_value_args_to_args (val, al, &ac);
1588 if (!val->call_data)
1589 button = XmCreateLabel (instance->parent, val->name, al, ac);
1591 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1593 XtSetArg (al [ac], XmNset, val->selected); ac++;
1594 XtSetArg (al [ac], XmNindicatorType,
1595 (val->type == TOGGLE_TYPE ?
1596 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1597 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1598 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1599 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1600 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1601 (XtPointer)instance);
1605 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1606 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1607 (XtPointer)instance);
1610 XtManageChild (button);
1616 make_progress (widget_instance *instance)
1621 widget_value* val = instance->info->val;
1623 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1624 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1625 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1626 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1627 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1628 /* The highlight doesn't appear to be dynamically set which makes it
1629 look ugly. I think this may be a LessTif bug but for now we just
1631 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1632 if (!val->call_data)
1633 XtSetArg (al [ac], XmNsensitive, False); ac++;
1635 /* add any args the user supplied for creation time */
1636 lw_add_value_args_to_args (val, al, &ac);
1638 scale = XmCreateScale (instance->parent, val->name, al, ac);
1640 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1641 (XtPointer)instance);
1643 XtManageChild (scale);
1649 make_text_field (widget_instance *instance)
1654 widget_value* val = instance->info->val;
1656 XtSetArg (al [ac], XmNsensitive, val->enabled && val->call_data); ac++;
1657 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1658 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1659 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1660 /* The highlight doesn't appear to be dynamically set which makes it
1661 look ugly. I think this may be a LessTif bug but for now we just
1663 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1665 /* add any args the user supplied for creation time */
1666 lw_add_value_args_to_args (val, al, &ac);
1668 text = XmCreateTextField (instance->parent, val->name, al, ac);
1670 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1671 (XtPointer)instance);
1673 XtManageChild (text);
1680 make_combo_box (widget_instance *instance)
1685 widget_value* val = instance->info->val;
1687 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1688 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1689 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1690 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1691 /* The highlight doesn't appear to be dynamically set which makes it
1692 look ugly. I think this may be a LessTif bug but for now we just
1694 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1696 /* add any args the user supplied for creation time */
1697 lw_add_value_args_to_args (val, al, &ac);
1699 combo = XmCreateComboBox (instance->parent, val->name, al, ac);
1701 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1702 (XtPointer)instance);
1704 XtManageChild (combo);
1711 /* Table of functions to create widgets */
1713 widget_creation_entry
1714 xm_creation_table [] =
1716 #ifdef LWLIB_MENUBARS_MOTIF
1717 {"menubar", make_menubar},
1718 {"popup", make_popup_menu},
1720 #ifdef LWLIB_SCROLLBARS_MOTIF
1721 {"vertical-scrollbar", make_vertical_scrollbar},
1722 {"horizontal-scrollbar", make_horizontal_scrollbar},
1724 {"button", make_button},
1725 {"progress", make_progress},
1726 {"text-field", make_text_field},
1728 {"combo-box", make_combo_box},
1733 \f/* Destruction of instances */
1735 xm_destroy_instance (widget_instance* instance)
1737 #ifdef LWLIB_DIALOGS_MOTIF
1738 /* It appears that this is used only for dialog boxes. */
1739 Widget widget = instance->widget;
1740 /* recycle the dialog boxes */
1741 /* Disable the recycling until we can find a way to have the dialog box
1742 get reasonable layout after we modify its contents. */
1744 && XtClass (widget) == xmDialogShellWidgetClass)
1746 destroyed_instance* dead_instance =
1747 make_destroyed_instance (instance->info->name,
1748 instance->info->type,
1751 instance->pop_up_p);
1752 dead_instance->next = all_destroyed_instances;
1753 all_destroyed_instances = dead_instance;
1754 XtUnmanageChild (first_child (instance->widget));
1755 XFlush (XtDisplay (instance->widget));
1756 XtAddCallback (instance->parent, XtNdestroyCallback,
1757 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1761 /* This might not be necessary now that the nosel is attached to
1762 popdown instead of destroy, but it can't hurt. */
1763 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1764 xm_nosel_callback, (XtPointer)instance);
1766 XtDestroyWidget (instance->widget);
1768 #endif /* LWLIB_DIALOGS_MOTIF */
1771 \f/* popup utility */
1772 #ifdef LWLIB_MENUBARS_MOTIF
1775 xm_popup_menu (Widget widget, XEvent *event)
1777 if (event->type == ButtonPress || event->type == ButtonRelease)
1779 /* This is so totally ridiculous: there's NO WAY to tell Motif
1780 that *any* button can select a menu item. Only one button
1781 can have that honor.
1784 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1785 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1786 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1787 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1788 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1792 XtSetArg (al [0], XmNmenuPost, trans);
1793 XtSetValues (widget, al, 1);
1795 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1797 XtManageChild (widget);
1802 #ifdef LWLIB_DIALOGS_MOTIF
1805 set_min_dialog_size (Widget w)
1811 XtSetArg (al [0], XmNwidth, &width);
1812 XtSetArg (al [1], XmNheight, &height);
1813 XtGetValues (w, al, 2);
1815 XtSetArg (al [0], XmNminWidth, width);
1816 XtSetArg (al [1], XmNminHeight, height);
1817 XtSetValues (w, al, 2);
1823 xm_pop_instance (widget_instance* instance, Boolean up)
1825 Widget widget = instance->widget;
1827 #ifdef LWLIB_DIALOGS_MOTIF
1828 if (XtClass (widget) == xmDialogShellWidgetClass)
1830 Widget widget_to_manage = first_child (widget);
1833 XtManageChild (widget_to_manage);
1834 set_min_dialog_size (widget);
1835 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1838 XtUnmanageChild (widget_to_manage);
1844 XtManageChild (widget);
1846 XtUnmanageChild (widget);
1851 /* motif callback */
1853 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1856 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1858 XtPointer user_data;
1859 widget_instance* instance = (widget_instance*)closure;
1860 Widget instance_widget;
1866 if (widget->core.being_destroyed)
1869 instance_widget = instance->widget;
1870 if (!instance_widget)
1873 id = instance->info->id;
1875 XtSetArg(al [0], XmNuserData, &user_data);
1876 XtGetValues (widget, al, 1);
1880 if (instance->info->pre_activate_cb)
1881 instance->info->pre_activate_cb (widget, id, user_data);
1884 if (instance->info->selection_cb)
1885 instance->info->selection_cb (widget, id, user_data);
1888 if (instance->info->selection_cb)
1889 instance->info->selection_cb (widget, id, (XtPointer) -1);
1892 if (instance->info->post_activate_cb)
1893 instance->info->post_activate_cb (widget, id, user_data);
1900 /* Like lw_internal_update_other_instances except that it does not do
1901 anything if its shell parent is not managed. This is to protect
1902 lw_internal_update_other_instances to dereference freed memory
1903 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1906 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1907 XtPointer call_data)
1910 for (parent = widget; parent; parent = XtParent (parent))
1911 if (XtIsShell (parent))
1913 else if (!XtIsManaged (parent))
1915 lw_internal_update_other_instances (widget, closure, call_data);
1919 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1921 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF))
1922 /* We want the selected status to change only when we decide it
1923 should change. Yuck but correct. */
1924 if (XtClass (widget) == xmToggleButtonWidgetClass
1925 || XtClass (widget) == xmToggleButtonGadgetClass)
1930 XtSetArg (al [0], XmNset, &check);
1931 XtGetValues (widget, al, 1);
1933 XtSetArg (al [0], XmNset, !check);
1934 XtSetValues (widget, al, 1);
1937 lw_internal_update_other_instances (widget, closure, call_data);
1938 do_call (widget, closure, selection);
1942 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1944 do_call (widget, closure, post_activate);
1947 #ifdef LWLIB_DIALOGS_MOTIF
1950 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
1952 /* This callback is only called when a dialog box is dismissed with the wm's
1953 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
1954 in that case, not just unmapped, so that it releases its keyboard grabs.
1955 But there are problems with running our callbacks while the widget is in
1956 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
1957 instead of XmDESTROY and then destroy it ourself after having run the
1960 do_call (widget, closure, no_selection);
1961 XtDestroyWidget (widget);
1966 #ifdef LWLIB_MENUBARS_MOTIF
1969 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1974 /* new behavior for incremental menu construction */
1979 do_call (widget, closure, pre_activate);
1982 #endif /* LWLIB_MENUBARS_MOTIF */
1984 #ifdef LWLIB_SCROLLBARS_MOTIF
1986 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
1988 widget_instance *instance = (widget_instance *) closure;
1990 XmScrollBarCallbackStruct *data =
1991 (XmScrollBarCallbackStruct *) call_data;
1992 scroll_event event_data;
1993 scrollbar_values *val =
1994 (scrollbar_values *) instance->info->val->scrollbar_data;
1997 if (!instance || widget->core.being_destroyed)
2000 id = instance->info->id;
2002 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2003 event_data.slider_value =
2004 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2006 if (event_data.slider_value > (val->maximum - val->slider_size))
2007 event_data.slider_value = val->maximum - val->slider_size;
2008 else if (event_data.slider_value < 1)
2009 event_data.slider_value = 1;
2013 switch (data->event->xany.type)
2017 event_data.time = data->event->xkey.time;
2021 event_data.time = data->event->xbutton.time;
2024 event_data.time = data->event->xmotion.time;
2028 event_data.time = data->event->xcrossing.time;
2031 event_data.time = 0;
2036 event_data.time = 0;
2038 switch (data->reason)
2040 case XmCR_DECREMENT:
2041 event_data.action = SCROLLBAR_LINE_UP;
2043 case XmCR_INCREMENT:
2044 event_data.action = SCROLLBAR_LINE_DOWN;
2046 case XmCR_PAGE_DECREMENT:
2047 event_data.action = SCROLLBAR_PAGE_UP;
2049 case XmCR_PAGE_INCREMENT:
2050 event_data.action = SCROLLBAR_PAGE_DOWN;
2053 event_data.action = SCROLLBAR_TOP;
2055 case XmCR_TO_BOTTOM:
2056 event_data.action = SCROLLBAR_BOTTOM;
2059 event_data.action = SCROLLBAR_DRAG;
2061 case XmCR_VALUE_CHANGED:
2062 event_data.action = SCROLLBAR_CHANGE;
2065 event_data.action = SCROLLBAR_CHANGE;
2069 if (instance->info->pre_activate_cb)
2070 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2072 #endif /* LWLIB_SCROLLBARS_MOTIF */
2075 /* set the keyboard focus */
2077 xm_set_keyboard_focus (Widget parent, Widget w)
2079 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2080 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2081 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2082 Presumably the problem was elsewhere, and is now gone...