1 /* The lwlib interface to Motif widgets.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 This file is part of the Lucid Widget Library.
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
31 #include <X11/StringDefs.h>
32 #include <X11/IntrinsicP.h>
33 #include <X11/ObjectP.h>
34 #include <X11/CoreP.h>
35 #include <X11/CompositeP.h>
38 #include "lwlib-utils.h"
41 #include <Xm/BulletinB.h>
42 #include <Xm/CascadeB.h>
43 #include <Xm/DrawingA.h>
44 #include <Xm/FileSB.h>
47 #include <Xm/MenuShell.h>
48 #include <Xm/MessageB.h>
50 #include <Xm/PushBG.h>
51 #include <Xm/ArrowB.h>
52 #include <Xm/ScrollBar.h>
53 #include <Xm/SelectioB.h>
56 #include <Xm/ToggleB.h>
57 #include <Xm/ToggleBG.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/ScrolledW.h>
60 #include <Xm/Separator.h>
61 #include <Xm/DialogS.h>
63 #ifdef LWLIB_WIDGETS_MOTIF
66 #include <Xm/ComboBoxP.h>
70 #ifdef LWLIB_MENUBARS_MOTIF
71 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
73 static void xm_internal_update_other_instances (Widget, XtPointer,
75 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
76 static void xm_generic_callback (Widget, XtPointer, XtPointer);
77 static void mark_dead_instance_destroyed (Widget widget, XtPointer closure,
79 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
80 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
82 #ifdef LWLIB_SCROLLBARS_MOTIF
83 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
86 #ifdef LWLIB_MENUBARS_MOTIF
88 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
92 \f/* Structures to keep destroyed instances */
93 typedef struct _destroyed_instance
100 struct _destroyed_instance* next;
101 } destroyed_instance;
103 static destroyed_instance*
104 all_destroyed_instances = NULL;
106 /* Utility function. */
108 safe_strdup (char* s)
112 result = (char *) malloc (strlen (s) + 1);
119 static destroyed_instance*
120 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
123 destroyed_instance* instance =
124 (destroyed_instance*) malloc (sizeof (destroyed_instance));
125 instance->name = safe_strdup (name);
126 instance->type = safe_strdup (type);
127 instance->widget = widget;
128 instance->parent = parent;
129 instance->pop_up_p = pop_up_p;
130 instance->next = NULL;
135 free_destroyed_instance (destroyed_instance* instance)
137 free (instance->name);
138 free (instance->type);
142 \f/* motif utility functions */
144 first_child (Widget widget)
146 return ((CompositeWidget)widget)->composite.children [0];
150 lw_motif_widget_p (Widget widget)
153 #ifdef LWLIB_DIALOGS_MOTIF
154 XtClass (widget) == xmDialogShellWidgetClass ||
156 XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
160 resource_string (Widget widget, char *name)
165 resource.resource_name = "labelString";
166 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
167 resource.resource_type = XtRString;
168 resource.resource_size = sizeof (String);
169 resource.resource_offset = 0;
170 resource.default_type = XtRImmediate;
171 resource.default_addr = 0;
173 XtGetSubresources (widget, (XtPointer)&result, name,
174 name, &resource, 1, NULL, 0);
180 #ifdef LWLIB_DIALOGS_MOTIF
183 is_in_dialog_box (Widget w)
187 wmshell = XtParent (w);
188 while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
189 wmshell = XtParent (wmshell);
191 if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
197 #endif /* LWLIB_DIALOGS_MOTIF */
199 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
201 /* update the label of anything subclass of a label */
203 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
205 XmString built_string = NULL;
206 XmString key_string = NULL;
207 XmString val_string = NULL;
208 XmString name_string = NULL;
213 /* Don't clobber pixmap types. */
214 XtSetArg (al [0], XmNlabelType, &type);
215 XtGetValues (widget, al, 1);
217 if (type == XmPIXMAP)
222 #ifdef LWLIB_DIALOGS_MOTIF
224 * Sigh. The main text of a label is the name field for menubar
225 * entries. The value field is a possible additional field to be
226 * concatenated on to the name field. HOWEVER, with dialog boxes
227 * the value field is the complete text which is supposed to be
228 * displayed as the label. Yuck.
230 if (is_in_dialog_box (widget))
232 char *value_name = NULL;
234 value_name = resource_string (widget, val->value);
236 value_name = val->value;
239 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
242 #endif /* LWLIB_DIALOGS_MOTIF */
244 char *value_name = NULL;
245 char *res_name = NULL;
247 res_name = resource_string (widget, val->name);
248 /* Concatenating the value with itself seems just plain daft. */
252 XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
257 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
259 value_name = XtMalloc (strlen (val->value) + 2);
261 strcat (value_name, " ");
262 strcat (value_name, val->value);
265 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
268 XmStringConcat (name_string, val_string);
274 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
275 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
280 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
281 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
285 XtSetValues (widget, al, ac);
288 XmStringFree (built_string);
291 XmStringFree (key_string);
294 XmStringFree (name_string);
297 XmStringFree (val_string);
300 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
302 \f/* update of list */
304 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
308 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
309 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
311 for (cur = val->contents, i = 0; cur; cur = cur->next)
314 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
316 XmListAddItem (widget, xmstr, 0);
318 XmListSelectPos (widget, i, False);
319 XmStringFree (xmstr);
323 \f/* update of buttons */
325 xm_update_pushbutton (widget_instance* instance, Widget widget,
329 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
330 XtSetValues (widget, al, 1);
331 XtRemoveAllCallbacks (widget, XmNactivateCallback);
332 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
335 #ifdef LWLIB_MENUBARS_MOTIF
338 xm_update_cascadebutton (widget_instance* instance, Widget widget,
341 /* Should also rebuild the menu by calling ...update_menu... */
343 && val->type == CASCADE_TYPE
345 && val->contents->type == INCREMENTAL_TYPE)
347 /* okay, we're now doing a lisp callback to incrementally generate
349 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
350 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
352 XtCallCallbacks ((Widget)widget,
353 XmNcascadingCallback,
354 (XtPointer)val->contents);
357 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
358 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
363 #endif /* LWLIB_MENUBARS_MOTIF */
365 \f/* update toggle and radiobox */
367 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
370 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
371 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
373 XtSetArg (al [0], XmNset, val->selected);
374 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
375 XtSetValues (widget, al, 1);
379 xm_update_radiobox (widget_instance* instance, Widget widget,
385 /* update the callback */
386 XtRemoveAllCallbacks (widget, XmNentryCallback);
387 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
389 /* first update all the toggles */
390 /* Energize kernel interface is currently bad. It sets the selected widget
391 with the selected flag but returns it by its name. So we currently
392 have to support both setting the selection with the selected slot
393 of val contents and setting it with the "value" slot of val. The latter
394 has a higher priority. This to be removed when the kernel is fixed. */
395 for (cur = val->contents; cur; cur = cur->next)
397 toggle = XtNameToWidget (widget, cur->value);
401 XtSetArg (al [0], XmNsensitive, cur->enabled);
402 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
403 XtSetValues (toggle, al, 2);
407 /* The selected was specified by the value slot */
410 toggle = XtNameToWidget (widget, val->value);
414 XtSetArg (al [0], XmNset, True);
415 XtSetValues (toggle, al, 1);
420 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
421 /* update of combo box */
423 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
427 XtRemoveAllCallbacks (widget, XmNselectionCallback);
428 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
430 for (cur = val->contents, i = 0; cur; cur = cur->next)
433 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
435 XmListAddItem (CB_List (widget), xmstr, 0);
437 XmListSelectPos (CB_List (widget), i, False);
438 XmStringFree (xmstr);
443 #ifdef LWLIB_MENUBARS_MOTIF
445 \f/* update a popup menu, pulldown menu or a menubar */
447 make_menu_in_widget (widget_instance* instance, Widget widget,
450 Widget* children = 0;
458 Boolean menubar_p = False;
460 /* Allocate the children array */
461 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
462 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
464 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
465 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
466 XtGetValues (widget, al, 1);
468 /* add the unmap callback for popups and pulldowns */
469 /*** this sounds bogus ***/
470 /* probably because it is -- cet */
473 XtAddCallback (XtParent (widget), XmNpopdownCallback,
474 xm_pop_down_callback, (XtPointer)instance);
478 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
482 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
483 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
484 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
489 /* A pushright marker which is not needed for the real Motif
496 /* #### - xlwmenu.h supports several types that motif does
497 not. Also, motif supports pixmaps w/ type NO_LINE and
498 lwlib provides no way to access that functionality. --Stig */
499 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
501 button = XmCreateSeparator (widget, "separator", al, ac);
504 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
505 make_menu_in_widget (instance, menu, cur->contents);
506 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
507 button = XmCreateCascadeButton (widget, cur->name, al, ac);
509 xm_update_label (instance, button, cur);
511 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
512 (XtPointer)instance);
516 button = XmCreateCascadeButton (widget, cur->name, al, ac);
517 else if (!cur->call_data)
518 button = XmCreateLabel (widget, cur->name, al, ac);
519 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
521 XtSetArg (al [ac], XmNindicatorType,
522 (cur->type == TOGGLE_TYPE ?
523 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
524 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
525 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
528 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
530 xm_update_label (instance, button, cur);
532 /* don't add a callback to a simple label */
533 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
534 xm_update_toggle (instance, button, cur);
535 else if (cur->call_data)
536 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
537 (XtPointer)instance);
538 } /* switch (cur->type) */
541 children [num_children++] = button;
544 /* Last entry is the help button. This used be done after managing
545 the buttons. The comment claimed that it had to be done this way
546 otherwise the menubar ended up only 4 pixels high. That must
547 have been in the Old World. In the New World it stays the proper
548 height if you don't manage them until after you set this and as a
549 bonus the Help menu ends up where it is supposed to. */
553 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
554 XtSetValues (widget, al, ac);
558 XtManageChildren (children, num_children);
560 XtFree ((char *) children);
564 update_one_menu_entry (widget_instance* instance, Widget widget,
565 widget_value* val, Boolean deep_p)
570 widget_value* contents;
572 if (val->change == NO_CHANGE)
575 /* update the sensitivity and userdata */
576 /* Common to all widget types */
577 XtSetArg (al [0], XmNsensitive, val->enabled);
578 XtSetArg (al [1], XmNuserData, val->call_data);
579 XtSetValues (widget, al, 2);
581 /* update the menu button as a label. */
582 if (val->change >= VISIBLE_CHANGE)
584 xm_update_label (instance, widget, val);
585 if (XtClass (widget) == xmToggleButtonWidgetClass
586 || XtClass (widget) == xmToggleButtonGadgetClass)
588 xm_update_toggle (instance, widget, val);
593 /* update the pulldown/pullaside as needed */
595 XtSetArg (al [0], XmNsubMenuId, &menu);
596 XtGetValues (widget, al, 1);
598 contents = val->contents;
604 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
605 make_menu_in_widget (instance, menu, contents);
607 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
608 XtSetValues (widget, al, ac);
614 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
615 XtSetValues (widget, al, ac);
616 XtDestroyWidget (menu);
618 else if (deep_p && contents->change != NO_CHANGE)
619 xm_update_menu (instance, menu, val, 1);
623 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
626 /* Widget is a RowColumn widget whose contents have to be updated
627 * to reflect the list of items in val->contents */
628 if (val->contents->change == STRUCTURAL_CHANGE)
630 destroy_all_children (widget);
631 make_menu_in_widget (instance, widget, val->contents);
635 /* Update all the buttons of the RowColumn in order. */
637 unsigned int num_children;
639 widget_value *cur = 0;
641 children = XtCompositeChildren (widget, &num_children);
644 for (i = 0, cur = val->contents; i < num_children; i++)
648 /* skip if this is a pushright marker or a separator */
649 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
653 /* #### - this could puke if you have a separator as the
654 last item on a pullright menu. */
662 if (children [i]->core.being_destroyed
663 || strcmp (XtName (children [i]), cur->name))
665 update_one_menu_entry (instance, children [i], cur, deep_p);
668 XtFree ((char *) children);
675 #endif /* LWLIB_MENUBARS_MOTIF */
678 /* update text widgets */
681 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
683 XmTextSetString (widget, val->value ? val->value : (char *) "");
684 XtRemoveAllCallbacks (widget, XmNactivateCallback);
685 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
686 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
687 XtAddCallback (widget, XmNvalueChangedCallback,
688 xm_internal_update_other_instances, instance);
692 xm_update_text_field (widget_instance* instance, Widget widget,
695 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
696 XtRemoveAllCallbacks (widget, XmNactivateCallback);
697 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
698 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
699 XtAddCallback (widget, XmNvalueChangedCallback,
700 xm_internal_update_other_instances, instance);
704 #ifdef LWLIB_SCROLLBARS_MOTIF
707 * If this function looks like it does a lot more work than it needs to,
708 * you're right. Blame the Motif scrollbar for not being smart about
709 * updating its appearance.
712 xm_update_scrollbar (widget_instance *instance, Widget widget,
715 if (val->scrollbar_data)
717 scrollbar_values *data = val->scrollbar_data;
718 int widget_sliderSize, widget_val;
719 int new_sliderSize, new_value;
721 double h_water, l_water;
724 /* First size and position the scrollbar widget. */
725 XtSetArg (al [0], XtNx, data->scrollbar_x);
726 XtSetArg (al [1], XtNy, data->scrollbar_y);
727 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
728 XtSetArg (al [3], XtNheight, data->scrollbar_height);
729 XtSetValues (widget, al, 4);
731 /* Now size the scrollbar's slider. */
732 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
733 XtSetArg (al [1], XmNvalue, &widget_val);
734 XtGetValues (widget, al, 2);
736 percent = (double) data->slider_size /
737 (double) (data->maximum - data->minimum);
738 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
740 percent = (double) (data->slider_position - data->minimum) /
741 (double) (data->maximum - data->minimum);
742 new_value = (int) ((double) (INT_MAX - 1) * percent);
744 if (new_sliderSize > (INT_MAX - 1))
745 new_sliderSize = INT_MAX - 1;
746 else if (new_sliderSize < 1)
749 if (new_value > (INT_MAX - new_sliderSize))
750 new_value = INT_MAX - new_sliderSize;
751 else if (new_value < 1)
756 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
758 int force = ((INT_MAX - widget_sliderSize - widget_val)
760 : (INT_MAX - new_sliderSize - new_value));
763 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
764 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
765 || (double)new_value < (l_water * (double)widget_val)
766 || (double)new_value > (h_water * (double)widget_val))
768 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
775 #endif /* LWLIB_SCROLLBARS_MOTIF */
778 /* update a motif widget */
781 xm_update_one_widget (widget_instance* instance, Widget widget,
782 widget_value* val, Boolean deep_p)
788 /* Mark as not edited */
791 /* Common to all widget types */
792 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
793 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
794 XtSetValues (widget, al, ac);
796 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
797 /* Common to all label like widgets */
798 if (XtIsSubclass (widget, xmLabelWidgetClass))
799 xm_update_label (instance, widget, val);
801 class = XtClass (widget);
802 /* Class specific things */
803 if (class == xmPushButtonWidgetClass ||
804 class == xmArrowButtonWidgetClass)
806 xm_update_pushbutton (instance, widget, val);
808 #ifdef LWLIB_MENUBARS_MOTIF
809 else if (class == xmCascadeButtonWidgetClass)
811 xm_update_cascadebutton (instance, widget, val);
814 else if (class == xmToggleButtonWidgetClass
815 || class == xmToggleButtonGadgetClass)
817 xm_update_toggle (instance, widget, val);
819 else if (class == xmRowColumnWidgetClass)
821 Boolean radiobox = 0;
823 XtSetArg (al [0], XmNradioBehavior, &radiobox);
824 XtGetValues (widget, al, 1);
827 xm_update_radiobox (instance, widget, val);
828 #ifdef LWLIB_MENUBARS_MOTIF
830 xm_update_menu (instance, widget, val, deep_p);
833 else if (class == xmTextWidgetClass)
835 xm_update_text (instance, widget, val);
837 else if (class == xmTextFieldWidgetClass)
839 xm_update_text_field (instance, widget, val);
841 else if (class == xmListWidgetClass)
843 xm_update_list (instance, widget, val);
845 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
846 else if (class == xmComboBoxWidgetClass)
848 xm_update_combo_box (instance, widget, val);
851 #ifdef LWLIB_SCROLLBARS_MOTIF
852 else if (class == xmScrollBarWidgetClass)
854 xm_update_scrollbar (instance, widget, val);
857 /* Lastly update our global arg values. */
858 if (val->args && val->args->nargs)
859 XtSetValues (widget, val->args->args, val->args->nargs);
862 \f/* getting the value back */
864 xm_update_one_value (widget_instance* instance, Widget widget,
867 WidgetClass class = XtClass (widget);
868 widget_value *old_wv;
870 /* copy the call_data slot into the "return" widget_value */
871 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
872 if (!strcmp (val->name, old_wv->name))
874 val->call_data = old_wv->call_data;
878 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
881 XtSetArg (al [0], XmNset, &val->selected);
882 XtGetValues (widget, al, 1);
885 else if (class == xmTextWidgetClass)
889 val->value = XmTextGetString (widget);
892 else if (class == xmTextFieldWidgetClass)
896 val->value = XmTextFieldGetString (widget);
899 else if (class == xmRowColumnWidgetClass)
901 Boolean radiobox = 0;
904 XtSetArg (al [0], XmNradioBehavior, &radiobox);
905 XtGetValues (widget, al, 1);
910 CompositeWidget radio = (CompositeWidget)widget;
912 for (i = 0; i < radio->composite.num_children; i++)
915 Widget toggle = radio->composite.children [i];
918 XtSetArg (al [0], XmNset, &set);
919 XtGetValues (toggle, al, 1);
924 val->value = safe_strdup (XtName (toggle));
930 else if (class == xmListWidgetClass
931 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
932 || class == xmComboBoxWidgetClass
938 Widget list = widget;
939 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
940 if (class == xmComboBoxWidgetClass)
941 list = CB_List (widget);
943 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
947 for (cur = val->contents, i = 0; cur; cur = cur->next)
951 cur->selected = False;
953 for (j = 0; j < pos_cnt; j++)
954 if (pos_list [j] == i)
956 cur->selected = True;
957 val->value = safe_strdup (cur->name);
961 XtFree ((char *) pos_list);
964 #ifdef LWLIB_SCROLLBARS_MOTIF
965 else if (class == xmScrollBarWidgetClass)
967 /* This function is not used by the scrollbar. */
974 /* This function is for activating a button from a program. It's wrong because
975 we pass a NULL argument in the call_data which is not Motif compatible.
976 This is used from the XmNdefaultAction callback of the List widgets to
977 have a double-click put down a dialog box like the button would do.
978 I could not find a way to do that with accelerators.
981 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
983 Widget button = (Widget)closure;
984 XtCallCallbacks (button, XmNactivateCallback, NULL);
987 /* creation functions */
989 #ifdef LWLIB_DIALOGS_MOTIF
993 #if (XmVersion >= 1002)
994 # define ARMANDACTIVATE_KLUDGE
998 #ifdef ARMANDACTIVATE_KLUDGE
999 /* We want typing Return at a dialog box to select the default button; but
1000 we're satisfied with having it select the leftmost button instead.
1002 In Motif 1.1.5 we could do this by putting this resource in the
1005 *dialog*button1.accelerators:#override\
1006 <KeyPress>Return: ArmAndActivate()\n\
1007 <KeyPress>KP_Enter: ArmAndActivate()\n\
1008 Ctrl<KeyPress>m: ArmAndActivate()\n
1010 but that doesn't work with 1.2.1 and I don't understand why. However,
1011 doing the equivalent C code does work, with the notable disadvantage that
1012 the user can't override it. So that's what we do until we figure out
1013 something better....
1015 static char button_trans[] = "\
1016 <KeyPress>Return: ArmAndActivate()\n\
1017 <KeyPress>KP_Enter: ArmAndActivate()\n\
1018 Ctrl<KeyPress>m: ArmAndActivate()\n";
1020 #endif /* ARMANDACTIVATE_KLUDGE */
1024 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1025 was a segv down in libXm somewhere if you used the middle button on a
1026 dialog box to begin a drag; when you released the button to make a drop
1027 things would lose if you were not over the button where you started the
1028 drag (canceling the operation). This was probably due to the fact that
1029 the dialog boxes were not set up to handle a drag but were trying to do
1030 so anyway for some reason.
1032 So we disable drag-and-drop in dialog boxes by turning off the binding for
1033 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1034 solution as it only works in default configurations, but...
1036 static char disable_dnd_trans[] = "<Btn2Down>: ";
1037 #endif /* DND_KLUDGE */
1041 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1042 const char* shell_title, const char* icon_name,
1043 Boolean text_input_slot, Boolean radio_box, Boolean list,
1044 int left_buttons, int right_buttons)
1050 Widget icon_separator;
1055 Widget children [16]; /* for the final XtManageChildren */
1057 Arg al[64]; /* Arg List */
1058 int ac; /* Arg Count */
1062 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1063 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1064 #else /* ! DND_KLUDGE */
1065 # define DO_DND_KLUDGE(widget)
1066 #endif /* ! DND_KLUDGE */
1071 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1072 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1073 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1074 result = XmCreateDialogShell (parent, "dialog", al, ac);
1076 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1077 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1078 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1079 form = XmCreateForm (result, (char *) shell_title, al, ac);
1084 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1085 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1086 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1091 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1092 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1093 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1094 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1095 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1096 XtSetArg(al[ac], XmNspacing, 13); ac++;
1097 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1098 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1099 XtSetArg(al[ac], XmNisAligned, True); ac++;
1100 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1101 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1102 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1103 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1104 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1105 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1106 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1107 row = XmCreateRowColumn (form, "row", al, ac);
1110 for (i = 0; i < left_buttons; i++)
1112 char button_name [16];
1113 sprintf (button_name, "button%d", i + 1);
1117 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1118 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1120 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1121 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1122 DO_DND_KLUDGE (children [n_children]);
1126 button = children [n_children];
1128 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1129 XtSetValues (row, al, ac);
1131 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1133 XtTranslations losers = XtParseTranslationTable (button_trans);
1134 XtOverrideTranslations (button, losers);
1135 XtFree ((char *) losers);
1137 #endif /* ARMANDACTIVATE_KLUDGE */
1143 /* invisible seperator button */
1145 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1146 children [n_children] = XmCreateLabel (row, "separator_button",
1148 DO_DND_KLUDGE (children [n_children]);
1151 for (i = 0; i < right_buttons; i++)
1153 char button_name [16];
1154 sprintf (button_name, "button%d", left_buttons + i + 1);
1156 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1157 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1158 DO_DND_KLUDGE (children [n_children]);
1159 if (! button) button = children [n_children];
1163 XtManageChildren (children, n_children);
1166 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1167 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1168 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1169 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1170 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1171 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1172 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1173 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1174 separator = XmCreateSeparator (form, "", al, ac);
1177 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1178 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1179 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1180 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1181 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1182 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1183 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1184 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1185 DO_DND_KLUDGE (icon);
1188 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1189 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1190 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1191 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1192 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1193 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1194 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1195 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1196 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1197 icon_separator = XmCreateLabel (form, "", al, ac);
1198 DO_DND_KLUDGE (icon_separator);
1200 if (text_input_slot)
1203 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1204 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1205 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1206 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1207 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1208 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1209 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1210 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1211 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1212 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1213 value = XmCreateTextField (form, "value", al, ac);
1214 DO_DND_KLUDGE (value);
1220 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1221 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1222 XtSetArg(al[ac], XmNspacing, 13); ac++;
1223 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1224 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1225 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1226 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1227 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1228 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1229 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1230 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1231 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1232 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1233 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1236 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1237 children [i++] = radio_butt;
1238 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1239 children [i++] = radio_butt;
1240 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1241 children [i++] = radio_butt;
1242 XtManageChildren (children, i);
1247 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1248 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1249 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1250 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1251 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1252 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1253 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1254 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1255 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1256 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1257 value = XmCreateScrolledList (form, "list", al, ac);
1259 /* this is the easiest way I found to have the dble click in the
1260 list activate the default button */
1261 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1265 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1266 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1267 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1268 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1269 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1270 XtSetArg(al[ac], XmNbottomWidget,
1271 text_input_slot || radio_box || list ? value : separator); ac++;
1272 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1273 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1274 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1275 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1276 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1277 message = XmCreateLabel (form, "message", al, ac);
1278 DO_DND_KLUDGE (message);
1281 XtManageChild (value);
1284 children [i] = row; i++;
1285 children [i] = separator; i++;
1286 if (text_input_slot || radio_box)
1288 children [i] = value; i++;
1290 children [i] = message; i++;
1291 children [i] = icon; i++;
1292 children [i] = icon_separator; i++;
1293 XtManageChildren (children, i);
1295 if (text_input_slot || list)
1297 XtInstallAccelerators (value, button);
1298 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1302 XtInstallAccelerators (form, button);
1303 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1307 XtFree ((char *) dnd_override);
1309 #undef DO_DND_KLUDGE
1314 static destroyed_instance*
1315 find_matching_instance (widget_instance* instance)
1317 destroyed_instance* cur;
1318 destroyed_instance* prev;
1319 char* type = instance->info->type;
1320 char* name = instance->info->name;
1322 for (prev = NULL, cur = all_destroyed_instances;
1324 prev = cur, cur = cur->next)
1326 if (!strcmp (cur->name, name)
1327 && !strcmp (cur->type, type)
1328 && cur->parent == instance->parent
1329 && cur->pop_up_p == instance->pop_up_p)
1332 prev->next = cur->next;
1334 all_destroyed_instances = cur->next;
1337 /* do some cleanup */
1338 else if (!cur->widget)
1341 prev->next = cur->next;
1343 all_destroyed_instances = cur->next;
1344 free_destroyed_instance (cur);
1345 cur = prev ? prev : all_destroyed_instances;
1352 recenter_widget (Widget widget)
1354 Widget parent = XtParent (widget);
1355 Screen* screen = XtScreen (widget);
1356 Dimension screen_width = WidthOfScreen (screen);
1357 Dimension screen_height = HeightOfScreen (screen);
1358 Dimension parent_width = 0;
1359 Dimension parent_height = 0;
1360 Dimension child_width = 0;
1361 Dimension child_height = 0;
1366 XtSetArg (al [0], XtNwidth, &child_width);
1367 XtSetArg (al [1], XtNheight, &child_height);
1368 XtGetValues (widget, al, 2);
1370 XtSetArg (al [0], XtNwidth, &parent_width);
1371 XtSetArg (al [1], XtNheight, &parent_height);
1372 XtGetValues (parent, al, 2);
1374 x = (Position) ((parent_width - child_width) / 2);
1375 y = (Position) ((parent_height - child_height) / 2);
1377 XtTranslateCoords (parent, x, y, &x, &y);
1379 if ((Dimension) (x + child_width) > screen_width)
1380 x = screen_width - child_width;
1384 if ((Dimension) (y + child_height) > screen_height)
1385 y = screen_height - child_height;
1389 XtSetArg (al [0], XtNx, x);
1390 XtSetArg (al [1], XtNy, y);
1391 XtSetValues (widget, al, 2);
1395 recycle_instance (destroyed_instance* instance)
1397 Widget widget = instance->widget;
1399 /* widget is NULL if the parent was destroyed. */
1405 /* Remove the destroy callback as the instance is not in the list
1407 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1408 mark_dead_instance_destroyed,
1409 (XtPointer)instance);
1411 /* Give the focus to the initial item */
1412 focus = XtNameToWidget (widget, "*value");
1414 focus = XtNameToWidget (widget, "*button1");
1416 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1418 /* shrink the separator label back to their original size */
1419 separator = XtNameToWidget (widget, "*separator_button");
1423 XtSetArg (al [0], XtNwidth, 5);
1424 XtSetArg (al [1], XtNheight, 5);
1425 XtSetValues (separator, al, 2);
1428 /* Center the dialog in its parent */
1429 recenter_widget (widget);
1431 free_destroyed_instance (instance);
1436 xm_create_dialog (widget_instance* instance)
1438 char* name = instance->info->type;
1439 Widget parent = instance->parent;
1441 Boolean pop_up_p = instance->pop_up_p;
1442 const char* shell_name = 0;
1443 const char* icon_name = 0;
1444 Boolean text_input_slot = False;
1445 Boolean radio_box = False;
1446 Boolean list = False;
1448 int left_buttons = 0;
1449 int right_buttons = 1;
1450 destroyed_instance* dead_one;
1452 /* try to find a widget to recycle */
1453 dead_one = find_matching_instance (instance);
1456 Widget recycled_widget = recycle_instance (dead_one);
1457 if (recycled_widget)
1458 return recycled_widget;
1463 icon_name = "dbox-error";
1464 shell_name = "Error";
1468 icon_name = "dbox-info";
1469 shell_name = "Information";
1474 icon_name = "dbox-question";
1475 shell_name = "Prompt";
1479 text_input_slot = True;
1480 icon_name = "dbox-question";
1481 shell_name = "Prompt";
1485 icon_name = "dbox-question";
1486 shell_name = "Question";
1490 total_buttons = name [1] - '0';
1492 if (name [3] == 'T' || name [3] == 't')
1494 text_input_slot = False;
1498 right_buttons = name [4] - '0';
1500 left_buttons = total_buttons - right_buttons;
1502 widget = make_dialog (name, parent, pop_up_p,
1503 shell_name, icon_name, text_input_slot, radio_box,
1504 list, left_buttons, right_buttons);
1506 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1507 (XtPointer) instance);
1511 #endif /* LWLIB_DIALOGS_MOTIF */
1513 #ifdef LWLIB_MENUBARS_MOTIF
1515 make_menubar (widget_instance* instance)
1520 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1521 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1523 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1527 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1529 Widget menu = (Widget) closure;
1530 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1534 make_popup_menu (widget_instance* instance)
1536 Widget parent = instance->parent;
1537 Window parent_window = parent->core.window;
1540 /* sets the parent window to 0 to fool Motif into not generating a grab */
1541 parent->core.window = 0;
1542 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1543 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1545 parent->core.window = parent_window;
1548 #endif /* LWLIB_MENUBARS_MOTIF */
1550 #ifdef LWLIB_SCROLLBARS_MOTIF
1552 make_scrollbar (widget_instance *instance, int vertical)
1556 static XtCallbackRec callbacks[2] =
1557 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1559 callbacks[0].closure = (XtPointer) instance;
1561 XtSetArg (al[ac], XmNminimum, 1); ac++;
1562 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1563 XtSetArg (al[ac], XmNincrement, 1); ac++;
1564 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1565 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1566 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1568 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1569 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1570 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1571 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1572 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1573 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1574 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1575 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1577 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1581 make_vertical_scrollbar (widget_instance *instance)
1583 return make_scrollbar (instance, 1);
1587 make_horizontal_scrollbar (widget_instance *instance)
1589 return make_scrollbar (instance, 0);
1592 #endif /* LWLIB_SCROLLBARS_MOTIF */
1594 #ifdef LWLIB_WIDGETS_MOTIF
1597 xm_create_button (widget_instance *instance)
1602 widget_value* val = instance->info->val;
1604 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1605 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1606 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1607 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1608 /* The highlight doesn't appear to be dynamically set which makes it
1609 look ugly. I think this may be a LessTif bug but for now we just
1611 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1613 /* add any args the user supplied for creation time */
1614 lw_add_value_args_to_args (val, al, &ac);
1616 if (!val->call_data)
1617 button = XmCreateLabel (instance->parent, val->name, al, ac);
1619 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1621 XtSetArg (al [ac], XmNset, val->selected); ac++;
1622 XtSetArg (al [ac], XmNindicatorType,
1623 (val->type == TOGGLE_TYPE ?
1624 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1625 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1626 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1627 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1628 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1629 (XtPointer)instance);
1633 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1634 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1635 (XtPointer)instance);
1638 XtManageChild (button);
1644 xm_create_progress (widget_instance *instance)
1649 widget_value* val = instance->info->val;
1650 #if 0 /* This looks too awful, although more correct. */
1651 if (!val->call_data)
1653 XtSetArg (al [ac], XmNsensitive, False); ac++;
1657 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1660 XtSetArg (al [ac], XmNsensitive, True); ac++;
1662 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1663 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1664 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1665 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1666 /* The highlight doesn't appear to be dynamically set which makes it
1667 look ugly. I think this may be a LessTif bug but for now we just
1669 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1670 /* add any args the user supplied for creation time */
1671 lw_add_value_args_to_args (val, al, &ac);
1673 scale = XmCreateScale (instance->parent, val->name, al, ac);
1675 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1676 (XtPointer)instance);
1678 XtManageChild (scale);
1684 xm_create_text_field (widget_instance *instance)
1689 widget_value* val = instance->info->val;
1691 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1692 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1693 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1694 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1695 /* The highlight doesn't appear to be dynamically set which makes it
1696 look ugly. I think this may be a LessTif bug but for now we just
1698 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1700 /* add any args the user supplied for creation time */
1701 lw_add_value_args_to_args (val, al, &ac);
1703 text = XmCreateTextField (instance->parent, val->name, al, ac);
1705 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1706 (XtPointer)instance);
1708 XtManageChild (text);
1714 xm_create_label_field (widget_instance *instance)
1716 return xm_create_label (instance->parent, instance->info->val);
1720 xm_create_label (Widget parent, widget_value* val)
1726 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1727 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1728 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1729 /* The highlight doesn't appear to be dynamically set which makes it
1730 look ugly. I think this may be a LessTif bug but for now we just
1732 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1734 /* add any args the user supplied for creation time */
1735 lw_add_value_args_to_args (val, al, &ac);
1737 label = XmCreateLabel (parent, val->name, al, ac);
1739 XtManageChild (label);
1741 /* Do it again for arguments that have no effect until the widget is realized. */
1743 lw_add_value_args_to_args (val, al, &ac);
1744 XtSetValues (label, al, ac);
1751 xm_create_combo_box (widget_instance *instance)
1756 widget_value* val = instance->info->val;
1758 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1759 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1760 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1761 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1762 /* The highlight doesn't appear to be dynamically set which makes it
1763 look ugly. I think this may be a LessTif bug but for now we just
1765 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1767 /* add any args the user supplied for creation time */
1768 lw_add_value_args_to_args (val, al, &ac);
1770 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1772 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1773 (XtPointer)instance);
1775 XtManageChild (combo);
1780 #endif /* LWLIB_WIDGETS_MOTIF */
1783 /* Table of functions to create widgets */
1785 widget_creation_entry
1786 xm_creation_table [] =
1788 #ifdef LWLIB_MENUBARS_MOTIF
1789 {"menubar", make_menubar},
1790 {"popup", make_popup_menu},
1792 #ifdef LWLIB_SCROLLBARS_MOTIF
1793 {"vertical-scrollbar", make_vertical_scrollbar},
1794 {"horizontal-scrollbar", make_horizontal_scrollbar},
1796 #ifdef LWLIB_WIDGETS_MOTIF
1797 {"button", xm_create_button},
1798 {"progress", xm_create_progress},
1799 {"text-field", xm_create_text_field},
1800 {"label", xm_create_label_field},
1802 {"combo-box", xm_create_combo_box},
1808 \f/* Destruction of instances */
1810 xm_destroy_instance (widget_instance* instance)
1812 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1813 /* It appears that this is used only for dialog boxes. */
1814 Widget widget = instance->widget;
1815 /* recycle the dialog boxes */
1816 /* Disable the recycling until we can find a way to have the dialog box
1817 get reasonable layout after we modify its contents. */
1819 && XtClass (widget) == xmDialogShellWidgetClass)
1821 destroyed_instance* dead_instance =
1822 make_destroyed_instance (instance->info->name,
1823 instance->info->type,
1826 instance->pop_up_p);
1827 dead_instance->next = all_destroyed_instances;
1828 all_destroyed_instances = dead_instance;
1829 XtUnmanageChild (first_child (instance->widget));
1830 XFlush (XtDisplay (instance->widget));
1831 XtAddCallback (instance->parent, XtNdestroyCallback,
1832 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1836 /* This might not be necessary now that the nosel is attached to
1837 popdown instead of destroy, but it can't hurt. */
1838 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1839 xm_nosel_callback, (XtPointer)instance);
1841 XtDestroyWidget (instance->widget);
1843 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1846 \f/* popup utility */
1847 #ifdef LWLIB_MENUBARS_MOTIF
1850 xm_popup_menu (Widget widget, XEvent *event)
1852 if (event->type == ButtonPress || event->type == ButtonRelease)
1854 /* This is so totally ridiculous: there's NO WAY to tell Motif
1855 that *any* button can select a menu item. Only one button
1856 can have that honor.
1859 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1860 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1861 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1862 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1863 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1867 XtSetArg (al [0], XmNmenuPost, trans);
1868 XtSetValues (widget, al, 1);
1870 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1872 XtManageChild (widget);
1877 #ifdef LWLIB_DIALOGS_MOTIF
1880 set_min_dialog_size (Widget w)
1886 XtSetArg (al [0], XmNwidth, &width);
1887 XtSetArg (al [1], XmNheight, &height);
1888 XtGetValues (w, al, 2);
1890 XtSetArg (al [0], XmNminWidth, width);
1891 XtSetArg (al [1], XmNminHeight, height);
1892 XtSetValues (w, al, 2);
1898 xm_pop_instance (widget_instance* instance, Boolean up)
1900 Widget widget = instance->widget;
1902 #ifdef LWLIB_DIALOGS_MOTIF
1903 if (XtClass (widget) == xmDialogShellWidgetClass)
1905 Widget widget_to_manage = first_child (widget);
1908 XtManageChild (widget_to_manage);
1909 set_min_dialog_size (widget);
1910 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1913 XtUnmanageChild (widget_to_manage);
1919 XtManageChild (widget);
1921 XtUnmanageChild (widget);
1926 /* motif callback */
1928 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1931 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1933 XtPointer user_data;
1934 widget_instance* instance = (widget_instance*)closure;
1935 Widget instance_widget;
1941 if (widget->core.being_destroyed)
1944 instance_widget = instance->widget;
1945 if (!instance_widget)
1948 id = instance->info->id;
1950 XtSetArg(al [0], XmNuserData, &user_data);
1951 XtGetValues (widget, al, 1);
1955 if (instance->info->pre_activate_cb)
1956 instance->info->pre_activate_cb (widget, id, user_data);
1959 if (instance->info->selection_cb)
1960 instance->info->selection_cb (widget, id, user_data);
1963 if (instance->info->selection_cb)
1964 instance->info->selection_cb (widget, id, (XtPointer) -1);
1967 if (instance->info->post_activate_cb)
1968 instance->info->post_activate_cb (widget, id, user_data);
1975 /* Like lw_internal_update_other_instances except that it does not do
1976 anything if its shell parent is not managed. This is to protect
1977 lw_internal_update_other_instances to dereference freed memory
1978 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1981 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1982 XtPointer call_data)
1985 for (parent = widget; parent; parent = XtParent (parent))
1986 if (XtIsShell (parent))
1988 else if (!XtIsManaged (parent))
1990 lw_internal_update_other_instances (widget, closure, call_data);
1994 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1996 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
1997 /* We want the selected status to change only when we decide it
1998 should change. Yuck but correct. */
1999 if (XtClass (widget) == xmToggleButtonWidgetClass
2000 || XtClass (widget) == xmToggleButtonGadgetClass)
2005 XtSetArg (al [0], XmNset, &check);
2006 XtGetValues (widget, al, 1);
2008 XtSetArg (al [0], XmNset, !check);
2009 XtSetValues (widget, al, 1);
2012 lw_internal_update_other_instances (widget, closure, call_data);
2013 do_call (widget, closure, selection);
2017 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2019 do_call (widget, closure, post_activate);
2022 #ifdef LWLIB_MENUBARS_MOTIF
2025 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2030 /* new behavior for incremental menu construction */
2035 do_call (widget, closure, pre_activate);
2038 #endif /* LWLIB_MENUBARS_MOTIF */
2040 #ifdef LWLIB_SCROLLBARS_MOTIF
2042 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2044 widget_instance *instance = (widget_instance *) closure;
2046 XmScrollBarCallbackStruct *data =
2047 (XmScrollBarCallbackStruct *) call_data;
2048 scroll_event event_data;
2049 scrollbar_values *val =
2050 (scrollbar_values *) instance->info->val->scrollbar_data;
2053 if (!instance || widget->core.being_destroyed)
2056 id = instance->info->id;
2058 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2059 event_data.slider_value =
2060 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2062 if (event_data.slider_value > (val->maximum - val->slider_size))
2063 event_data.slider_value = val->maximum - val->slider_size;
2064 else if (event_data.slider_value < 1)
2065 event_data.slider_value = 1;
2069 switch (data->event->xany.type)
2073 event_data.time = data->event->xkey.time;
2077 event_data.time = data->event->xbutton.time;
2080 event_data.time = data->event->xmotion.time;
2084 event_data.time = data->event->xcrossing.time;
2087 event_data.time = 0;
2092 event_data.time = 0;
2094 switch (data->reason)
2096 case XmCR_DECREMENT:
2097 event_data.action = SCROLLBAR_LINE_UP;
2099 case XmCR_INCREMENT:
2100 event_data.action = SCROLLBAR_LINE_DOWN;
2102 case XmCR_PAGE_DECREMENT:
2103 event_data.action = SCROLLBAR_PAGE_UP;
2105 case XmCR_PAGE_INCREMENT:
2106 event_data.action = SCROLLBAR_PAGE_DOWN;
2109 event_data.action = SCROLLBAR_TOP;
2111 case XmCR_TO_BOTTOM:
2112 event_data.action = SCROLLBAR_BOTTOM;
2115 event_data.action = SCROLLBAR_DRAG;
2117 case XmCR_VALUE_CHANGED:
2118 event_data.action = SCROLLBAR_CHANGE;
2121 event_data.action = SCROLLBAR_CHANGE;
2125 if (instance->info->pre_activate_cb)
2126 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2128 #endif /* LWLIB_SCROLLBARS_MOTIF */
2130 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2132 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2133 XtPointer call_data)
2135 destroyed_instance* instance = (destroyed_instance*)closure;
2136 instance->widget = NULL;
2140 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2142 /* This callback is only called when a dialog box is dismissed with the wm's
2143 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2144 in that case, not just unmapped, so that it releases its keyboard grabs.
2145 But there are problems with running our callbacks while the widget is in
2146 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2147 instead of XmDESTROY and then destroy it ourself after having run the
2150 do_call (widget, closure, no_selection);
2151 XtDestroyWidget (widget);
2156 /* set the keyboard focus */
2158 xm_set_keyboard_focus (Widget parent, Widget w)
2160 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2161 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2162 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2163 Presumably the problem was elsewhere, and is now gone...