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
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
12 The Lucid Widget Library is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <X11/StringDefs.h>
30 #include <X11/IntrinsicP.h>
31 #include <X11/ObjectP.h>
32 #include <X11/CoreP.h>
33 #include <X11/CompositeP.h>
36 #include "lwlib-utils.h"
39 #include <Xm/BulletinB.h>
40 #include <Xm/CascadeB.h>
41 #include <Xm/DrawingA.h>
42 #include <Xm/FileSB.h>
45 #include <Xm/MenuShell.h>
46 #include <Xm/MessageB.h>
48 #include <Xm/PushBG.h>
49 #include <Xm/ArrowB.h>
50 #include <Xm/ScrollBar.h>
51 #include <Xm/SelectioB.h>
54 #include <Xm/ToggleB.h>
55 #include <Xm/ToggleBG.h>
56 #include <Xm/RowColumn.h>
57 #include <Xm/ScrolledW.h>
58 #include <Xm/Separator.h>
59 #include <Xm/DialogS.h>
61 #ifdef LWLIB_WIDGETS_MOTIF
64 #include <Xm/ComboBoxP.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, XtPointer);
72 static void xm_pop_down_callback(Widget, XtPointer, XtPointer);
73 static void xm_generic_callback(Widget, XtPointer, XtPointer);
74 static void mark_dead_instance_destroyed(Widget widget, XtPointer closure,
76 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
77 static void xm_nosel_callback(Widget, XtPointer, XtPointer);
79 #ifdef LWLIB_SCROLLBARS_MOTIF
80 static void xm_scrollbar_callback(Widget, XtPointer, XtPointer);
83 #ifdef LWLIB_MENUBARS_MOTIF
85 xm_update_menu(widget_instance * instance, Widget widget, widget_value * val,
89 /* Structures to keep destroyed instances */
90 typedef struct _destroyed_instance {
96 struct _destroyed_instance *next;
99 static destroyed_instance *all_destroyed_instances = NULL;
101 /* Utility function. */
102 static char *safe_strdup(char *s)
107 result = (char *)malloc(strlen(s) + 1);
114 static destroyed_instance *make_destroyed_instance(char *name, char *type,
115 Widget widget, Widget parent,
118 destroyed_instance *instance =
119 (destroyed_instance *) malloc(sizeof(destroyed_instance));
120 instance->name = safe_strdup(name);
121 instance->type = safe_strdup(type);
122 instance->widget = widget;
123 instance->parent = parent;
124 instance->pop_up_p = pop_up_p;
125 instance->next = NULL;
129 static void free_destroyed_instance(destroyed_instance * instance)
131 free(instance->name);
132 free(instance->type);
136 /* motif utility functions */
137 Widget first_child(Widget widget)
139 return ((CompositeWidget) widget)->composite.children[0];
142 Boolean lw_motif_widget_p(Widget widget)
145 #ifdef LWLIB_DIALOGS_MOTIF
146 XtClass(widget) == xmDialogShellWidgetClass ||
148 XmIsPrimitive(widget) || XmIsManager(widget) || XmIsGadget(widget);
151 static char *resource_string(Widget widget, char *name)
156 resource.resource_name = "labelString";
157 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
158 resource.resource_type = XtRString;
159 resource.resource_size = sizeof(String);
160 resource.resource_offset = 0;
161 resource.default_type = XtRImmediate;
162 resource.default_addr = 0;
164 XtGetSubresources(widget, (XtPointer) & result, name,
165 name, &resource, 1, NULL, 0);
169 #ifdef LWLIB_DIALOGS_MOTIF
171 static Boolean is_in_dialog_box(Widget w)
175 wmshell = XtParent(w);
176 while (wmshell && (XtClass(wmshell) != xmDialogShellWidgetClass))
177 wmshell = XtParent(wmshell);
179 if (wmshell && XtClass(wmshell) == xmDialogShellWidgetClass)
185 #endif /* LWLIB_DIALOGS_MOTIF */
187 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
189 /* update the label of anything subclass of a label */
191 xm_update_label(widget_instance * instance, Widget widget, widget_value * val)
193 XmString built_string = NULL;
194 XmString key_string = NULL;
195 XmString val_string = NULL;
196 XmString name_string = NULL;
201 /* Don't clobber pixmap types. */
202 XtSetArg(al[0], XmNlabelType, &type);
203 XtGetValues(widget, al, 1);
205 if (type == XmPIXMAP)
209 /* #### Temporary fix. I though Motif was supposed to grok %_
211 lw_remove_accelerator_spec(val->value);
213 #ifdef LWLIB_DIALOGS_MOTIF
215 * Sigh. The main text of a label is the name field for menubar
216 * entries. The value field is a possible additional field to be
217 * concatenated on to the name field. HOWEVER, with dialog boxes
218 * the value field is the complete text which is supposed to be
219 * displayed as the label. Yuck.
221 if (is_in_dialog_box(widget)) {
222 char *value_name = NULL;
224 value_name = resource_string(widget, val->value);
226 value_name = val->value;
229 XmStringCreateLtoR(value_name,
230 XmSTRING_DEFAULT_CHARSET);
232 #endif /* LWLIB_DIALOGS_MOTIF */
234 char *value_name = NULL;
235 char *res_name = NULL;
237 res_name = resource_string(widget, val->name);
238 /* Concatenating the value with itself seems just plain daft. */
241 XmStringCreateLtoR(val->value,
242 XmSTRING_DEFAULT_CHARSET);
245 XmStringCreateLtoR(res_name,
246 XmSTRING_DEFAULT_CHARSET);
248 value_name = XtMalloc(strlen(val->value) + 2);
250 strcat(value_name, " ");
251 strcat(value_name, val->value);
254 XmStringCreateLtoR(value_name,
255 XmSTRING_DEFAULT_CHARSET);
258 XmStringConcat(name_string, val_string);
264 XtSetArg(al[ac], XmNlabelString, built_string);
266 XtSetArg(al[ac], XmNlabelType, XmSTRING);
272 XmStringCreateLtoR(val->key, XmSTRING_DEFAULT_CHARSET);
273 XtSetArg(al[ac], XmNacceleratorText, key_string);
278 XtSetValues(widget, al, ac);
281 XmStringFree(built_string);
284 XmStringFree(key_string);
287 XmStringFree(name_string);
290 XmStringFree(val_string);
294 xm_safe_update_label(widget_instance * instance, Widget widget,
297 /* Don't clobber non-labels. */
298 if (XtIsSubclass(widget, xmLabelWidgetClass))
299 xm_update_label(instance, widget, val);
302 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
306 xm_update_list(widget_instance * instance, Widget widget, widget_value * val)
310 XtRemoveAllCallbacks(widget, XmNsingleSelectionCallback);
311 XtAddCallback(widget, XmNsingleSelectionCallback, xm_generic_callback,
313 for (cur = val->contents, i = 0; cur; cur = cur->next)
316 XmStringCreate(cur->value,
317 XmSTRING_DEFAULT_CHARSET);
319 XmListAddItem(widget, xmstr, 0);
321 XmListSelectPos(widget, i, False);
326 /* update of buttons */
328 xm_update_pushbutton(widget_instance * instance, Widget widget,
332 XtSetArg(al[0], XmNalignment, XmALIGNMENT_CENTER);
333 XtSetValues(widget, al, 1);
334 XtRemoveAllCallbacks(widget, XmNactivateCallback);
335 XtAddCallback(widget, XmNactivateCallback, xm_generic_callback,
339 #ifdef LWLIB_WIDGETS_MOTIF
341 xm_update_progress(widget_instance * instance, Widget scale, widget_value * val)
345 Dimension height = 0;
347 if (!val->call_data) {
348 XtSetArg(al[ac], XmNeditable, False);
351 XtSetArg(al[ac], XmNeditable, val->enabled);
354 height = (Dimension) lw_get_value_arg(val, XtNheight);
355 width = (Dimension) lw_get_value_arg(val, XtNwidth);
357 XtSetArg(al[ac], XmNscaleHeight, height);
361 XtSetArg(al[ac], XmNscaleWidth, width);
365 XtSetValues(scale, al, 1);
367 #endif /* LWLIB_WIDGETS_MOTIF */
369 #ifdef LWLIB_MENUBARS_MOTIF
372 xm_update_cascadebutton(widget_instance * instance, Widget widget,
375 /* Should also rebuild the menu by calling ...update_menu... */
377 && val->type == CASCADE_TYPE
378 && val->contents && val->contents->type == INCREMENTAL_TYPE) {
379 /* okay, we're now doing a lisp callback to incrementally generate
381 XtRemoveAllCallbacks(widget, XmNcascadingCallback);
382 XtAddCallback(widget, XmNcascadingCallback,
383 xm_pull_down_callback, instance);
384 XtCallCallbacks((Widget) widget, XmNcascadingCallback,
385 (XtPointer) val->contents);
388 XtRemoveAllCallbacks(widget, XmNcascadingCallback);
389 XtAddCallback(widget, XmNcascadingCallback,
390 xm_pull_down_callback, instance);
394 #endif /* LWLIB_MENUBARS_MOTIF */
396 /* update toggle and radiobox */
398 xm_update_toggle(widget_instance * instance, Widget widget, widget_value * val)
401 XtRemoveAllCallbacks(widget, XmNvalueChangedCallback);
402 XtAddCallback(widget, XmNvalueChangedCallback, xm_generic_callback,
404 XtSetArg(al[0], XmNset, val->selected);
405 XtSetArg(al[1], XmNalignment, XmALIGNMENT_BEGINNING);
406 XtSetValues(widget, al, 1);
410 xm_update_radiobox(widget_instance * instance, Widget widget,
416 /* update the callback */
417 XtRemoveAllCallbacks(widget, XmNentryCallback);
418 XtAddCallback(widget, XmNentryCallback, xm_generic_callback, instance);
420 /* first update all the toggles */
421 /* Energize kernel interface is currently bad. It sets the selected widget
422 with the selected flag but returns it by its name. So we currently
423 have to support both setting the selection with the selected slot
424 of val contents and setting it with the "value" slot of val. The latter
425 has a higher priority. This to be removed when the kernel is fixed. */
426 for (cur = val->contents; cur; cur = cur->next) {
427 toggle = XtNameToWidget(widget, cur->value);
430 XtSetArg(al[0], XmNsensitive, cur->enabled);
431 XtSetArg(al[1], XmNset,
433 && cur->selected ? cur->selected : False));
434 XtSetValues(toggle, al, 2);
438 /* The selected was specified by the value slot */
440 toggle = XtNameToWidget(widget, val->value);
443 XtSetArg(al[0], XmNset, True);
444 XtSetValues(toggle, al, 1);
449 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
450 /* update of combo box */
452 xm_update_combo_box(widget_instance * instance, Widget widget,
457 XtRemoveAllCallbacks(widget, XmNselectionCallback);
458 XtAddCallback(widget, XmNselectionCallback, xm_generic_callback,
460 for (cur = val->contents, i = 0; cur; cur = cur->next)
463 XmStringCreate(cur->value,
464 XmSTRING_DEFAULT_CHARSET);
466 XmListAddItem(CB_List(widget), xmstr, 0);
468 XmListSelectPos(CB_List(widget), i, False);
474 #ifdef LWLIB_MENUBARS_MOTIF
476 /* update a popup menu, pulldown menu or a menubar */
478 make_menu_in_widget(widget_instance * instance, Widget widget,
481 Widget *children = 0;
489 Boolean menubar_p = False;
491 /* Allocate the children array */
492 for (num_children = 0, cur = val; cur;
493 num_children++, cur = cur->next) ;
494 children = (Widget *) XtMalloc(num_children * sizeof(Widget));
496 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
497 XtSetArg(al[0], XmNisHomogeneous, &menubar_p);
498 XtGetValues(widget, al, 1);
500 /* add the unmap callback for popups and pulldowns */
501 /*** this sounds bogus ***/
502 /* probably because it is -- cet */
505 XtAddCallback (XtParent (widget), XmNpopdownCallback,
506 xm_pop_down_callback, (XtPointer)instance);
510 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next) {
513 XtSetArg(al[ac], XmNsensitive, cur->enabled);
515 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
517 XtSetArg(al[ac], XmNuserData, cur->call_data);
522 /* A pushright marker which is not needed for the real Motif
528 /* #### - xlwmenu.h supports several types that motif does
529 not. Also, motif supports pixmaps w/ type NO_LINE and
530 lwlib provides no way to access that functionality. --Stig */
531 XtSetArg(al[ac], XmNseparatorType, cur->value),
534 button = XmCreateSeparator(widget, "separator", al, ac);
538 XmCreatePulldownMenu(widget, "pulldown", NULL, 0);
539 make_menu_in_widget(instance, menu, cur->contents);
540 XtSetArg(al[ac], XmNsubMenuId, menu);
543 XmCreateCascadeButton(widget, cur->name, al, ac);
545 xm_safe_update_label(instance, button, cur);
547 XtAddCallback(button, XmNcascadingCallback,
548 xm_pull_down_callback,
549 (XtPointer) instance);
554 XmCreateCascadeButton(widget, cur->name, al,
556 else if (!cur->call_data)
558 XmCreateLabel(widget, cur->name, al, ac);
559 else if (cur->type == TOGGLE_TYPE
560 || cur->type == RADIO_TYPE) {
561 XtSetArg(al[ac], XmNindicatorType,
563 TOGGLE_TYPE ? XmN_OF_MANY :
566 XtSetArg(al[ac], XmNvisibleWhenOff, True);
569 XmCreateToggleButtonGadget(widget,
574 XmCreatePushButtonGadget(widget, cur->name,
577 xm_safe_update_label(instance, button, cur);
579 /* don't add a callback to a simple label */
580 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
581 xm_update_toggle(instance, button, cur);
582 else if (cur->call_data)
583 XtAddCallback(button, XmNactivateCallback,
585 (XtPointer) instance);
586 } /* switch (cur->type) */
589 children[num_children++] = button;
592 /* Last entry is the help button. This used be done after managing
593 the buttons. The comment claimed that it had to be done this way
594 otherwise the menubar ended up only 4 pixels high. That must
595 have been in the Old World. In the New World it stays the proper
596 height if you don't manage them until after you set this and as a
597 bonus the Help menu ends up where it is supposed to. */
600 XtSetArg(al[ac], XmNmenuHelpWidget, button);
602 XtSetValues(widget, al, ac);
606 XtManageChildren(children, num_children);
608 XtFree((char *)children);
612 update_one_menu_entry(widget_instance * instance, Widget widget,
613 widget_value * val, Boolean deep_p)
618 widget_value *contents;
620 if (val->change == NO_CHANGE)
623 /* update the sensitivity and userdata */
624 /* Common to all widget types */
625 XtSetArg(al[0], XmNsensitive, val->enabled);
626 XtSetArg(al[1], XmNuserData, val->call_data);
627 XtSetValues(widget, al, 2);
629 /* update the menu button as a label. */
630 if (val->change >= VISIBLE_CHANGE) {
631 xm_safe_update_label(instance, widget, val);
633 if (XtClass(widget) == xmToggleButtonWidgetClass
634 || XtClass(widget) == xmToggleButtonGadgetClass) {
635 xm_update_toggle(instance, widget, val);
639 /* update the pulldown/pullaside as needed */
641 XtSetArg(al[0], XmNsubMenuId, &menu);
642 XtGetValues(widget, al, 1);
644 contents = val->contents;
649 XmCreatePulldownMenu(widget, "pulldown", NULL, 0);
650 make_menu_in_widget(instance, menu, contents);
652 XtSetArg(al[ac], XmNsubMenuId, menu);
654 XtSetValues(widget, al, ac);
656 } else if (!contents) {
658 XtSetArg(al[ac], XmNsubMenuId, NULL);
660 XtSetValues(widget, al, ac);
661 XtDestroyWidget(menu);
662 } else if (deep_p && contents->change != NO_CHANGE)
663 xm_update_menu(instance, menu, val, 1);
667 xm_update_menu(widget_instance * instance, Widget widget, widget_value * val,
670 /* Widget is a RowColumn widget whose contents have to be updated
671 * to reflect the list of items in val->contents */
672 if (val->contents->change == STRUCTURAL_CHANGE) {
673 destroy_all_children(widget);
674 make_menu_in_widget(instance, widget, val->contents);
676 /* Update all the buttons of the RowColumn in order. */
678 unsigned int num_children;
680 widget_value *cur = 0;
682 children = XtCompositeChildren(widget, &num_children);
684 for (i = 0, cur = val->contents; i < num_children; i++) {
687 /* skip if this is a pushright marker or a separator */
688 if (cur->type == PUSHRIGHT_TYPE
689 || cur->type == SEPARATOR_TYPE) {
692 /* #### - this could puke if you have a separator as the
693 last item on a pullright menu. */
701 if (children[i]->core.being_destroyed
702 || strcmp(XtName(children[i]), cur->name))
704 update_one_menu_entry(instance, children[i],
708 XtFree((char *)children);
715 #endif /* LWLIB_MENUBARS_MOTIF */
717 /* update text widgets */
720 xm_update_text(widget_instance * instance, Widget widget, widget_value * val)
722 XmTextSetString(widget, val->value ? val->value : (char *)"");
723 XtRemoveAllCallbacks(widget, XmNactivateCallback);
724 XtAddCallback(widget, XmNactivateCallback, xm_generic_callback,
726 XtRemoveAllCallbacks(widget, XmNvalueChangedCallback);
727 XtAddCallback(widget, XmNvalueChangedCallback,
728 xm_internal_update_other_instances, instance);
732 xm_update_text_field(widget_instance * instance, Widget widget,
735 XmTextFieldSetString(widget, val->value ? val->value : (char *)"");
736 XtRemoveAllCallbacks(widget, XmNactivateCallback);
737 XtAddCallback(widget, XmNactivateCallback, xm_generic_callback,
739 XtRemoveAllCallbacks(widget, XmNvalueChangedCallback);
740 XtAddCallback(widget, XmNvalueChangedCallback,
741 xm_internal_update_other_instances, instance);
744 #ifdef LWLIB_SCROLLBARS_MOTIF
747 * If this function looks like it does a lot more work than it needs to,
748 * you're right. Blame the Motif scrollbar for not being smart about
749 * updating its appearance.
752 xm_update_scrollbar(widget_instance * instance, Widget widget,
755 if (val->scrollbar_data) {
756 scrollbar_values *data = val->scrollbar_data;
757 int widget_sliderSize, widget_val;
758 int new_sliderSize, new_value;
760 double h_water, l_water;
763 /* First size and position the scrollbar widget. */
764 XtSetArg(al[0], XtNx, data->scrollbar_x);
765 XtSetArg(al[1], XtNy, data->scrollbar_y);
766 XtSetArg(al[2], XtNwidth, data->scrollbar_width);
767 XtSetArg(al[3], XtNheight, data->scrollbar_height);
768 XtSetValues(widget, al, 4);
770 /* Now size the scrollbar's slider. */
771 XtSetArg(al[0], XmNsliderSize, &widget_sliderSize);
772 XtSetArg(al[1], XmNvalue, &widget_val);
773 XtGetValues(widget, al, 2);
775 percent = (double)data->slider_size /
776 (double)(data->maximum - data->minimum);
777 new_sliderSize = (int)((double)(INT_MAX - 1) * percent);
779 percent = (double)(data->slider_position - data->minimum) /
780 (double)(data->maximum - data->minimum);
781 new_value = (int)((double)(INT_MAX - 1) * percent);
783 if (new_sliderSize > (INT_MAX - 1))
784 new_sliderSize = INT_MAX - 1;
785 else if (new_sliderSize < 1)
788 if (new_value > (INT_MAX - new_sliderSize))
789 new_value = INT_MAX - new_sliderSize;
790 else if (new_value < 1)
795 if (new_sliderSize != widget_sliderSize
796 || new_value != widget_val) {
797 int force = ((INT_MAX - widget_sliderSize - widget_val)
799 : (INT_MAX - new_sliderSize - new_value));
802 || (double)new_sliderSize <
803 (l_water * (double)widget_sliderSize)
804 || (double)new_sliderSize >
805 (h_water * (double)widget_sliderSize)
806 || (double)new_value <
807 (l_water * (double)widget_val)
808 || (double)new_value >
809 (h_water * (double)widget_val)) {
810 XmScrollBarSetValues(widget, new_value,
811 new_sliderSize, 1, 1,
818 #endif /* LWLIB_SCROLLBARS_MOTIF */
820 /* update a motif widget */
823 xm_update_one_widget(widget_instance * instance, Widget widget,
824 widget_value * val, Boolean deep_p)
830 /* Mark as not edited */
833 /* Common to all widget types */
834 XtSetArg(al[ac], XmNsensitive, val->enabled);
836 XtSetArg(al[ac], XmNuserData, val->call_data);
838 XtSetValues(widget, al, ac);
840 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
841 /* Common to all label like widgets */
842 xm_safe_update_label(instance, widget, val);
844 class = XtClass(widget);
845 /* Class specific things */
846 if (class == xmPushButtonWidgetClass ||
847 class == xmArrowButtonWidgetClass) {
848 xm_update_pushbutton(instance, widget, val);
850 #ifdef LWLIB_MENUBARS_MOTIF
851 else if (class == xmCascadeButtonWidgetClass) {
852 xm_update_cascadebutton(instance, widget, val);
855 else if (class == xmToggleButtonWidgetClass
856 || class == xmToggleButtonGadgetClass) {
857 xm_update_toggle(instance, widget, val);
858 } else if (class == xmRowColumnWidgetClass) {
859 Boolean radiobox = 0;
861 XtSetArg(al[0], XmNradioBehavior, &radiobox);
862 XtGetValues(widget, al, 1);
865 xm_update_radiobox(instance, widget, val);
866 #ifdef LWLIB_MENUBARS_MOTIF
868 xm_update_menu(instance, widget, val, deep_p);
870 } else if (class == xmTextWidgetClass) {
871 xm_update_text(instance, widget, val);
872 } else if (class == xmTextFieldWidgetClass) {
873 xm_update_text_field(instance, widget, val);
874 } else if (class == xmListWidgetClass) {
875 xm_update_list(instance, widget, val);
877 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
878 else if (class == xmComboBoxWidgetClass) {
879 xm_update_combo_box(instance, widget, val);
882 #ifdef LWLIB_SCROLLBARS_MOTIF
883 else if (class == xmScrollBarWidgetClass) {
884 xm_update_scrollbar(instance, widget, val);
887 #ifdef LWLIB_WIDGETS_MOTIF
888 else if (class == xmScaleWidgetClass) {
889 xm_update_progress(instance, widget, val);
892 /* Lastly update our global arg values. */
893 if (val->args && val->args->nargs)
894 XtSetValues(widget, val->args->args, val->args->nargs);
897 /* getting the value back */
899 xm_update_one_value(widget_instance * instance, Widget widget,
902 WidgetClass class = XtClass(widget);
903 widget_value *old_wv;
905 /* copy the call_data slot into the "return" widget_value */
906 for (old_wv = instance->info->val->contents; old_wv;
907 old_wv = old_wv->next)
908 if (!strcmp(val->name, old_wv->name)) {
909 val->call_data = old_wv->call_data;
913 if (class == xmToggleButtonWidgetClass
914 || class == xmToggleButtonGadgetClass) {
916 XtSetArg(al[0], XmNset, &val->selected);
917 XtGetValues(widget, al, 1);
919 } else if (class == xmTextWidgetClass) {
922 val->value = XmTextGetString(widget);
924 } else if (class == xmTextFieldWidgetClass) {
927 val->value = XmTextFieldGetString(widget);
929 } else if (class == xmRowColumnWidgetClass) {
930 Boolean radiobox = 0;
933 XtSetArg(al[0], XmNradioBehavior, &radiobox);
934 XtGetValues(widget, al, 1);
938 CompositeWidget radio = (CompositeWidget) widget;
940 for (i = 0; i < radio->composite.num_children; i++) {
942 Widget toggle = radio->composite.children[i];
945 XtSetArg(al[0], XmNset, &set);
946 XtGetValues(toggle, al, 1);
951 safe_strdup(XtName(toggle));
956 } else if (class == xmListWidgetClass
957 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
958 || class == xmComboBoxWidgetClass
963 Widget list = widget;
964 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
965 if (class == xmComboBoxWidgetClass)
966 list = CB_List(widget);
968 if (XmListGetSelectedPos(list, &pos_list, &pos_cnt)) {
971 for (cur = val->contents, i = 0; cur; cur = cur->next)
974 cur->selected = False;
976 for (j = 0; j < pos_cnt; j++)
977 if (pos_list[j] == i) {
978 cur->selected = True;
985 XtFree((char *)pos_list);
988 #ifdef LWLIB_SCROLLBARS_MOTIF
989 else if (class == xmScrollBarWidgetClass) {
990 /* This function is not used by the scrollbar. */
996 /* This function is for activating a button from a program. It's wrong because
997 we pass a NULL argument in the call_data which is not Motif compatible.
998 This is used from the XmNdefaultAction callback of the List widgets to
999 have a double-click put down a dialog box like the button would do.
1000 I could not find a way to do that with accelerators.
1003 activate_button(Widget widget, XtPointer closure, XtPointer call_data)
1005 Widget button = (Widget) closure;
1006 XtCallCallbacks(button, XmNactivateCallback, NULL);
1009 /* creation functions */
1011 #ifdef LWLIB_DIALOGS_MOTIF
1015 #if (XmVersion >= 1002)
1016 # define ARMANDACTIVATE_KLUDGE
1020 #ifdef ARMANDACTIVATE_KLUDGE
1021 /* We want typing Return at a dialog box to select the default button; but
1022 we're satisfied with having it select the leftmost button instead.
1024 In Motif 1.1.5 we could do this by putting this resource in the
1027 *dialog*button1.accelerators:#override\
1028 <KeyPress>Return: ArmAndActivate()\n\
1029 <KeyPress>KP_Enter: ArmAndActivate()\n\
1030 Ctrl<KeyPress>m: ArmAndActivate()\n
1032 but that doesn't work with 1.2.1 and I don't understand why. However,
1033 doing the equivalent C code does work, with the notable disadvantage that
1034 the user can't override it. So that's what we do until we figure out
1035 something better....
1037 static char button_trans[] = "\
1038 <KeyPress>Return: ArmAndActivate()\n\
1039 <KeyPress>KP_Enter: ArmAndActivate()\n\
1040 Ctrl<KeyPress>m: ArmAndActivate()\n";
1042 #endif /* ARMANDACTIVATE_KLUDGE */
1045 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1046 was a segv down in libXm somewhere if you used the middle button on a
1047 dialog box to begin a drag; when you released the button to make a drop
1048 things would lose if you were not over the button where you started the
1049 drag (canceling the operation). This was probably due to the fact that
1050 the dialog boxes were not set up to handle a drag but were trying to do
1051 so anyway for some reason.
1053 So we disable drag-and-drop in dialog boxes by turning off the binding for
1054 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1055 solution as it only works in default configurations, but...
1057 static char disable_dnd_trans[] = "<Btn2Down>: ";
1058 #endif /* DND_KLUDGE */
1061 make_dialog(char *name, Widget parent, Boolean pop_up_p,
1062 const char *shell_title, const char *icon_name,
1063 Boolean text_input_slot, Boolean radio_box, Boolean list,
1064 int left_buttons, int right_buttons)
1070 Widget icon_separator;
1075 Widget children[16]; /* for the final XtManageChildren */
1077 Arg al[64]; /* Arg List */
1078 int ac; /* Arg Count */
1082 XtTranslations dnd_override =
1083 XtParseTranslationTable(disable_dnd_trans);
1084 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1085 #else /* ! DND_KLUDGE */
1086 # define DO_DND_KLUDGE(widget)
1087 #endif /* ! DND_KLUDGE */
1091 XtSetArg(al[ac], XmNtitle, shell_title);
1093 XtSetArg(al[ac], XtNallowShellResize, True);
1095 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP);
1097 result = XmCreateDialogShell(parent, "dialog", al, ac);
1099 XtSetArg(al[ac], XmNautoUnmanage, FALSE);
1101 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; *//* ####is this ok? */
1102 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1104 form = XmCreateForm(result, (char *)shell_title, al, ac);
1107 XtSetArg(al[ac], XmNautoUnmanage, FALSE);
1109 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1111 form = XmCreateForm(parent, (char *)shell_title, al, ac);
1116 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN);
1118 XtSetArg(al[ac], XmNorientation, XmVERTICAL);
1120 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1);
1122 XtSetArg(al[ac], XmNmarginWidth, 0);
1124 XtSetArg(al[ac], XmNmarginHeight, 0);
1126 XtSetArg(al[ac], XmNspacing, 13);
1128 XtSetArg(al[ac], XmNadjustLast, False);
1130 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);
1132 XtSetArg(al[ac], XmNisAligned, True);
1134 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1136 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM);
1138 XtSetArg(al[ac], XmNbottomOffset, 13);
1140 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);
1142 XtSetArg(al[ac], XmNleftOffset, 13);
1144 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1146 XtSetArg(al[ac], XmNrightOffset, 13);
1148 row = XmCreateRowColumn(form, "row", al, ac);
1151 for (i = 0; i < left_buttons; i++) {
1152 char button_name[16];
1153 int sz = snprintf(button_name, sizeof(button_name), "button%d", i + 1);
1154 assert(sz >= 0 && sz < sizeof(button_name));
1157 XtSetArg(al[ac], XmNhighlightThickness, 1);
1159 XtSetArg(al[ac], XmNshowAsDefault, TRUE);
1162 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1164 children[n_children] =
1165 XmCreatePushButton(row, button_name, al, ac);
1166 DO_DND_KLUDGE(children[n_children]);
1169 button = children[n_children];
1171 XtSetArg(al[ac], XmNdefaultButton, button);
1173 XtSetValues(row, al, ac);
1175 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1177 XtTranslations losers =
1178 XtParseTranslationTable(button_trans);
1179 XtOverrideTranslations(button, losers);
1180 XtFree((char *)losers);
1182 #endif /* ARMANDACTIVATE_KLUDGE */
1188 /* invisible separator button */
1190 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1192 children[n_children] = XmCreateLabel(row, "separator_button", al, ac);
1193 DO_DND_KLUDGE(children[n_children]);
1196 for (i = 0; i < right_buttons; i++) {
1197 char button_name[16];
1198 int sz = snprintf(button_name, sizeof(button_name),
1199 "button%d", left_buttons + i + 1);
1200 assert(sz >= 0 && sz < sizeof(button_name));
1202 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1204 children[n_children] =
1205 XmCreatePushButton(row, button_name, al, ac);
1206 DO_DND_KLUDGE(children[n_children]);
1208 button = children[n_children];
1212 XtManageChildren(children, n_children);
1215 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1217 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1219 XtSetArg(al[ac], XmNbottomOffset, 13);
1221 XtSetArg(al[ac], XmNbottomWidget, row);
1223 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);
1225 XtSetArg(al[ac], XmNleftOffset, 0);
1227 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1229 XtSetArg(al[ac], XmNrightOffset, 0);
1231 separator = XmCreateSeparator(form, "", al, ac);
1234 XtSetArg(al[ac], XmNlabelType, XmPIXMAP);
1236 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);
1238 XtSetArg(al[ac], XmNtopOffset, 13);
1240 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);
1242 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);
1244 XtSetArg(al[ac], XmNleftOffset, 13);
1246 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);
1248 icon = XmCreateLabel(form, (char *)icon_name, al, ac);
1249 DO_DND_KLUDGE(icon);
1252 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1254 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);
1256 XtSetArg(al[ac], XmNtopOffset, 6);
1258 XtSetArg(al[ac], XmNtopWidget, icon);
1260 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1262 XtSetArg(al[ac], XmNbottomOffset, 6);
1264 XtSetArg(al[ac], XmNbottomWidget, separator);
1266 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);
1268 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);
1270 icon_separator = XmCreateLabel(form, "", al, ac);
1271 DO_DND_KLUDGE(icon_separator);
1273 if (text_input_slot) {
1275 XtSetArg(al[ac], XmNcolumns, 50);
1277 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1279 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1281 XtSetArg(al[ac], XmNbottomOffset, 13);
1283 XtSetArg(al[ac], XmNbottomWidget, separator);
1285 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1287 XtSetArg(al[ac], XmNleftOffset, 13);
1289 XtSetArg(al[ac], XmNleftWidget, icon);
1291 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1293 XtSetArg(al[ac], XmNrightOffset, 13);
1295 value = XmCreateTextField(form, "value", al, ac);
1296 DO_DND_KLUDGE(value);
1297 } else if (radio_box) {
1300 XtSetArg(al[ac], XmNmarginWidth, 0);
1302 XtSetArg(al[ac], XmNmarginHeight, 0);
1304 XtSetArg(al[ac], XmNspacing, 13);
1306 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);
1308 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);
1310 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1312 XtSetArg(al[ac], XmNbottomOffset, 13);
1314 XtSetArg(al[ac], XmNbottomWidget, separator);
1316 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1318 XtSetArg(al[ac], XmNleftOffset, 13);
1320 XtSetArg(al[ac], XmNleftWidget, icon);
1322 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1324 XtSetArg(al[ac], XmNrightOffset, 13);
1326 value = XmCreateRadioBox(form, "radiobutton1", al, ac);
1330 XmCreateToggleButtonGadget(value, "radio1", al, ac);
1331 children[i++] = radio_butt;
1333 XmCreateToggleButtonGadget(value, "radio2", al, ac);
1334 children[i++] = radio_butt;
1336 XmCreateToggleButtonGadget(value, "radio3", al, ac);
1337 children[i++] = radio_butt;
1338 XtManageChildren(children, i);
1341 XtSetArg(al[ac], XmNvisibleItemCount, 5);
1343 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1345 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1347 XtSetArg(al[ac], XmNbottomOffset, 13);
1349 XtSetArg(al[ac], XmNbottomWidget, separator);
1351 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1353 XtSetArg(al[ac], XmNleftOffset, 13);
1355 XtSetArg(al[ac], XmNleftWidget, icon);
1357 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1359 XtSetArg(al[ac], XmNrightOffset, 13);
1361 value = XmCreateScrolledList(form, "list", al, ac);
1363 /* this is the easiest way I found to have the double click in the
1364 list activate the default button */
1365 XtAddCallback(value, XmNdefaultActionCallback, activate_button,
1368 /* else add nothing; it's a separator */
1371 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1373 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);
1375 XtSetArg(al[ac], XmNtopOffset, 13);
1377 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1379 XtSetArg(al[ac], XmNbottomOffset, 13);
1381 XtSetArg(al[ac], XmNbottomWidget,
1382 text_input_slot || radio_box || list ? value : separator);
1384 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1386 XtSetArg(al[ac], XmNleftOffset, 13);
1388 XtSetArg(al[ac], XmNleftWidget, icon);
1390 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1392 XtSetArg(al[ac], XmNrightOffset, 13);
1394 message = XmCreateLabel(form, "message", al, ac);
1395 DO_DND_KLUDGE(message);
1398 XtManageChild(value);
1403 children[i] = separator;
1405 if (text_input_slot || radio_box) {
1406 children[i] = value;
1409 children[i] = message;
1413 children[i] = icon_separator;
1415 XtManageChildren(children, i);
1417 if (text_input_slot || list) {
1418 XtInstallAccelerators(value, button);
1419 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1420 } else if (radio_box) {
1421 XtInstallAccelerators(form, button);
1422 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1424 /* else we don' need no STEENKIN' assellerators. */
1427 XtFree((char *)dnd_override);
1429 #undef DO_DND_KLUDGE
1434 static destroyed_instance *find_matching_instance(widget_instance * instance)
1436 destroyed_instance *cur;
1437 destroyed_instance *prev;
1438 char *type = instance->info->type;
1439 char *name = instance->info->name;
1441 for (prev = NULL, cur = all_destroyed_instances;
1442 cur; prev = cur, cur = cur->next) {
1443 if (!strcmp(cur->name, name)
1444 && !strcmp(cur->type, type)
1445 && cur->parent == instance->parent
1446 && cur->pop_up_p == instance->pop_up_p) {
1448 prev->next = cur->next;
1450 all_destroyed_instances = cur->next;
1453 /* do some cleanup */
1454 else if (!cur->widget) {
1456 prev->next = cur->next;
1458 all_destroyed_instances = cur->next;
1459 free_destroyed_instance(cur);
1460 cur = prev ? prev : all_destroyed_instances;
1466 static void recenter_widget(Widget widget)
1468 Widget parent = XtParent(widget);
1469 Screen *screen = XtScreen(widget);
1470 Dimension screen_width = WidthOfScreen(screen);
1471 Dimension screen_height = HeightOfScreen(screen);
1472 Dimension parent_width = 0;
1473 Dimension parent_height = 0;
1474 Dimension child_width = 0;
1475 Dimension child_height = 0;
1480 XtSetArg(al[0], XtNwidth, &child_width);
1481 XtSetArg(al[1], XtNheight, &child_height);
1482 XtGetValues(widget, al, 2);
1484 XtSetArg(al[0], XtNwidth, &parent_width);
1485 XtSetArg(al[1], XtNheight, &parent_height);
1486 XtGetValues(parent, al, 2);
1488 x = (Position) ((parent_width - child_width) / 2);
1489 y = (Position) ((parent_height - child_height) / 2);
1491 XtTranslateCoords(parent, x, y, &x, &y);
1493 if ((Dimension) (x + child_width) > screen_width)
1494 x = screen_width - child_width;
1498 if ((Dimension) (y + child_height) > screen_height)
1499 y = screen_height - child_height;
1503 XtSetArg(al[0], XtNx, x);
1504 XtSetArg(al[1], XtNy, y);
1505 XtSetValues(widget, al, 2);
1508 static Widget recycle_instance(destroyed_instance * instance)
1510 Widget widget = instance->widget;
1512 /* widget is NULL if the parent was destroyed. */
1517 /* Remove the destroy callback as the instance is not in the list
1519 XtRemoveCallback(instance->parent, XtNdestroyCallback,
1520 mark_dead_instance_destroyed,
1521 (XtPointer) instance);
1523 /* Give the focus to the initial item */
1524 focus = XtNameToWidget(widget, "*value");
1526 focus = XtNameToWidget(widget, "*button1");
1528 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1530 /* shrink the separator label back to their original size */
1531 separator = XtNameToWidget(widget, "*separator_button");
1534 XtSetArg(al[0], XtNwidth, 5);
1535 XtSetArg(al[1], XtNheight, 5);
1536 XtSetValues(separator, al, 2);
1539 /* Center the dialog in its parent */
1540 recenter_widget(widget);
1542 free_destroyed_instance(instance);
1546 Widget xm_create_dialog(widget_instance * instance)
1548 char *name = instance->info->type;
1549 Widget parent = instance->parent;
1551 Boolean pop_up_p = instance->pop_up_p;
1552 const char *shell_name = 0;
1553 const char *icon_name = 0;
1554 Boolean text_input_slot = False;
1555 Boolean radio_box = False;
1556 Boolean list = False;
1558 int left_buttons = 0;
1559 int right_buttons = 1;
1560 destroyed_instance *dead_one;
1562 /* try to find a widget to recycle */
1563 dead_one = find_matching_instance(instance);
1565 Widget recycled_widget = recycle_instance(dead_one);
1566 if (recycled_widget)
1567 return recycled_widget;
1573 icon_name = "dbox-error";
1574 shell_name = "Error";
1579 icon_name = "dbox-info";
1580 shell_name = "Information";
1586 icon_name = "dbox-question";
1587 shell_name = "Prompt";
1592 text_input_slot = True;
1593 icon_name = "dbox-question";
1594 shell_name = "Prompt";
1599 icon_name = "dbox-question";
1600 shell_name = "Question";
1604 total_buttons = name[1] - '0';
1606 if (name[3] == 'T' || name[3] == 't') {
1607 text_input_slot = False;
1610 right_buttons = name[4] - '0';
1612 left_buttons = total_buttons - right_buttons;
1614 widget = make_dialog(name, parent, pop_up_p,
1615 shell_name, icon_name, text_input_slot, radio_box,
1616 list, left_buttons, right_buttons);
1618 XtAddCallback(widget, XmNpopdownCallback, xm_nosel_callback,
1619 (XtPointer) instance);
1623 #endif /* LWLIB_DIALOGS_MOTIF */
1625 #ifdef LWLIB_MENUBARS_MOTIF
1626 static Widget make_menubar(widget_instance * instance)
1631 XtSetArg(al[ac], XmNmarginHeight, 0);
1633 XtSetArg(al[ac], XmNshadowThickness, 3);
1636 return XmCreateMenuBar(instance->parent, instance->info->name, al, ac);
1639 static void remove_grabs(Widget shell, XtPointer closure, XtPointer call_data)
1641 Widget menu = (Widget) closure;
1642 XmRemoveFromPostFromList(menu, XtParent(XtParent((Widget) menu)));
1645 static Widget make_popup_menu(widget_instance * instance)
1647 Widget parent = instance->parent;
1648 Window parent_window = parent->core.window;
1651 /* sets the parent window to 0 to fool Motif into not generating a grab */
1652 parent->core.window = 0;
1653 result = XmCreatePopupMenu(parent, instance->info->name, NULL, 0);
1654 XtAddCallback(XtParent(result), XmNpopdownCallback, remove_grabs,
1655 (XtPointer) result);
1656 parent->core.window = parent_window;
1659 #endif /* LWLIB_MENUBARS_MOTIF */
1661 #ifdef LWLIB_SCROLLBARS_MOTIF
1662 static Widget make_scrollbar(widget_instance * instance, int vertical)
1666 static XtCallbackRec callbacks[2] =
1667 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1669 callbacks[0].closure = (XtPointer) instance;
1671 XtSetArg(al[ac], XmNminimum, 1);
1673 XtSetArg(al[ac], XmNmaximum, INT_MAX);
1675 XtSetArg(al[ac], XmNincrement, 1);
1677 XtSetArg(al[ac], XmNpageIncrement, 1);
1679 XtSetArg(al[ac], XmNborderWidth, 0);
1681 XtSetArg(al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL);
1684 XtSetArg(al[ac], XmNdecrementCallback, callbacks);
1686 XtSetArg(al[ac], XmNdragCallback, callbacks);
1688 XtSetArg(al[ac], XmNincrementCallback, callbacks);
1690 XtSetArg(al[ac], XmNpageDecrementCallback, callbacks);
1692 XtSetArg(al[ac], XmNpageIncrementCallback, callbacks);
1694 XtSetArg(al[ac], XmNtoBottomCallback, callbacks);
1696 XtSetArg(al[ac], XmNtoTopCallback, callbacks);
1698 XtSetArg(al[ac], XmNvalueChangedCallback, callbacks);
1701 return XmCreateScrollBar(instance->parent, instance->info->name, al,
1705 static Widget make_vertical_scrollbar(widget_instance * instance)
1707 return make_scrollbar(instance, 1);
1710 static Widget make_horizontal_scrollbar(widget_instance * instance)
1712 return make_scrollbar(instance, 0);
1715 #endif /* LWLIB_SCROLLBARS_MOTIF */
1717 #ifdef LWLIB_WIDGETS_MOTIF
1719 static Widget xm_create_button(widget_instance * instance)
1724 widget_value *val = instance->info->val;
1726 XtSetArg(al[ac], XmNsensitive, val->enabled);
1728 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1730 XtSetArg(al[ac], XmNuserData, val->call_data);
1732 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1734 /* The highlight doesn't appear to be dynamically set which makes it
1735 look ugly. I think this may be a LessTif bug but for now we just
1737 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1740 /* add any args the user supplied for creation time */
1741 lw_add_value_args_to_args(val, al, &ac);
1743 if (!val->call_data)
1744 button = XmCreateLabel(instance->parent, val->name, al, ac);
1746 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE) {
1747 XtSetArg(al[ac], XmNset, val->selected);
1749 XtSetArg(al[ac], XmNindicatorType,
1750 (val->type == TOGGLE_TYPE ?
1751 XmN_OF_MANY : XmONE_OF_MANY));
1753 XtSetArg(al[ac], XmNvisibleWhenOff, True);
1756 XmCreateToggleButton(instance->parent, val->name, al, ac);
1757 XtRemoveAllCallbacks(button, XmNvalueChangedCallback);
1758 XtAddCallback(button, XmNvalueChangedCallback,
1759 xm_generic_callback, (XtPointer) instance);
1762 XmCreatePushButton(instance->parent, val->name, al, ac);
1763 XtAddCallback(button, XmNactivateCallback, xm_generic_callback,
1764 (XtPointer) instance);
1767 XtManageChild(button);
1772 static Widget xm_create_progress(widget_instance * instance)
1776 Dimension height = 0;
1777 Dimension width = 0;
1779 widget_value *val = instance->info->val;
1780 if (!val->call_data) {
1781 XtSetArg(al[ac], XmNeditable, False);
1784 XtSetArg(al[ac], XmNeditable, val->enabled);
1787 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1789 XtSetArg(al[ac], XmNuserData, val->call_data);
1791 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1793 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);
1795 /* The highlight doesn't appear to be dynamically set which makes it
1796 look ugly. I think this may be a LessTif bug but for now we just
1798 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1801 height = (Dimension) lw_get_value_arg(val, XtNheight);
1802 width = (Dimension) lw_get_value_arg(val, XtNwidth);
1804 XtSetArg(al[ac], XmNscaleHeight, height);
1808 XtSetArg(al[ac], XmNscaleWidth, width);
1812 /* add any args the user supplied for creation time */
1813 lw_add_value_args_to_args(val, al, &ac);
1815 scale = XmCreateScale(instance->parent, val->name, al, ac);
1817 XtAddCallback(scale, XmNvalueChangedCallback,
1818 xm_generic_callback, (XtPointer) instance);
1820 XtManageChild(scale);
1825 static Widget xm_create_text_field(widget_instance * instance)
1830 widget_value *val = instance->info->val;
1832 XtSetArg(al[ac], XmNsensitive, val->enabled);
1834 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1836 XtSetArg(al[ac], XmNuserData, val->call_data);
1838 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1840 /* The highlight doesn't appear to be dynamically set which makes it
1841 look ugly. I think this may be a LessTif bug but for now we just
1843 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1846 /* add any args the user supplied for creation time */
1847 lw_add_value_args_to_args(val, al, &ac);
1849 text = XmCreateTextField(instance->parent, val->name, al, ac);
1851 XtAddCallback(text, XmNvalueChangedCallback,
1852 xm_generic_callback, (XtPointer) instance);
1854 XtManageChild(text);
1859 static Widget xm_create_label_field(widget_instance * instance)
1861 return xm_create_label(instance->parent, instance->info->val);
1864 Widget xm_create_label(Widget parent, widget_value * val)
1870 XtSetArg(al[ac], XmNsensitive, val->enabled);
1872 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1874 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1876 /* The highlight doesn't appear to be dynamically set which makes it
1877 look ugly. I think this may be a LessTif bug but for now we just
1879 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1882 /* add any args the user supplied for creation time */
1883 lw_add_value_args_to_args(val, al, &ac);
1885 label = XmCreateLabel(parent, val->name, al, ac);
1887 XtManageChild(label);
1889 /* Do it again for arguments that have no effect until the widget is realized. */
1891 lw_add_value_args_to_args(val, al, &ac);
1892 XtSetValues(label, al, ac);
1898 static Widget xm_create_combo_box(widget_instance * instance)
1903 widget_value *val = instance->info->val;
1905 XtSetArg(al[ac], XmNsensitive, val->enabled);
1907 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1909 XtSetArg(al[ac], XmNuserData, val->call_data);
1911 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1913 /* The highlight doesn't appear to be dynamically set which makes it
1914 look ugly. I think this may be a LessTif bug but for now we just
1916 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1919 /* add any args the user supplied for creation time */
1920 lw_add_value_args_to_args(val, al, &ac);
1922 combo = XmCreateDropDownComboBox(instance->parent, val->name, al, ac);
1924 XtAddCallback(combo, XmNselectionCallback, xm_generic_callback,
1925 (XtPointer) instance);
1927 XtManageChild(combo);
1932 #endif /* LWLIB_WIDGETS_MOTIF */
1934 /* Table of functions to create widgets */
1936 const widget_creation_entry xm_creation_table[] = {
1937 #ifdef LWLIB_MENUBARS_MOTIF
1938 {"menubar", make_menubar},
1939 {"popup", make_popup_menu},
1941 #ifdef LWLIB_SCROLLBARS_MOTIF
1942 {"vertical-scrollbar", make_vertical_scrollbar},
1943 {"horizontal-scrollbar", make_horizontal_scrollbar},
1945 #ifdef LWLIB_WIDGETS_MOTIF
1946 {"button", xm_create_button},
1947 {"progress", xm_create_progress},
1948 {"text-field", xm_create_text_field},
1949 {"label", xm_create_label_field},
1951 {"combo-box", xm_create_combo_box},
1957 /* Destruction of instances */
1958 void xm_destroy_instance(widget_instance * instance)
1960 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1961 /* It appears that this is used only for dialog boxes. */
1962 Widget widget = instance->widget;
1963 /* recycle the dialog boxes */
1964 /* Disable the recycling until we can find a way to have the dialog box
1965 get reasonable layout after we modify its contents. */
1966 if (0 && XtClass(widget) == xmDialogShellWidgetClass) {
1967 destroyed_instance *dead_instance =
1968 make_destroyed_instance(instance->info->name,
1969 instance->info->type,
1972 instance->pop_up_p);
1973 dead_instance->next = all_destroyed_instances;
1974 all_destroyed_instances = dead_instance;
1975 XtUnmanageChild(first_child(instance->widget));
1976 XFlush(XtDisplay(instance->widget));
1977 XtAddCallback(instance->parent, XtNdestroyCallback,
1978 mark_dead_instance_destroyed,
1979 (XtPointer) dead_instance);
1981 /* This might not be necessary now that the nosel is attached to
1982 popdown instead of destroy, but it can't hurt. */
1983 XtRemoveCallback(instance->widget, XtNdestroyCallback,
1984 xm_nosel_callback, (XtPointer) instance);
1986 XtDestroyWidget(instance->widget);
1988 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1992 #ifdef LWLIB_MENUBARS_MOTIF
1994 void xm_popup_menu(Widget widget, XEvent * event)
1996 if (event->type == ButtonPress || event->type == ButtonRelease) {
1997 /* This is so totally ridiculous: there's NO WAY to tell Motif
1998 that *any* button can select a menu item. Only one button
1999 can have that honor.
2002 if (event->xbutton.state & Button5Mask)
2003 trans = "<Btn5Down>";
2004 else if (event->xbutton.state & Button4Mask)
2005 trans = "<Btn4Down>";
2006 else if (event->xbutton.state & Button3Mask)
2007 trans = "<Btn3Down>";
2008 else if (event->xbutton.state & Button2Mask)
2009 trans = "<Btn2Down>";
2010 else if (event->xbutton.state & Button1Mask)
2011 trans = "<Btn1Down>";
2014 XtSetArg(al[0], XmNmenuPost, trans);
2015 XtSetValues(widget, al, 1);
2017 XmMenuPosition(widget, (XButtonPressedEvent *) event);
2019 XtManageChild(widget);
2024 #ifdef LWLIB_DIALOGS_MOTIF
2026 static void set_min_dialog_size(Widget w)
2032 XtSetArg(al[0], XmNwidth, &width);
2033 XtSetArg(al[1], XmNheight, &height);
2034 XtGetValues(w, al, 2);
2036 XtSetArg(al[0], XmNminWidth, width);
2037 XtSetArg(al[1], XmNminHeight, height);
2038 XtSetValues(w, al, 2);
2043 void xm_pop_instance(widget_instance * instance, Boolean up)
2045 Widget widget = instance->widget;
2047 #ifdef LWLIB_DIALOGS_MOTIF
2048 if (XtClass(widget) == xmDialogShellWidgetClass) {
2049 Widget widget_to_manage = first_child(widget);
2051 XtManageChild(widget_to_manage);
2052 set_min_dialog_size(widget);
2053 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
2055 XtUnmanageChild(widget_to_manage);
2060 XtManageChild(widget);
2062 XtUnmanageChild(widget);
2066 /* motif callback */
2068 enum do_call_type { pre_activate, selection, no_selection, post_activate };
2070 static void do_call(Widget widget, XtPointer closure, enum do_call_type type)
2072 XtPointer user_data;
2073 widget_instance *instance = (widget_instance *) closure;
2074 Widget instance_widget;
2080 if (widget->core.being_destroyed)
2083 instance_widget = instance->widget;
2084 if (!instance_widget)
2087 id = instance->info->id;
2089 XtSetArg(al[0], XmNuserData, &user_data);
2090 XtGetValues(widget, al, 1);
2093 if (instance->info->pre_activate_cb)
2094 instance->info->pre_activate_cb(widget, id, user_data);
2097 if (instance->info->selection_cb)
2098 instance->info->selection_cb(widget, id, user_data);
2101 if (instance->info->selection_cb)
2102 instance->info->selection_cb(widget, id,
2106 if (instance->info->post_activate_cb)
2107 instance->info->post_activate_cb(widget, id, user_data);
2114 /* Like lw_internal_update_other_instances except that it does not do
2115 anything if its shell parent is not managed. This is to protect
2116 lw_internal_update_other_instances to dereference freed memory
2117 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2120 xm_internal_update_other_instances(Widget widget, XtPointer closure,
2121 XtPointer call_data)
2124 for (parent = widget; parent; parent = XtParent(parent))
2125 if (XtIsShell(parent))
2127 else if (!XtIsManaged(parent))
2129 lw_internal_update_other_instances(widget, closure, call_data);
2133 xm_generic_callback(Widget widget, XtPointer closure, XtPointer call_data)
2135 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2136 /* We want the selected status to change only when we decide it
2137 should change. Yuck but correct. */
2138 if (XtClass(widget) == xmToggleButtonWidgetClass
2139 || XtClass(widget) == xmToggleButtonGadgetClass) {
2143 XtSetArg(al[0], XmNset, &check);
2144 XtGetValues(widget, al, 1);
2146 XtSetArg(al[0], XmNset, !check);
2147 XtSetValues(widget, al, 1);
2150 lw_internal_update_other_instances(widget, closure, call_data);
2151 do_call(widget, closure, selection);
2155 xm_pop_down_callback(Widget widget, XtPointer closure, XtPointer call_data)
2157 do_call(widget, closure, post_activate);
2160 #ifdef LWLIB_MENUBARS_MOTIF
2163 xm_pull_down_callback(Widget widget, XtPointer closure, XtPointer call_data)
2167 /* new behavior for incremental menu construction */
2171 do_call(widget, closure, pre_activate);
2174 #endif /* LWLIB_MENUBARS_MOTIF */
2176 #ifdef LWLIB_SCROLLBARS_MOTIF
2178 xm_scrollbar_callback(Widget widget, XtPointer closure, XtPointer call_data)
2180 widget_instance *instance = (widget_instance *) closure;
2182 XmScrollBarCallbackStruct *data =
2183 (XmScrollBarCallbackStruct *) call_data;
2184 scroll_event event_data;
2185 scrollbar_values *val =
2186 (scrollbar_values *) instance->info->val->scrollbar_data;
2189 if (!instance || widget->core.being_destroyed)
2192 id = instance->info->id;
2194 percent = (double)(data->value - 1) / (double)(INT_MAX - 1);
2195 event_data.slider_value =
2196 (int)(percent * (double)(val->maximum - val->minimum)) +
2199 if (event_data.slider_value > (val->maximum - val->slider_size))
2200 event_data.slider_value = val->maximum - val->slider_size;
2201 else if (event_data.slider_value < 1)
2202 event_data.slider_value = 1;
2205 switch (data->event->xany.type) {
2208 event_data.time = data->event->xkey.time;
2212 event_data.time = data->event->xbutton.time;
2215 event_data.time = data->event->xmotion.time;
2219 event_data.time = data->event->xcrossing.time;
2222 event_data.time = 0;
2226 event_data.time = 0;
2228 switch (data->reason) {
2229 case XmCR_DECREMENT:
2230 event_data.action = SCROLLBAR_LINE_UP;
2232 case XmCR_INCREMENT:
2233 event_data.action = SCROLLBAR_LINE_DOWN;
2235 case XmCR_PAGE_DECREMENT:
2236 event_data.action = SCROLLBAR_PAGE_UP;
2238 case XmCR_PAGE_INCREMENT:
2239 event_data.action = SCROLLBAR_PAGE_DOWN;
2242 event_data.action = SCROLLBAR_TOP;
2244 case XmCR_TO_BOTTOM:
2245 event_data.action = SCROLLBAR_BOTTOM;
2248 event_data.action = SCROLLBAR_DRAG;
2250 case XmCR_VALUE_CHANGED:
2251 event_data.action = SCROLLBAR_CHANGE;
2254 event_data.action = SCROLLBAR_CHANGE;
2258 if (instance->info->pre_activate_cb)
2259 instance->info->pre_activate_cb(widget, id,
2260 (XtPointer) & event_data);
2262 #endif /* LWLIB_SCROLLBARS_MOTIF */
2264 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2266 mark_dead_instance_destroyed(Widget widget, XtPointer closure,
2267 XtPointer call_data)
2269 destroyed_instance *instance = (destroyed_instance *) closure;
2270 instance->widget = NULL;
2274 xm_nosel_callback(Widget widget, XtPointer closure, XtPointer call_data)
2276 /* This callback is only called when a dialog box is dismissed with the wm's
2277 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2278 in that case, not just unmapped, so that it releases its keyboard grabs.
2279 But there are problems with running our callbacks while the widget is in
2280 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2281 instead of XmDESTROY and then destroy it ourself after having run the
2284 do_call(widget, closure, no_selection);
2285 XtDestroyWidget(widget);
2289 /* set the keyboard focus */
2290 void xm_set_keyboard_focus(Widget parent, Widget w)
2292 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
2293 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2294 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2295 Presumably the problem was elsewhere, and is now gone...