1 /* The lwlib interface to Motif widgets.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 This file is part of the Lucid Widget Library.
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
31 #include <X11/StringDefs.h>
32 #include <X11/IntrinsicP.h>
33 #include <X11/ObjectP.h>
34 #include <X11/CoreP.h>
35 #include <X11/CompositeP.h>
38 #include "lwlib-utils.h"
41 #include <Xm/BulletinB.h>
42 #include <Xm/CascadeB.h>
43 #include <Xm/DrawingA.h>
44 #include <Xm/FileSB.h>
47 #include <Xm/MenuShell.h>
48 #include <Xm/MessageB.h>
50 #include <Xm/PushBG.h>
51 #include <Xm/ArrowB.h>
52 #include <Xm/ScrollBar.h>
53 #include <Xm/SelectioB.h>
56 #include <Xm/ToggleB.h>
57 #include <Xm/ToggleBG.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/ScrolledW.h>
60 #include <Xm/Separator.h>
61 #include <Xm/DialogS.h>
63 #ifdef LWLIB_WIDGETS_MOTIF
66 #include <Xm/ComboBoxP.h>
70 #ifdef LWLIB_MENUBARS_MOTIF
71 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
73 static void xm_internal_update_other_instances (Widget, XtPointer,
75 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
76 static void xm_generic_callback (Widget, XtPointer, XtPointer);
77 static void mark_dead_instance_destroyed (Widget widget, XtPointer closure,
79 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
80 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
82 #ifdef LWLIB_SCROLLBARS_MOTIF
83 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
86 #ifdef LWLIB_MENUBARS_MOTIF
88 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
92 \f/* Structures to keep destroyed instances */
93 typedef struct _destroyed_instance
100 struct _destroyed_instance* next;
101 } destroyed_instance;
103 static destroyed_instance*
104 all_destroyed_instances = NULL;
106 /* Utility function. */
108 safe_strdup (char* s)
112 result = (char *) malloc (strlen (s) + 1);
119 static destroyed_instance*
120 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
123 destroyed_instance* instance =
124 (destroyed_instance*) malloc (sizeof (destroyed_instance));
125 instance->name = safe_strdup (name);
126 instance->type = safe_strdup (type);
127 instance->widget = widget;
128 instance->parent = parent;
129 instance->pop_up_p = pop_up_p;
130 instance->next = NULL;
135 free_destroyed_instance (destroyed_instance* instance)
137 free (instance->name);
138 free (instance->type);
142 \f/* motif utility functions */
144 first_child (Widget widget)
146 return ((CompositeWidget)widget)->composite.children [0];
150 lw_motif_widget_p (Widget widget)
153 #ifdef LWLIB_DIALOGS_MOTIF
154 XtClass (widget) == xmDialogShellWidgetClass ||
156 XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
160 resource_string (Widget widget, char *name)
165 resource.resource_name = "labelString";
166 resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
167 resource.resource_type = XtRString;
168 resource.resource_size = sizeof (String);
169 resource.resource_offset = 0;
170 resource.default_type = XtRImmediate;
171 resource.default_addr = 0;
173 XtGetSubresources (widget, (XtPointer)&result, name,
174 name, &resource, 1, NULL, 0);
180 #ifdef LWLIB_DIALOGS_MOTIF
183 is_in_dialog_box (Widget w)
187 wmshell = XtParent (w);
188 while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
189 wmshell = XtParent (wmshell);
191 if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
197 #endif /* LWLIB_DIALOGS_MOTIF */
199 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
201 /* update the label of anything subclass of a label */
203 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
205 XmString built_string = NULL;
206 XmString key_string = NULL;
207 XmString val_string = NULL;
208 XmString name_string = NULL;
213 /* Don't clobber pixmap types. */
214 XtSetArg (al [0], XmNlabelType, &type);
215 XtGetValues (widget, al, 1);
217 if (type == XmPIXMAP)
222 #ifdef LWLIB_DIALOGS_MOTIF
224 * Sigh. The main text of a label is the name field for menubar
225 * entries. The value field is a possible additional field to be
226 * concatenated on to the name field. HOWEVER, with dialog boxes
227 * the value field is the complete text which is supposed to be
228 * displayed as the label. Yuck.
230 if (is_in_dialog_box (widget))
232 char *value_name = NULL;
234 value_name = resource_string (widget, val->value);
236 value_name = val->value;
239 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
242 #endif /* LWLIB_DIALOGS_MOTIF */
244 char *value_name = NULL;
245 char *res_name = NULL;
247 res_name = resource_string (widget, val->name);
248 /* Concatenating the value with itself seems just plain daft. */
252 XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
257 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
259 value_name = XtMalloc (strlen (val->value) + 2);
261 strcat (value_name, " ");
262 strcat (value_name, val->value);
265 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
268 XmStringConcat (name_string, val_string);
274 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
275 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
280 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
281 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
285 XtSetValues (widget, al, ac);
288 XmStringFree (built_string);
291 XmStringFree (key_string);
294 XmStringFree (name_string);
297 XmStringFree (val_string);
300 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
302 \f/* update of list */
304 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
308 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
309 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
311 for (cur = val->contents, i = 0; cur; cur = cur->next)
314 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
316 XmListAddItem (widget, xmstr, 0);
318 XmListSelectPos (widget, i, False);
319 XmStringFree (xmstr);
323 \f/* update of buttons */
325 xm_update_pushbutton (widget_instance* instance, Widget widget,
329 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
330 XtSetValues (widget, al, 1);
331 XtRemoveAllCallbacks (widget, XmNactivateCallback);
332 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
335 #ifdef LWLIB_MENUBARS_MOTIF
338 xm_update_cascadebutton (widget_instance* instance, Widget widget,
341 /* Should also rebuild the menu by calling ...update_menu... */
343 && val->type == CASCADE_TYPE
345 && val->contents->type == INCREMENTAL_TYPE)
347 /* okay, we're now doing a lisp callback to incrementally generate
349 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
350 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
352 XtCallCallbacks ((Widget)widget,
353 XmNcascadingCallback,
354 (XtPointer)val->contents);
357 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
358 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
363 #endif /* LWLIB_MENUBARS_MOTIF */
365 \f/* update toggle and radiobox */
367 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
370 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
371 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
373 XtSetArg (al [0], XmNset, val->selected);
374 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
375 XtSetValues (widget, al, 1);
379 xm_update_radiobox (widget_instance* instance, Widget widget,
385 /* update the callback */
386 XtRemoveAllCallbacks (widget, XmNentryCallback);
387 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
389 /* first update all the toggles */
390 /* Energize kernel interface is currently bad. It sets the selected widget
391 with the selected flag but returns it by its name. So we currently
392 have to support both setting the selection with the selected slot
393 of val contents and setting it with the "value" slot of val. The latter
394 has a higher priority. This to be removed when the kernel is fixed. */
395 for (cur = val->contents; cur; cur = cur->next)
397 toggle = XtNameToWidget (widget, cur->value);
401 XtSetArg (al [0], XmNsensitive, cur->enabled);
402 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
403 XtSetValues (toggle, al, 2);
407 /* The selected was specified by the value slot */
410 toggle = XtNameToWidget (widget, val->value);
414 XtSetArg (al [0], XmNset, True);
415 XtSetValues (toggle, al, 1);
420 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
421 /* update of combo box */
423 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
427 XtRemoveAllCallbacks (widget, XmNselectionCallback);
428 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
430 for (cur = val->contents, i = 0; cur; cur = cur->next)
433 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
435 XmListAddItem (CB_List (widget), xmstr, 0);
437 XmListSelectPos (CB_List (widget), i, False);
438 XmStringFree (xmstr);
443 #ifdef LWLIB_MENUBARS_MOTIF
445 \f/* update a popup menu, pulldown menu or a menubar */
447 make_menu_in_widget (widget_instance* instance, Widget widget,
450 Widget* children = 0;
458 Boolean menubar_p = False;
460 /* Allocate the children array */
461 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
462 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
464 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
465 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
466 XtGetValues (widget, al, 1);
468 /* add the unmap callback for popups and pulldowns */
469 /*** this sounds bogus ***/
470 /* probably because it is -- cet */
473 XtAddCallback (XtParent (widget), XmNpopdownCallback,
474 xm_pop_down_callback, (XtPointer)instance);
478 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
482 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
483 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
484 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
489 /* A pushright marker which is not needed for the real Motif
496 /* #### - xlwmenu.h supports several types that motif does
497 not. Also, motif supports pixmaps w/ type NO_LINE and
498 lwlib provides no way to access that functionality. --Stig */
499 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
501 button = XmCreateSeparator (widget, "separator", al, ac);
504 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
505 make_menu_in_widget (instance, menu, cur->contents);
506 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
507 button = XmCreateCascadeButton (widget, cur->name, al, ac);
509 xm_update_label (instance, button, cur);
511 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
512 (XtPointer)instance);
516 button = XmCreateCascadeButton (widget, cur->name, al, ac);
517 else if (!cur->call_data)
518 button = XmCreateLabel (widget, cur->name, al, ac);
519 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
521 XtSetArg (al [ac], XmNindicatorType,
522 (cur->type == TOGGLE_TYPE ?
523 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
524 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
525 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
528 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
530 xm_update_label (instance, button, cur);
532 /* don't add a callback to a simple label */
533 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
534 xm_update_toggle (instance, button, cur);
535 else if (cur->call_data)
536 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
537 (XtPointer)instance);
538 } /* switch (cur->type) */
541 children [num_children++] = button;
544 /* Last entry is the help button. This used be done after managing
545 the buttons. The comment claimed that it had to be done this way
546 otherwise the menubar ended up only 4 pixels high. That must
547 have been in the Old World. In the New World it stays the proper
548 height if you don't manage them until after you set this and as a
549 bonus the Help menu ends up where it is supposed to. */
553 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
554 XtSetValues (widget, al, ac);
558 XtManageChildren (children, num_children);
560 XtFree ((char *) children);
564 update_one_menu_entry (widget_instance* instance, Widget widget,
565 widget_value* val, Boolean deep_p)
570 widget_value* contents;
572 if (val->change == NO_CHANGE)
575 /* update the sensitivity and userdata */
576 /* Common to all widget types */
577 XtSetArg (al [0], XmNsensitive, val->enabled);
578 XtSetArg (al [1], XmNuserData, val->call_data);
579 XtSetValues (widget, al, 2);
581 /* update the menu button as a label. */
582 if (val->change >= VISIBLE_CHANGE)
584 xm_update_label (instance, widget, val);
585 if (XtClass (widget) == xmToggleButtonWidgetClass
586 || XtClass (widget) == xmToggleButtonGadgetClass)
588 xm_update_toggle (instance, widget, val);
593 /* update the pulldown/pullaside as needed */
595 XtSetArg (al [0], XmNsubMenuId, &menu);
596 XtGetValues (widget, al, 1);
598 contents = val->contents;
604 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
605 make_menu_in_widget (instance, menu, contents);
607 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
608 XtSetValues (widget, al, ac);
614 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
615 XtSetValues (widget, al, ac);
616 XtDestroyWidget (menu);
618 else if (deep_p && contents->change != NO_CHANGE)
619 xm_update_menu (instance, menu, val, 1);
623 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
626 /* Widget is a RowColumn widget whose contents have to be updated
627 * to reflect the list of items in val->contents */
628 if (val->contents->change == STRUCTURAL_CHANGE)
630 destroy_all_children (widget);
631 make_menu_in_widget (instance, widget, val->contents);
635 /* Update all the buttons of the RowColumn in order. */
637 unsigned int num_children;
639 widget_value *cur = 0;
641 children = XtCompositeChildren (widget, &num_children);
644 for (i = 0, cur = val->contents; i < num_children; i++)
648 /* skip if this is a pushright marker or a separator */
649 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
653 /* #### - this could puke if you have a separator as the
654 last item on a pullright menu. */
662 if (children [i]->core.being_destroyed
663 || strcmp (XtName (children [i]), cur->name))
665 update_one_menu_entry (instance, children [i], cur, deep_p);
668 XtFree ((char *) children);
675 #endif /* LWLIB_MENUBARS_MOTIF */
678 /* update text widgets */
681 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
683 XmTextSetString (widget, val->value ? val->value : (char *) "");
684 XtRemoveAllCallbacks (widget, XmNactivateCallback);
685 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
686 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
687 XtAddCallback (widget, XmNvalueChangedCallback,
688 xm_internal_update_other_instances, instance);
692 xm_update_text_field (widget_instance* instance, Widget widget,
695 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
696 XtRemoveAllCallbacks (widget, XmNactivateCallback);
697 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
698 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
699 XtAddCallback (widget, XmNvalueChangedCallback,
700 xm_internal_update_other_instances, instance);
704 #ifdef LWLIB_SCROLLBARS_MOTIF
707 * If this function looks like it does a lot more work than it needs to,
708 * you're right. Blame the Motif scrollbar for not being smart about
709 * updating its appearance.
712 xm_update_scrollbar (widget_instance *instance, Widget widget,
715 if (val->scrollbar_data)
717 scrollbar_values *data = val->scrollbar_data;
718 int widget_sliderSize, widget_val;
719 int new_sliderSize, new_value;
721 double h_water, l_water;
724 /* First size and position the scrollbar widget. */
725 XtSetArg (al [0], XtNx, data->scrollbar_x);
726 XtSetArg (al [1], XtNy, data->scrollbar_y);
727 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
728 XtSetArg (al [3], XtNheight, data->scrollbar_height);
729 XtSetValues (widget, al, 4);
731 /* Now size the scrollbar's slider. */
732 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
733 XtSetArg (al [1], XmNvalue, &widget_val);
734 XtGetValues (widget, al, 2);
736 percent = (double) data->slider_size /
737 (double) (data->maximum - data->minimum);
738 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
740 percent = (double) (data->slider_position - data->minimum) /
741 (double) (data->maximum - data->minimum);
742 new_value = (int) ((double) (INT_MAX - 1) * percent);
744 if (new_sliderSize > (INT_MAX - 1))
745 new_sliderSize = INT_MAX - 1;
746 else if (new_sliderSize < 1)
749 if (new_value > (INT_MAX - new_sliderSize))
750 new_value = INT_MAX - new_sliderSize;
751 else if (new_value < 1)
756 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
758 int force = ((INT_MAX - widget_sliderSize - widget_val)
760 : (INT_MAX - new_sliderSize - new_value));
763 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
764 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
765 || (double)new_value < (l_water * (double)widget_val)
766 || (double)new_value > (h_water * (double)widget_val))
768 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
775 #endif /* LWLIB_SCROLLBARS_MOTIF */
778 /* update a motif widget */
781 xm_update_one_widget (widget_instance* instance, Widget widget,
782 widget_value* val, Boolean deep_p)
788 /* Mark as not edited */
791 /* Common to all widget types */
792 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
793 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
794 lw_add_value_args_to_args (val, al, &ac);
796 XtSetValues (widget, al, ac);
798 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
799 /* Common to all label like widgets */
800 if (XtIsSubclass (widget, xmLabelWidgetClass))
801 xm_update_label (instance, widget, val);
803 class = XtClass (widget);
804 /* Class specific things */
805 if (class == xmPushButtonWidgetClass ||
806 class == xmArrowButtonWidgetClass)
808 xm_update_pushbutton (instance, widget, val);
810 #ifdef LWLIB_MENUBARS_MOTIF
811 else if (class == xmCascadeButtonWidgetClass)
813 xm_update_cascadebutton (instance, widget, val);
816 else if (class == xmToggleButtonWidgetClass
817 || class == xmToggleButtonGadgetClass)
819 xm_update_toggle (instance, widget, val);
821 else if (class == xmRowColumnWidgetClass)
823 Boolean radiobox = 0;
825 XtSetArg (al [0], XmNradioBehavior, &radiobox);
826 XtGetValues (widget, al, 1);
829 xm_update_radiobox (instance, widget, val);
830 #ifdef LWLIB_MENUBARS_MOTIF
832 xm_update_menu (instance, widget, val, deep_p);
835 else if (class == xmTextWidgetClass)
837 xm_update_text (instance, widget, val);
839 else if (class == xmTextFieldWidgetClass)
841 xm_update_text_field (instance, widget, val);
843 else if (class == xmListWidgetClass)
845 xm_update_list (instance, widget, val);
847 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
848 else if (class == xmComboBoxWidgetClass)
850 xm_update_combo_box (instance, widget, val);
853 #ifdef LWLIB_SCROLLBARS_MOTIF
854 else if (class == xmScrollBarWidgetClass)
856 xm_update_scrollbar (instance, widget, val);
861 \f/* getting the value back */
863 xm_update_one_value (widget_instance* instance, Widget widget,
866 WidgetClass class = XtClass (widget);
867 widget_value *old_wv;
869 /* copy the call_data slot into the "return" widget_value */
870 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
871 if (!strcmp (val->name, old_wv->name))
873 val->call_data = old_wv->call_data;
877 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
880 XtSetArg (al [0], XmNset, &val->selected);
881 XtGetValues (widget, al, 1);
884 else if (class == xmTextWidgetClass)
888 val->value = XmTextGetString (widget);
891 else if (class == xmTextFieldWidgetClass)
895 val->value = XmTextFieldGetString (widget);
898 else if (class == xmRowColumnWidgetClass)
900 Boolean radiobox = 0;
903 XtSetArg (al [0], XmNradioBehavior, &radiobox);
904 XtGetValues (widget, al, 1);
909 CompositeWidget radio = (CompositeWidget)widget;
911 for (i = 0; i < radio->composite.num_children; i++)
914 Widget toggle = radio->composite.children [i];
917 XtSetArg (al [0], XmNset, &set);
918 XtGetValues (toggle, al, 1);
923 val->value = safe_strdup (XtName (toggle));
929 else if (class == xmListWidgetClass
930 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
931 || class == xmComboBoxWidgetClass
937 Widget list = widget;
938 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
939 if (class == xmComboBoxWidgetClass)
940 list = CB_List (widget);
942 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
946 for (cur = val->contents, i = 0; cur; cur = cur->next)
950 cur->selected = False;
952 for (j = 0; j < pos_cnt; j++)
953 if (pos_list [j] == i)
955 cur->selected = True;
956 val->value = safe_strdup (cur->name);
960 XtFree ((char *) pos_list);
963 #ifdef LWLIB_SCROLLBARS_MOTIF
964 else if (class == xmScrollBarWidgetClass)
966 /* This function is not used by the scrollbar. */
973 /* This function is for activating a button from a program. It's wrong because
974 we pass a NULL argument in the call_data which is not Motif compatible.
975 This is used from the XmNdefaultAction callback of the List widgets to
976 have a double-click put down a dialog box like the button would do.
977 I could not find a way to do that with accelerators.
980 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
982 Widget button = (Widget)closure;
983 XtCallCallbacks (button, XmNactivateCallback, NULL);
986 /* creation functions */
988 #ifdef LWLIB_DIALOGS_MOTIF
992 #if (XmVersion >= 1002)
993 # define ARMANDACTIVATE_KLUDGE
997 #ifdef ARMANDACTIVATE_KLUDGE
998 /* We want typing Return at a dialog box to select the default button; but
999 we're satisfied with having it select the leftmost button instead.
1001 In Motif 1.1.5 we could do this by putting this resource in the
1004 *dialog*button1.accelerators:#override\
1005 <KeyPress>Return: ArmAndActivate()\n\
1006 <KeyPress>KP_Enter: ArmAndActivate()\n\
1007 Ctrl<KeyPress>m: ArmAndActivate()\n
1009 but that doesn't work with 1.2.1 and I don't understand why. However,
1010 doing the equivalent C code does work, with the notable disadvantage that
1011 the user can't override it. So that's what we do until we figure out
1012 something better....
1014 static char button_trans[] = "\
1015 <KeyPress>Return: ArmAndActivate()\n\
1016 <KeyPress>KP_Enter: ArmAndActivate()\n\
1017 Ctrl<KeyPress>m: ArmAndActivate()\n";
1019 #endif /* ARMANDACTIVATE_KLUDGE */
1023 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1024 was a segv down in libXm somewhere if you used the middle button on a
1025 dialog box to begin a drag; when you released the button to make a drop
1026 things would lose if you were not over the button where you started the
1027 drag (canceling the operation). This was probably due to the fact that
1028 the dialog boxes were not set up to handle a drag but were trying to do
1029 so anyway for some reason.
1031 So we disable drag-and-drop in dialog boxes by turning off the binding for
1032 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1033 solution as it only works in default configurations, but...
1035 static char disable_dnd_trans[] = "<Btn2Down>: ";
1036 #endif /* DND_KLUDGE */
1040 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1041 const char* shell_title, const char* icon_name,
1042 Boolean text_input_slot, Boolean radio_box, Boolean list,
1043 int left_buttons, int right_buttons)
1049 Widget icon_separator;
1054 Widget children [16]; /* for the final XtManageChildren */
1056 Arg al[64]; /* Arg List */
1057 int ac; /* Arg Count */
1061 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1062 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1063 #else /* ! DND_KLUDGE */
1064 # define DO_DND_KLUDGE(widget)
1065 #endif /* ! DND_KLUDGE */
1070 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1071 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1072 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1073 result = XmCreateDialogShell (parent, "dialog", al, ac);
1075 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1076 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1077 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1078 form = XmCreateForm (result, (char *) shell_title, al, ac);
1083 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1084 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1085 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1090 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1091 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1092 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1093 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1094 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1095 XtSetArg(al[ac], XmNspacing, 13); ac++;
1096 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1097 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1098 XtSetArg(al[ac], XmNisAligned, True); ac++;
1099 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1100 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1101 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1102 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1103 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1104 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1105 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1106 row = XmCreateRowColumn (form, "row", al, ac);
1109 for (i = 0; i < left_buttons; i++)
1111 char button_name [16];
1112 sprintf (button_name, "button%d", i + 1);
1116 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1117 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1119 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1120 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1121 DO_DND_KLUDGE (children [n_children]);
1125 button = children [n_children];
1127 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1128 XtSetValues (row, al, ac);
1130 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1132 XtTranslations losers = XtParseTranslationTable (button_trans);
1133 XtOverrideTranslations (button, losers);
1134 XtFree ((char *) losers);
1136 #endif /* ARMANDACTIVATE_KLUDGE */
1142 /* invisible seperator button */
1144 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1145 children [n_children] = XmCreateLabel (row, "separator_button",
1147 DO_DND_KLUDGE (children [n_children]);
1150 for (i = 0; i < right_buttons; i++)
1152 char button_name [16];
1153 sprintf (button_name, "button%d", left_buttons + i + 1);
1155 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1156 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1157 DO_DND_KLUDGE (children [n_children]);
1158 if (! button) button = children [n_children];
1162 XtManageChildren (children, n_children);
1165 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1166 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1167 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1168 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1169 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1170 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1171 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1172 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1173 separator = XmCreateSeparator (form, "", al, ac);
1176 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1177 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1178 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1179 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1180 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1181 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1182 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1183 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1184 DO_DND_KLUDGE (icon);
1187 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1188 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1189 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1190 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1191 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1192 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1193 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1194 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1195 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1196 icon_separator = XmCreateLabel (form, "", al, ac);
1197 DO_DND_KLUDGE (icon_separator);
1199 if (text_input_slot)
1202 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1203 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1204 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1205 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1206 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1207 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1208 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1209 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1210 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1211 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1212 value = XmCreateTextField (form, "value", al, ac);
1213 DO_DND_KLUDGE (value);
1219 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1220 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1221 XtSetArg(al[ac], XmNspacing, 13); ac++;
1222 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1223 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1224 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1225 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1226 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1227 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1228 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1229 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1230 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1231 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1232 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1235 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1236 children [i++] = radio_butt;
1237 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1238 children [i++] = radio_butt;
1239 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1240 children [i++] = radio_butt;
1241 XtManageChildren (children, i);
1246 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1247 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1248 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1249 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1250 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1251 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1252 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1253 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1254 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1255 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1256 value = XmCreateScrolledList (form, "list", al, ac);
1258 /* this is the easiest way I found to have the dble click in the
1259 list activate the default button */
1260 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1264 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1265 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1266 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1267 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1268 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1269 XtSetArg(al[ac], XmNbottomWidget,
1270 text_input_slot || radio_box || list ? value : separator); ac++;
1271 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1272 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1273 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1274 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1275 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1276 message = XmCreateLabel (form, "message", al, ac);
1277 DO_DND_KLUDGE (message);
1280 XtManageChild (value);
1283 children [i] = row; i++;
1284 children [i] = separator; i++;
1285 if (text_input_slot || radio_box)
1287 children [i] = value; i++;
1289 children [i] = message; i++;
1290 children [i] = icon; i++;
1291 children [i] = icon_separator; i++;
1292 XtManageChildren (children, i);
1294 if (text_input_slot || list)
1296 XtInstallAccelerators (value, button);
1297 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1301 XtInstallAccelerators (form, button);
1302 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1306 XtFree ((char *) dnd_override);
1308 #undef DO_DND_KLUDGE
1313 static destroyed_instance*
1314 find_matching_instance (widget_instance* instance)
1316 destroyed_instance* cur;
1317 destroyed_instance* prev;
1318 char* type = instance->info->type;
1319 char* name = instance->info->name;
1321 for (prev = NULL, cur = all_destroyed_instances;
1323 prev = cur, cur = cur->next)
1325 if (!strcmp (cur->name, name)
1326 && !strcmp (cur->type, type)
1327 && cur->parent == instance->parent
1328 && cur->pop_up_p == instance->pop_up_p)
1331 prev->next = cur->next;
1333 all_destroyed_instances = cur->next;
1336 /* do some cleanup */
1337 else if (!cur->widget)
1340 prev->next = cur->next;
1342 all_destroyed_instances = cur->next;
1343 free_destroyed_instance (cur);
1344 cur = prev ? prev : all_destroyed_instances;
1351 recenter_widget (Widget widget)
1353 Widget parent = XtParent (widget);
1354 Screen* screen = XtScreen (widget);
1355 Dimension screen_width = WidthOfScreen (screen);
1356 Dimension screen_height = HeightOfScreen (screen);
1357 Dimension parent_width = 0;
1358 Dimension parent_height = 0;
1359 Dimension child_width = 0;
1360 Dimension child_height = 0;
1365 XtSetArg (al [0], XtNwidth, &child_width);
1366 XtSetArg (al [1], XtNheight, &child_height);
1367 XtGetValues (widget, al, 2);
1369 XtSetArg (al [0], XtNwidth, &parent_width);
1370 XtSetArg (al [1], XtNheight, &parent_height);
1371 XtGetValues (parent, al, 2);
1373 x = (Position) ((parent_width - child_width) / 2);
1374 y = (Position) ((parent_height - child_height) / 2);
1376 XtTranslateCoords (parent, x, y, &x, &y);
1378 if ((Dimension) (x + child_width) > screen_width)
1379 x = screen_width - child_width;
1383 if ((Dimension) (y + child_height) > screen_height)
1384 y = screen_height - child_height;
1388 XtSetArg (al [0], XtNx, x);
1389 XtSetArg (al [1], XtNy, y);
1390 XtSetValues (widget, al, 2);
1394 recycle_instance (destroyed_instance* instance)
1396 Widget widget = instance->widget;
1398 /* widget is NULL if the parent was destroyed. */
1404 /* Remove the destroy callback as the instance is not in the list
1406 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1407 mark_dead_instance_destroyed,
1408 (XtPointer)instance);
1410 /* Give the focus to the initial item */
1411 focus = XtNameToWidget (widget, "*value");
1413 focus = XtNameToWidget (widget, "*button1");
1415 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1417 /* shrink the separator label back to their original size */
1418 separator = XtNameToWidget (widget, "*separator_button");
1422 XtSetArg (al [0], XtNwidth, 5);
1423 XtSetArg (al [1], XtNheight, 5);
1424 XtSetValues (separator, al, 2);
1427 /* Center the dialog in its parent */
1428 recenter_widget (widget);
1430 free_destroyed_instance (instance);
1435 xm_create_dialog (widget_instance* instance)
1437 char* name = instance->info->type;
1438 Widget parent = instance->parent;
1440 Boolean pop_up_p = instance->pop_up_p;
1441 const char* shell_name = 0;
1442 const char* icon_name = 0;
1443 Boolean text_input_slot = False;
1444 Boolean radio_box = False;
1445 Boolean list = False;
1447 int left_buttons = 0;
1448 int right_buttons = 1;
1449 destroyed_instance* dead_one;
1451 /* try to find a widget to recycle */
1452 dead_one = find_matching_instance (instance);
1455 Widget recycled_widget = recycle_instance (dead_one);
1456 if (recycled_widget)
1457 return recycled_widget;
1462 icon_name = "dbox-error";
1463 shell_name = "Error";
1467 icon_name = "dbox-info";
1468 shell_name = "Information";
1473 icon_name = "dbox-question";
1474 shell_name = "Prompt";
1478 text_input_slot = True;
1479 icon_name = "dbox-question";
1480 shell_name = "Prompt";
1484 icon_name = "dbox-question";
1485 shell_name = "Question";
1489 total_buttons = name [1] - '0';
1491 if (name [3] == 'T' || name [3] == 't')
1493 text_input_slot = False;
1497 right_buttons = name [4] - '0';
1499 left_buttons = total_buttons - right_buttons;
1501 widget = make_dialog (name, parent, pop_up_p,
1502 shell_name, icon_name, text_input_slot, radio_box,
1503 list, left_buttons, right_buttons);
1505 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1506 (XtPointer) instance);
1510 #endif /* LWLIB_DIALOGS_MOTIF */
1512 #ifdef LWLIB_MENUBARS_MOTIF
1514 make_menubar (widget_instance* instance)
1519 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1520 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1522 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1526 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1528 Widget menu = (Widget) closure;
1529 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1533 make_popup_menu (widget_instance* instance)
1535 Widget parent = instance->parent;
1536 Window parent_window = parent->core.window;
1539 /* sets the parent window to 0 to fool Motif into not generating a grab */
1540 parent->core.window = 0;
1541 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1542 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1544 parent->core.window = parent_window;
1547 #endif /* LWLIB_MENUBARS_MOTIF */
1549 #ifdef LWLIB_SCROLLBARS_MOTIF
1551 make_scrollbar (widget_instance *instance, int vertical)
1555 static XtCallbackRec callbacks[2] =
1556 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1558 callbacks[0].closure = (XtPointer) instance;
1560 XtSetArg (al[ac], XmNminimum, 1); ac++;
1561 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1562 XtSetArg (al[ac], XmNincrement, 1); ac++;
1563 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1564 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1565 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1567 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1568 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1569 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1570 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1571 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1572 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1573 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1574 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1576 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1580 make_vertical_scrollbar (widget_instance *instance)
1582 return make_scrollbar (instance, 1);
1586 make_horizontal_scrollbar (widget_instance *instance)
1588 return make_scrollbar (instance, 0);
1591 #endif /* LWLIB_SCROLLBARS_MOTIF */
1593 #ifdef LWLIB_WIDGETS_MOTIF
1596 xm_create_button (widget_instance *instance)
1601 widget_value* val = instance->info->val;
1603 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1604 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1605 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1606 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1607 /* The highlight doesn't appear to be dynamically set which makes it
1608 look ugly. I think this may be a LessTif bug but for now we just
1610 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1612 /* add any args the user supplied for creation time */
1613 lw_add_value_args_to_args (val, al, &ac);
1615 if (!val->call_data)
1616 button = XmCreateLabel (instance->parent, val->name, al, ac);
1618 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1620 XtSetArg (al [ac], XmNset, val->selected); ac++;
1621 XtSetArg (al [ac], XmNindicatorType,
1622 (val->type == TOGGLE_TYPE ?
1623 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1624 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1625 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1626 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1627 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1628 (XtPointer)instance);
1632 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1633 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1634 (XtPointer)instance);
1637 XtManageChild (button);
1643 xm_create_progress (widget_instance *instance)
1648 widget_value* val = instance->info->val;
1650 if (!val->call_data)
1652 XtSetArg (al [ac], XmNsensitive, False); ac++;
1656 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1658 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1659 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1660 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1661 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1662 /* The highlight doesn't appear to be dynamically set which makes it
1663 look ugly. I think this may be a LessTif bug but for now we just
1665 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1666 /* add any args the user supplied for creation time */
1667 lw_add_value_args_to_args (val, al, &ac);
1669 scale = XmCreateScale (instance->parent, val->name, al, ac);
1671 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1672 (XtPointer)instance);
1674 XtManageChild (scale);
1680 xm_create_text_field (widget_instance *instance)
1685 widget_value* val = instance->info->val;
1687 XtSetArg (al [ac], XmNsensitive, val->enabled && val->call_data); ac++;
1688 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1689 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1690 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1691 /* The highlight doesn't appear to be dynamically set which makes it
1692 look ugly. I think this may be a LessTif bug but for now we just
1694 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1696 /* add any args the user supplied for creation time */
1697 lw_add_value_args_to_args (val, al, &ac);
1699 text = XmCreateTextField (instance->parent, val->name, al, ac);
1701 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1702 (XtPointer)instance);
1704 XtManageChild (text);
1710 xm_create_label_field (widget_instance *instance)
1712 return xm_create_label (instance->parent, instance->info->val);
1716 xm_create_label (Widget parent, widget_value* val)
1722 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1723 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1724 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1725 /* The highlight doesn't appear to be dynamically set which makes it
1726 look ugly. I think this may be a LessTif bug but for now we just
1728 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1730 /* add any args the user supplied for creation time */
1731 lw_add_value_args_to_args (val, al, &ac);
1733 label = XmCreateLabel (parent, val->name, al, ac);
1735 XtManageChild (label);
1737 /* Do it again for arguments that have no effect until the widget is realized. */
1739 lw_add_value_args_to_args (val, al, &ac);
1740 XtSetValues (label, al, ac);
1747 xm_create_combo_box (widget_instance *instance)
1752 widget_value* val = instance->info->val;
1754 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1755 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1756 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1757 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1758 /* The highlight doesn't appear to be dynamically set which makes it
1759 look ugly. I think this may be a LessTif bug but for now we just
1761 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1763 /* add any args the user supplied for creation time */
1764 lw_add_value_args_to_args (val, al, &ac);
1766 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1768 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1769 (XtPointer)instance);
1771 XtManageChild (combo);
1776 #endif /* LWLIB_WIDGETS_MOTIF */
1779 /* Table of functions to create widgets */
1781 widget_creation_entry
1782 xm_creation_table [] =
1784 #ifdef LWLIB_MENUBARS_MOTIF
1785 {"menubar", make_menubar},
1786 {"popup", make_popup_menu},
1788 #ifdef LWLIB_SCROLLBARS_MOTIF
1789 {"vertical-scrollbar", make_vertical_scrollbar},
1790 {"horizontal-scrollbar", make_horizontal_scrollbar},
1792 #ifdef LWLIB_WIDGETS_MOTIF
1793 {"button", xm_create_button},
1794 {"progress", xm_create_progress},
1795 {"text-field", xm_create_text_field},
1796 {"label", xm_create_label_field},
1798 {"combo-box", xm_create_combo_box},
1804 \f/* Destruction of instances */
1806 xm_destroy_instance (widget_instance* instance)
1808 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1809 /* It appears that this is used only for dialog boxes. */
1810 Widget widget = instance->widget;
1811 /* recycle the dialog boxes */
1812 /* Disable the recycling until we can find a way to have the dialog box
1813 get reasonable layout after we modify its contents. */
1815 && XtClass (widget) == xmDialogShellWidgetClass)
1817 destroyed_instance* dead_instance =
1818 make_destroyed_instance (instance->info->name,
1819 instance->info->type,
1822 instance->pop_up_p);
1823 dead_instance->next = all_destroyed_instances;
1824 all_destroyed_instances = dead_instance;
1825 XtUnmanageChild (first_child (instance->widget));
1826 XFlush (XtDisplay (instance->widget));
1827 XtAddCallback (instance->parent, XtNdestroyCallback,
1828 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1832 /* This might not be necessary now that the nosel is attached to
1833 popdown instead of destroy, but it can't hurt. */
1834 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1835 xm_nosel_callback, (XtPointer)instance);
1837 XtDestroyWidget (instance->widget);
1839 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1842 \f/* popup utility */
1843 #ifdef LWLIB_MENUBARS_MOTIF
1846 xm_popup_menu (Widget widget, XEvent *event)
1848 if (event->type == ButtonPress || event->type == ButtonRelease)
1850 /* This is so totally ridiculous: there's NO WAY to tell Motif
1851 that *any* button can select a menu item. Only one button
1852 can have that honor.
1855 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1856 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1857 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1858 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1859 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1863 XtSetArg (al [0], XmNmenuPost, trans);
1864 XtSetValues (widget, al, 1);
1866 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1868 XtManageChild (widget);
1873 #ifdef LWLIB_DIALOGS_MOTIF
1876 set_min_dialog_size (Widget w)
1882 XtSetArg (al [0], XmNwidth, &width);
1883 XtSetArg (al [1], XmNheight, &height);
1884 XtGetValues (w, al, 2);
1886 XtSetArg (al [0], XmNminWidth, width);
1887 XtSetArg (al [1], XmNminHeight, height);
1888 XtSetValues (w, al, 2);
1894 xm_pop_instance (widget_instance* instance, Boolean up)
1896 Widget widget = instance->widget;
1898 #ifdef LWLIB_DIALOGS_MOTIF
1899 if (XtClass (widget) == xmDialogShellWidgetClass)
1901 Widget widget_to_manage = first_child (widget);
1904 XtManageChild (widget_to_manage);
1905 set_min_dialog_size (widget);
1906 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1909 XtUnmanageChild (widget_to_manage);
1915 XtManageChild (widget);
1917 XtUnmanageChild (widget);
1922 /* motif callback */
1924 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1927 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1929 XtPointer user_data;
1930 widget_instance* instance = (widget_instance*)closure;
1931 Widget instance_widget;
1937 if (widget->core.being_destroyed)
1940 instance_widget = instance->widget;
1941 if (!instance_widget)
1944 id = instance->info->id;
1946 XtSetArg(al [0], XmNuserData, &user_data);
1947 XtGetValues (widget, al, 1);
1951 if (instance->info->pre_activate_cb)
1952 instance->info->pre_activate_cb (widget, id, user_data);
1955 if (instance->info->selection_cb)
1956 instance->info->selection_cb (widget, id, user_data);
1959 if (instance->info->selection_cb)
1960 instance->info->selection_cb (widget, id, (XtPointer) -1);
1963 if (instance->info->post_activate_cb)
1964 instance->info->post_activate_cb (widget, id, user_data);
1971 /* Like lw_internal_update_other_instances except that it does not do
1972 anything if its shell parent is not managed. This is to protect
1973 lw_internal_update_other_instances to dereference freed memory
1974 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1977 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1978 XtPointer call_data)
1981 for (parent = widget; parent; parent = XtParent (parent))
1982 if (XtIsShell (parent))
1984 else if (!XtIsManaged (parent))
1986 lw_internal_update_other_instances (widget, closure, call_data);
1990 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1992 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
1993 /* We want the selected status to change only when we decide it
1994 should change. Yuck but correct. */
1995 if (XtClass (widget) == xmToggleButtonWidgetClass
1996 || XtClass (widget) == xmToggleButtonGadgetClass)
2001 XtSetArg (al [0], XmNset, &check);
2002 XtGetValues (widget, al, 1);
2004 XtSetArg (al [0], XmNset, !check);
2005 XtSetValues (widget, al, 1);
2008 lw_internal_update_other_instances (widget, closure, call_data);
2009 do_call (widget, closure, selection);
2013 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2015 do_call (widget, closure, post_activate);
2018 #ifdef LWLIB_MENUBARS_MOTIF
2021 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2026 /* new behavior for incremental menu construction */
2031 do_call (widget, closure, pre_activate);
2034 #endif /* LWLIB_MENUBARS_MOTIF */
2036 #ifdef LWLIB_SCROLLBARS_MOTIF
2038 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2040 widget_instance *instance = (widget_instance *) closure;
2042 XmScrollBarCallbackStruct *data =
2043 (XmScrollBarCallbackStruct *) call_data;
2044 scroll_event event_data;
2045 scrollbar_values *val =
2046 (scrollbar_values *) instance->info->val->scrollbar_data;
2049 if (!instance || widget->core.being_destroyed)
2052 id = instance->info->id;
2054 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2055 event_data.slider_value =
2056 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2058 if (event_data.slider_value > (val->maximum - val->slider_size))
2059 event_data.slider_value = val->maximum - val->slider_size;
2060 else if (event_data.slider_value < 1)
2061 event_data.slider_value = 1;
2065 switch (data->event->xany.type)
2069 event_data.time = data->event->xkey.time;
2073 event_data.time = data->event->xbutton.time;
2076 event_data.time = data->event->xmotion.time;
2080 event_data.time = data->event->xcrossing.time;
2083 event_data.time = 0;
2088 event_data.time = 0;
2090 switch (data->reason)
2092 case XmCR_DECREMENT:
2093 event_data.action = SCROLLBAR_LINE_UP;
2095 case XmCR_INCREMENT:
2096 event_data.action = SCROLLBAR_LINE_DOWN;
2098 case XmCR_PAGE_DECREMENT:
2099 event_data.action = SCROLLBAR_PAGE_UP;
2101 case XmCR_PAGE_INCREMENT:
2102 event_data.action = SCROLLBAR_PAGE_DOWN;
2105 event_data.action = SCROLLBAR_TOP;
2107 case XmCR_TO_BOTTOM:
2108 event_data.action = SCROLLBAR_BOTTOM;
2111 event_data.action = SCROLLBAR_DRAG;
2113 case XmCR_VALUE_CHANGED:
2114 event_data.action = SCROLLBAR_CHANGE;
2117 event_data.action = SCROLLBAR_CHANGE;
2121 if (instance->info->pre_activate_cb)
2122 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2124 #endif /* LWLIB_SCROLLBARS_MOTIF */
2126 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2128 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2129 XtPointer call_data)
2131 destroyed_instance* instance = (destroyed_instance*)closure;
2132 instance->widget = NULL;
2136 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2138 /* This callback is only called when a dialog box is dismissed with the wm's
2139 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2140 in that case, not just unmapped, so that it releases its keyboard grabs.
2141 But there are problems with running our callbacks while the widget is in
2142 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2143 instead of XmDESTROY and then destroy it ourself after having run the
2146 do_call (widget, closure, no_selection);
2147 XtDestroyWidget (widget);
2152 /* set the keyboard focus */
2154 xm_set_keyboard_focus (Widget parent, Widget w)
2156 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2157 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2158 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2159 Presumably the problem was elsewhere, and is now gone...