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 sprintf(button_name, "button%d", i + 1);
1156 XtSetArg(al[ac], XmNhighlightThickness, 1);
1158 XtSetArg(al[ac], XmNshowAsDefault, TRUE);
1161 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1163 children[n_children] =
1164 XmCreatePushButton(row, button_name, al, ac);
1165 DO_DND_KLUDGE(children[n_children]);
1168 button = children[n_children];
1170 XtSetArg(al[ac], XmNdefaultButton, button);
1172 XtSetValues(row, al, ac);
1174 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1176 XtTranslations losers =
1177 XtParseTranslationTable(button_trans);
1178 XtOverrideTranslations(button, losers);
1179 XtFree((char *)losers);
1181 #endif /* ARMANDACTIVATE_KLUDGE */
1187 /* invisible separator button */
1189 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1191 children[n_children] = XmCreateLabel(row, "separator_button", al, ac);
1192 DO_DND_KLUDGE(children[n_children]);
1195 for (i = 0; i < right_buttons; i++) {
1196 char button_name[16];
1197 sprintf(button_name, "button%d", left_buttons + i + 1);
1199 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP);
1201 children[n_children] =
1202 XmCreatePushButton(row, button_name, al, ac);
1203 DO_DND_KLUDGE(children[n_children]);
1205 button = children[n_children];
1209 XtManageChildren(children, n_children);
1212 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1214 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1216 XtSetArg(al[ac], XmNbottomOffset, 13);
1218 XtSetArg(al[ac], XmNbottomWidget, row);
1220 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);
1222 XtSetArg(al[ac], XmNleftOffset, 0);
1224 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1226 XtSetArg(al[ac], XmNrightOffset, 0);
1228 separator = XmCreateSeparator(form, "", al, ac);
1231 XtSetArg(al[ac], XmNlabelType, XmPIXMAP);
1233 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);
1235 XtSetArg(al[ac], XmNtopOffset, 13);
1237 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);
1239 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);
1241 XtSetArg(al[ac], XmNleftOffset, 13);
1243 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);
1245 icon = XmCreateLabel(form, (char *)icon_name, al, ac);
1246 DO_DND_KLUDGE(icon);
1249 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1251 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);
1253 XtSetArg(al[ac], XmNtopOffset, 6);
1255 XtSetArg(al[ac], XmNtopWidget, icon);
1257 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1259 XtSetArg(al[ac], XmNbottomOffset, 6);
1261 XtSetArg(al[ac], XmNbottomWidget, separator);
1263 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);
1265 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);
1267 icon_separator = XmCreateLabel(form, "", al, ac);
1268 DO_DND_KLUDGE(icon_separator);
1270 if (text_input_slot) {
1272 XtSetArg(al[ac], XmNcolumns, 50);
1274 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1276 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1278 XtSetArg(al[ac], XmNbottomOffset, 13);
1280 XtSetArg(al[ac], XmNbottomWidget, separator);
1282 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1284 XtSetArg(al[ac], XmNleftOffset, 13);
1286 XtSetArg(al[ac], XmNleftWidget, icon);
1288 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1290 XtSetArg(al[ac], XmNrightOffset, 13);
1292 value = XmCreateTextField(form, "value", al, ac);
1293 DO_DND_KLUDGE(value);
1294 } else if (radio_box) {
1297 XtSetArg(al[ac], XmNmarginWidth, 0);
1299 XtSetArg(al[ac], XmNmarginHeight, 0);
1301 XtSetArg(al[ac], XmNspacing, 13);
1303 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);
1305 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);
1307 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1309 XtSetArg(al[ac], XmNbottomOffset, 13);
1311 XtSetArg(al[ac], XmNbottomWidget, separator);
1313 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1315 XtSetArg(al[ac], XmNleftOffset, 13);
1317 XtSetArg(al[ac], XmNleftWidget, icon);
1319 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1321 XtSetArg(al[ac], XmNrightOffset, 13);
1323 value = XmCreateRadioBox(form, "radiobutton1", al, ac);
1327 XmCreateToggleButtonGadget(value, "radio1", al, ac);
1328 children[i++] = radio_butt;
1330 XmCreateToggleButtonGadget(value, "radio2", al, ac);
1331 children[i++] = radio_butt;
1333 XmCreateToggleButtonGadget(value, "radio3", al, ac);
1334 children[i++] = radio_butt;
1335 XtManageChildren(children, i);
1338 XtSetArg(al[ac], XmNvisibleItemCount, 5);
1340 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);
1342 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1344 XtSetArg(al[ac], XmNbottomOffset, 13);
1346 XtSetArg(al[ac], XmNbottomWidget, separator);
1348 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1350 XtSetArg(al[ac], XmNleftOffset, 13);
1352 XtSetArg(al[ac], XmNleftWidget, icon);
1354 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1356 XtSetArg(al[ac], XmNrightOffset, 13);
1358 value = XmCreateScrolledList(form, "list", al, ac);
1360 /* this is the easiest way I found to have the double click in the
1361 list activate the default button */
1362 XtAddCallback(value, XmNdefaultActionCallback, activate_button,
1365 /* else add nothing; it's a separator */
1368 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1370 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);
1372 XtSetArg(al[ac], XmNtopOffset, 13);
1374 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);
1376 XtSetArg(al[ac], XmNbottomOffset, 13);
1378 XtSetArg(al[ac], XmNbottomWidget,
1379 text_input_slot || radio_box || list ? value : separator);
1381 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);
1383 XtSetArg(al[ac], XmNleftOffset, 13);
1385 XtSetArg(al[ac], XmNleftWidget, icon);
1387 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);
1389 XtSetArg(al[ac], XmNrightOffset, 13);
1391 message = XmCreateLabel(form, "message", al, ac);
1392 DO_DND_KLUDGE(message);
1395 XtManageChild(value);
1400 children[i] = separator;
1402 if (text_input_slot || radio_box) {
1403 children[i] = value;
1406 children[i] = message;
1410 children[i] = icon_separator;
1412 XtManageChildren(children, i);
1414 if (text_input_slot || list) {
1415 XtInstallAccelerators(value, button);
1416 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1417 } else if (radio_box) {
1418 XtInstallAccelerators(form, button);
1419 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1421 /* else we don' need no STEENKIN' assellerators. */
1424 XtFree((char *)dnd_override);
1426 #undef DO_DND_KLUDGE
1431 static destroyed_instance *find_matching_instance(widget_instance * instance)
1433 destroyed_instance *cur;
1434 destroyed_instance *prev;
1435 char *type = instance->info->type;
1436 char *name = instance->info->name;
1438 for (prev = NULL, cur = all_destroyed_instances;
1439 cur; prev = cur, cur = cur->next) {
1440 if (!strcmp(cur->name, name)
1441 && !strcmp(cur->type, type)
1442 && cur->parent == instance->parent
1443 && cur->pop_up_p == instance->pop_up_p) {
1445 prev->next = cur->next;
1447 all_destroyed_instances = cur->next;
1450 /* do some cleanup */
1451 else if (!cur->widget) {
1453 prev->next = cur->next;
1455 all_destroyed_instances = cur->next;
1456 free_destroyed_instance(cur);
1457 cur = prev ? prev : all_destroyed_instances;
1463 static void recenter_widget(Widget widget)
1465 Widget parent = XtParent(widget);
1466 Screen *screen = XtScreen(widget);
1467 Dimension screen_width = WidthOfScreen(screen);
1468 Dimension screen_height = HeightOfScreen(screen);
1469 Dimension parent_width = 0;
1470 Dimension parent_height = 0;
1471 Dimension child_width = 0;
1472 Dimension child_height = 0;
1477 XtSetArg(al[0], XtNwidth, &child_width);
1478 XtSetArg(al[1], XtNheight, &child_height);
1479 XtGetValues(widget, al, 2);
1481 XtSetArg(al[0], XtNwidth, &parent_width);
1482 XtSetArg(al[1], XtNheight, &parent_height);
1483 XtGetValues(parent, al, 2);
1485 x = (Position) ((parent_width - child_width) / 2);
1486 y = (Position) ((parent_height - child_height) / 2);
1488 XtTranslateCoords(parent, x, y, &x, &y);
1490 if ((Dimension) (x + child_width) > screen_width)
1491 x = screen_width - child_width;
1495 if ((Dimension) (y + child_height) > screen_height)
1496 y = screen_height - child_height;
1500 XtSetArg(al[0], XtNx, x);
1501 XtSetArg(al[1], XtNy, y);
1502 XtSetValues(widget, al, 2);
1505 static Widget recycle_instance(destroyed_instance * instance)
1507 Widget widget = instance->widget;
1509 /* widget is NULL if the parent was destroyed. */
1514 /* Remove the destroy callback as the instance is not in the list
1516 XtRemoveCallback(instance->parent, XtNdestroyCallback,
1517 mark_dead_instance_destroyed,
1518 (XtPointer) instance);
1520 /* Give the focus to the initial item */
1521 focus = XtNameToWidget(widget, "*value");
1523 focus = XtNameToWidget(widget, "*button1");
1525 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1527 /* shrink the separator label back to their original size */
1528 separator = XtNameToWidget(widget, "*separator_button");
1531 XtSetArg(al[0], XtNwidth, 5);
1532 XtSetArg(al[1], XtNheight, 5);
1533 XtSetValues(separator, al, 2);
1536 /* Center the dialog in its parent */
1537 recenter_widget(widget);
1539 free_destroyed_instance(instance);
1543 Widget xm_create_dialog(widget_instance * instance)
1545 char *name = instance->info->type;
1546 Widget parent = instance->parent;
1548 Boolean pop_up_p = instance->pop_up_p;
1549 const char *shell_name = 0;
1550 const char *icon_name = 0;
1551 Boolean text_input_slot = False;
1552 Boolean radio_box = False;
1553 Boolean list = False;
1555 int left_buttons = 0;
1556 int right_buttons = 1;
1557 destroyed_instance *dead_one;
1559 /* try to find a widget to recycle */
1560 dead_one = find_matching_instance(instance);
1562 Widget recycled_widget = recycle_instance(dead_one);
1563 if (recycled_widget)
1564 return recycled_widget;
1570 icon_name = "dbox-error";
1571 shell_name = "Error";
1576 icon_name = "dbox-info";
1577 shell_name = "Information";
1583 icon_name = "dbox-question";
1584 shell_name = "Prompt";
1589 text_input_slot = True;
1590 icon_name = "dbox-question";
1591 shell_name = "Prompt";
1596 icon_name = "dbox-question";
1597 shell_name = "Question";
1601 total_buttons = name[1] - '0';
1603 if (name[3] == 'T' || name[3] == 't') {
1604 text_input_slot = False;
1607 right_buttons = name[4] - '0';
1609 left_buttons = total_buttons - right_buttons;
1611 widget = make_dialog(name, parent, pop_up_p,
1612 shell_name, icon_name, text_input_slot, radio_box,
1613 list, left_buttons, right_buttons);
1615 XtAddCallback(widget, XmNpopdownCallback, xm_nosel_callback,
1616 (XtPointer) instance);
1620 #endif /* LWLIB_DIALOGS_MOTIF */
1622 #ifdef LWLIB_MENUBARS_MOTIF
1623 static Widget make_menubar(widget_instance * instance)
1628 XtSetArg(al[ac], XmNmarginHeight, 0);
1630 XtSetArg(al[ac], XmNshadowThickness, 3);
1633 return XmCreateMenuBar(instance->parent, instance->info->name, al, ac);
1636 static void remove_grabs(Widget shell, XtPointer closure, XtPointer call_data)
1638 Widget menu = (Widget) closure;
1639 XmRemoveFromPostFromList(menu, XtParent(XtParent((Widget) menu)));
1642 static Widget make_popup_menu(widget_instance * instance)
1644 Widget parent = instance->parent;
1645 Window parent_window = parent->core.window;
1648 /* sets the parent window to 0 to fool Motif into not generating a grab */
1649 parent->core.window = 0;
1650 result = XmCreatePopupMenu(parent, instance->info->name, NULL, 0);
1651 XtAddCallback(XtParent(result), XmNpopdownCallback, remove_grabs,
1652 (XtPointer) result);
1653 parent->core.window = parent_window;
1656 #endif /* LWLIB_MENUBARS_MOTIF */
1658 #ifdef LWLIB_SCROLLBARS_MOTIF
1659 static Widget make_scrollbar(widget_instance * instance, int vertical)
1663 static XtCallbackRec callbacks[2] =
1664 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1666 callbacks[0].closure = (XtPointer) instance;
1668 XtSetArg(al[ac], XmNminimum, 1);
1670 XtSetArg(al[ac], XmNmaximum, INT_MAX);
1672 XtSetArg(al[ac], XmNincrement, 1);
1674 XtSetArg(al[ac], XmNpageIncrement, 1);
1676 XtSetArg(al[ac], XmNborderWidth, 0);
1678 XtSetArg(al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL);
1681 XtSetArg(al[ac], XmNdecrementCallback, callbacks);
1683 XtSetArg(al[ac], XmNdragCallback, callbacks);
1685 XtSetArg(al[ac], XmNincrementCallback, callbacks);
1687 XtSetArg(al[ac], XmNpageDecrementCallback, callbacks);
1689 XtSetArg(al[ac], XmNpageIncrementCallback, callbacks);
1691 XtSetArg(al[ac], XmNtoBottomCallback, callbacks);
1693 XtSetArg(al[ac], XmNtoTopCallback, callbacks);
1695 XtSetArg(al[ac], XmNvalueChangedCallback, callbacks);
1698 return XmCreateScrollBar(instance->parent, instance->info->name, al,
1702 static Widget make_vertical_scrollbar(widget_instance * instance)
1704 return make_scrollbar(instance, 1);
1707 static Widget make_horizontal_scrollbar(widget_instance * instance)
1709 return make_scrollbar(instance, 0);
1712 #endif /* LWLIB_SCROLLBARS_MOTIF */
1714 #ifdef LWLIB_WIDGETS_MOTIF
1716 static Widget xm_create_button(widget_instance * instance)
1721 widget_value *val = instance->info->val;
1723 XtSetArg(al[ac], XmNsensitive, val->enabled);
1725 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1727 XtSetArg(al[ac], XmNuserData, val->call_data);
1729 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1731 /* The highlight doesn't appear to be dynamically set which makes it
1732 look ugly. I think this may be a LessTif bug but for now we just
1734 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1737 /* add any args the user supplied for creation time */
1738 lw_add_value_args_to_args(val, al, &ac);
1740 if (!val->call_data)
1741 button = XmCreateLabel(instance->parent, val->name, al, ac);
1743 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE) {
1744 XtSetArg(al[ac], XmNset, val->selected);
1746 XtSetArg(al[ac], XmNindicatorType,
1747 (val->type == TOGGLE_TYPE ?
1748 XmN_OF_MANY : XmONE_OF_MANY));
1750 XtSetArg(al[ac], XmNvisibleWhenOff, True);
1753 XmCreateToggleButton(instance->parent, val->name, al, ac);
1754 XtRemoveAllCallbacks(button, XmNvalueChangedCallback);
1755 XtAddCallback(button, XmNvalueChangedCallback,
1756 xm_generic_callback, (XtPointer) instance);
1759 XmCreatePushButton(instance->parent, val->name, al, ac);
1760 XtAddCallback(button, XmNactivateCallback, xm_generic_callback,
1761 (XtPointer) instance);
1764 XtManageChild(button);
1769 static Widget xm_create_progress(widget_instance * instance)
1773 Dimension height = 0;
1774 Dimension width = 0;
1776 widget_value *val = instance->info->val;
1777 if (!val->call_data) {
1778 XtSetArg(al[ac], XmNeditable, False);
1781 XtSetArg(al[ac], XmNeditable, val->enabled);
1784 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1786 XtSetArg(al[ac], XmNuserData, val->call_data);
1788 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1790 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);
1792 /* The highlight doesn't appear to be dynamically set which makes it
1793 look ugly. I think this may be a LessTif bug but for now we just
1795 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1798 height = (Dimension) lw_get_value_arg(val, XtNheight);
1799 width = (Dimension) lw_get_value_arg(val, XtNwidth);
1801 XtSetArg(al[ac], XmNscaleHeight, height);
1805 XtSetArg(al[ac], XmNscaleWidth, width);
1809 /* add any args the user supplied for creation time */
1810 lw_add_value_args_to_args(val, al, &ac);
1812 scale = XmCreateScale(instance->parent, val->name, al, ac);
1814 XtAddCallback(scale, XmNvalueChangedCallback,
1815 xm_generic_callback, (XtPointer) instance);
1817 XtManageChild(scale);
1822 static Widget xm_create_text_field(widget_instance * instance)
1827 widget_value *val = instance->info->val;
1829 XtSetArg(al[ac], XmNsensitive, val->enabled);
1831 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1833 XtSetArg(al[ac], XmNuserData, val->call_data);
1835 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1837 /* The highlight doesn't appear to be dynamically set which makes it
1838 look ugly. I think this may be a LessTif bug but for now we just
1840 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1843 /* add any args the user supplied for creation time */
1844 lw_add_value_args_to_args(val, al, &ac);
1846 text = XmCreateTextField(instance->parent, val->name, al, ac);
1848 XtAddCallback(text, XmNvalueChangedCallback,
1849 xm_generic_callback, (XtPointer) instance);
1851 XtManageChild(text);
1856 static Widget xm_create_label_field(widget_instance * instance)
1858 return xm_create_label(instance->parent, instance->info->val);
1861 Widget xm_create_label(Widget parent, widget_value * val)
1867 XtSetArg(al[ac], XmNsensitive, val->enabled);
1869 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1871 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1873 /* The highlight doesn't appear to be dynamically set which makes it
1874 look ugly. I think this may be a LessTif bug but for now we just
1876 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1879 /* add any args the user supplied for creation time */
1880 lw_add_value_args_to_args(val, al, &ac);
1882 label = XmCreateLabel(parent, val->name, al, ac);
1884 XtManageChild(label);
1886 /* Do it again for arguments that have no effect until the widget is realized. */
1888 lw_add_value_args_to_args(val, al, &ac);
1889 XtSetValues(label, al, ac);
1895 static Widget xm_create_combo_box(widget_instance * instance)
1900 widget_value *val = instance->info->val;
1902 XtSetArg(al[ac], XmNsensitive, val->enabled);
1904 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);
1906 XtSetArg(al[ac], XmNuserData, val->call_data);
1908 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);
1910 /* The highlight doesn't appear to be dynamically set which makes it
1911 look ugly. I think this may be a LessTif bug but for now we just
1913 XtSetArg(al[ac], XmNhighlightThickness, (Dimension) 0);
1916 /* add any args the user supplied for creation time */
1917 lw_add_value_args_to_args(val, al, &ac);
1919 combo = XmCreateDropDownComboBox(instance->parent, val->name, al, ac);
1921 XtAddCallback(combo, XmNselectionCallback, xm_generic_callback,
1922 (XtPointer) instance);
1924 XtManageChild(combo);
1929 #endif /* LWLIB_WIDGETS_MOTIF */
1931 /* Table of functions to create widgets */
1933 const widget_creation_entry xm_creation_table[] = {
1934 #ifdef LWLIB_MENUBARS_MOTIF
1935 {"menubar", make_menubar},
1936 {"popup", make_popup_menu},
1938 #ifdef LWLIB_SCROLLBARS_MOTIF
1939 {"vertical-scrollbar", make_vertical_scrollbar},
1940 {"horizontal-scrollbar", make_horizontal_scrollbar},
1942 #ifdef LWLIB_WIDGETS_MOTIF
1943 {"button", xm_create_button},
1944 {"progress", xm_create_progress},
1945 {"text-field", xm_create_text_field},
1946 {"label", xm_create_label_field},
1948 {"combo-box", xm_create_combo_box},
1954 /* Destruction of instances */
1955 void xm_destroy_instance(widget_instance * instance)
1957 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1958 /* It appears that this is used only for dialog boxes. */
1959 Widget widget = instance->widget;
1960 /* recycle the dialog boxes */
1961 /* Disable the recycling until we can find a way to have the dialog box
1962 get reasonable layout after we modify its contents. */
1963 if (0 && XtClass(widget) == xmDialogShellWidgetClass) {
1964 destroyed_instance *dead_instance =
1965 make_destroyed_instance(instance->info->name,
1966 instance->info->type,
1969 instance->pop_up_p);
1970 dead_instance->next = all_destroyed_instances;
1971 all_destroyed_instances = dead_instance;
1972 XtUnmanageChild(first_child(instance->widget));
1973 XFlush(XtDisplay(instance->widget));
1974 XtAddCallback(instance->parent, XtNdestroyCallback,
1975 mark_dead_instance_destroyed,
1976 (XtPointer) dead_instance);
1978 /* This might not be necessary now that the nosel is attached to
1979 popdown instead of destroy, but it can't hurt. */
1980 XtRemoveCallback(instance->widget, XtNdestroyCallback,
1981 xm_nosel_callback, (XtPointer) instance);
1983 XtDestroyWidget(instance->widget);
1985 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1989 #ifdef LWLIB_MENUBARS_MOTIF
1991 void xm_popup_menu(Widget widget, XEvent * event)
1993 if (event->type == ButtonPress || event->type == ButtonRelease) {
1994 /* This is so totally ridiculous: there's NO WAY to tell Motif
1995 that *any* button can select a menu item. Only one button
1996 can have that honor.
1999 if (event->xbutton.state & Button5Mask)
2000 trans = "<Btn5Down>";
2001 else if (event->xbutton.state & Button4Mask)
2002 trans = "<Btn4Down>";
2003 else if (event->xbutton.state & Button3Mask)
2004 trans = "<Btn3Down>";
2005 else if (event->xbutton.state & Button2Mask)
2006 trans = "<Btn2Down>";
2007 else if (event->xbutton.state & Button1Mask)
2008 trans = "<Btn1Down>";
2011 XtSetArg(al[0], XmNmenuPost, trans);
2012 XtSetValues(widget, al, 1);
2014 XmMenuPosition(widget, (XButtonPressedEvent *) event);
2016 XtManageChild(widget);
2021 #ifdef LWLIB_DIALOGS_MOTIF
2023 static void set_min_dialog_size(Widget w)
2029 XtSetArg(al[0], XmNwidth, &width);
2030 XtSetArg(al[1], XmNheight, &height);
2031 XtGetValues(w, al, 2);
2033 XtSetArg(al[0], XmNminWidth, width);
2034 XtSetArg(al[1], XmNminHeight, height);
2035 XtSetValues(w, al, 2);
2040 void xm_pop_instance(widget_instance * instance, Boolean up)
2042 Widget widget = instance->widget;
2044 #ifdef LWLIB_DIALOGS_MOTIF
2045 if (XtClass(widget) == xmDialogShellWidgetClass) {
2046 Widget widget_to_manage = first_child(widget);
2048 XtManageChild(widget_to_manage);
2049 set_min_dialog_size(widget);
2050 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
2052 XtUnmanageChild(widget_to_manage);
2057 XtManageChild(widget);
2059 XtUnmanageChild(widget);
2063 /* motif callback */
2065 enum do_call_type { pre_activate, selection, no_selection, post_activate };
2067 static void do_call(Widget widget, XtPointer closure, enum do_call_type type)
2069 XtPointer user_data;
2070 widget_instance *instance = (widget_instance *) closure;
2071 Widget instance_widget;
2077 if (widget->core.being_destroyed)
2080 instance_widget = instance->widget;
2081 if (!instance_widget)
2084 id = instance->info->id;
2086 XtSetArg(al[0], XmNuserData, &user_data);
2087 XtGetValues(widget, al, 1);
2090 if (instance->info->pre_activate_cb)
2091 instance->info->pre_activate_cb(widget, id, user_data);
2094 if (instance->info->selection_cb)
2095 instance->info->selection_cb(widget, id, user_data);
2098 if (instance->info->selection_cb)
2099 instance->info->selection_cb(widget, id,
2103 if (instance->info->post_activate_cb)
2104 instance->info->post_activate_cb(widget, id, user_data);
2111 /* Like lw_internal_update_other_instances except that it does not do
2112 anything if its shell parent is not managed. This is to protect
2113 lw_internal_update_other_instances to dereference freed memory
2114 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2117 xm_internal_update_other_instances(Widget widget, XtPointer closure,
2118 XtPointer call_data)
2121 for (parent = widget; parent; parent = XtParent(parent))
2122 if (XtIsShell(parent))
2124 else if (!XtIsManaged(parent))
2126 lw_internal_update_other_instances(widget, closure, call_data);
2130 xm_generic_callback(Widget widget, XtPointer closure, XtPointer call_data)
2132 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2133 /* We want the selected status to change only when we decide it
2134 should change. Yuck but correct. */
2135 if (XtClass(widget) == xmToggleButtonWidgetClass
2136 || XtClass(widget) == xmToggleButtonGadgetClass) {
2140 XtSetArg(al[0], XmNset, &check);
2141 XtGetValues(widget, al, 1);
2143 XtSetArg(al[0], XmNset, !check);
2144 XtSetValues(widget, al, 1);
2147 lw_internal_update_other_instances(widget, closure, call_data);
2148 do_call(widget, closure, selection);
2152 xm_pop_down_callback(Widget widget, XtPointer closure, XtPointer call_data)
2154 do_call(widget, closure, post_activate);
2157 #ifdef LWLIB_MENUBARS_MOTIF
2160 xm_pull_down_callback(Widget widget, XtPointer closure, XtPointer call_data)
2164 /* new behavior for incremental menu construction */
2168 do_call(widget, closure, pre_activate);
2171 #endif /* LWLIB_MENUBARS_MOTIF */
2173 #ifdef LWLIB_SCROLLBARS_MOTIF
2175 xm_scrollbar_callback(Widget widget, XtPointer closure, XtPointer call_data)
2177 widget_instance *instance = (widget_instance *) closure;
2179 XmScrollBarCallbackStruct *data =
2180 (XmScrollBarCallbackStruct *) call_data;
2181 scroll_event event_data;
2182 scrollbar_values *val =
2183 (scrollbar_values *) instance->info->val->scrollbar_data;
2186 if (!instance || widget->core.being_destroyed)
2189 id = instance->info->id;
2191 percent = (double)(data->value - 1) / (double)(INT_MAX - 1);
2192 event_data.slider_value =
2193 (int)(percent * (double)(val->maximum - val->minimum)) +
2196 if (event_data.slider_value > (val->maximum - val->slider_size))
2197 event_data.slider_value = val->maximum - val->slider_size;
2198 else if (event_data.slider_value < 1)
2199 event_data.slider_value = 1;
2202 switch (data->event->xany.type) {
2205 event_data.time = data->event->xkey.time;
2209 event_data.time = data->event->xbutton.time;
2212 event_data.time = data->event->xmotion.time;
2216 event_data.time = data->event->xcrossing.time;
2219 event_data.time = 0;
2223 event_data.time = 0;
2225 switch (data->reason) {
2226 case XmCR_DECREMENT:
2227 event_data.action = SCROLLBAR_LINE_UP;
2229 case XmCR_INCREMENT:
2230 event_data.action = SCROLLBAR_LINE_DOWN;
2232 case XmCR_PAGE_DECREMENT:
2233 event_data.action = SCROLLBAR_PAGE_UP;
2235 case XmCR_PAGE_INCREMENT:
2236 event_data.action = SCROLLBAR_PAGE_DOWN;
2239 event_data.action = SCROLLBAR_TOP;
2241 case XmCR_TO_BOTTOM:
2242 event_data.action = SCROLLBAR_BOTTOM;
2245 event_data.action = SCROLLBAR_DRAG;
2247 case XmCR_VALUE_CHANGED:
2248 event_data.action = SCROLLBAR_CHANGE;
2251 event_data.action = SCROLLBAR_CHANGE;
2255 if (instance->info->pre_activate_cb)
2256 instance->info->pre_activate_cb(widget, id,
2257 (XtPointer) & event_data);
2259 #endif /* LWLIB_SCROLLBARS_MOTIF */
2261 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2263 mark_dead_instance_destroyed(Widget widget, XtPointer closure,
2264 XtPointer call_data)
2266 destroyed_instance *instance = (destroyed_instance *) closure;
2267 instance->widget = NULL;
2271 xm_nosel_callback(Widget widget, XtPointer closure, XtPointer call_data)
2273 /* This callback is only called when a dialog box is dismissed with the wm's
2274 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2275 in that case, not just unmapped, so that it releases its keyboard grabs.
2276 But there are problems with running our callbacks while the widget is in
2277 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2278 instead of XmDESTROY and then destroy it ourself after having run the
2281 do_call(widget, closure, no_selection);
2282 XtDestroyWidget(widget);
2286 /* set the keyboard focus */
2287 void xm_set_keyboard_focus(Widget parent, Widget w)
2289 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
2290 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2291 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2292 Presumably the problem was elsewhere, and is now gone...