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 /* #### Temporary fix. I though Motif was supposed to grok %_
224 lw_remove_accelerator_spec (val->value);
226 #ifdef LWLIB_DIALOGS_MOTIF
228 * Sigh. The main text of a label is the name field for menubar
229 * entries. The value field is a possible additional field to be
230 * concatenated on to the name field. HOWEVER, with dialog boxes
231 * the value field is the complete text which is supposed to be
232 * displayed as the label. Yuck.
234 if (is_in_dialog_box (widget))
236 char *value_name = NULL;
238 value_name = resource_string (widget, val->value);
240 value_name = val->value;
243 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
246 #endif /* LWLIB_DIALOGS_MOTIF */
248 char *value_name = NULL;
249 char *res_name = NULL;
251 res_name = resource_string (widget, val->name);
252 /* Concatenating the value with itself seems just plain daft. */
256 XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
261 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
263 value_name = XtMalloc (strlen (val->value) + 2);
265 strcat (value_name, " ");
266 strcat (value_name, val->value);
269 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
272 XmStringConcat (name_string, val_string);
278 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
279 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
284 key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
285 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
289 XtSetValues (widget, al, ac);
292 XmStringFree (built_string);
295 XmStringFree (key_string);
298 XmStringFree (name_string);
301 XmStringFree (val_string);
304 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
306 \f/* update of list */
308 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
312 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
313 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
315 for (cur = val->contents, i = 0; cur; cur = cur->next)
318 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
320 XmListAddItem (widget, xmstr, 0);
322 XmListSelectPos (widget, i, False);
323 XmStringFree (xmstr);
327 \f/* update of buttons */
329 xm_update_pushbutton (widget_instance* instance, Widget widget,
333 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
334 XtSetValues (widget, al, 1);
335 XtRemoveAllCallbacks (widget, XmNactivateCallback);
336 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
340 xm_update_progress (widget_instance* instance, Widget scale,
345 Dimension height = 0;
349 XtSetArg (al [ac], XmNeditable, False); ac++;
353 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
355 height = (Dimension)lw_get_value_arg (val, XtNheight);
356 width = (Dimension)lw_get_value_arg (val, XtNwidth);
359 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
363 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
366 XtSetValues (scale, al, 1);
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
379 && val->contents->type == INCREMENTAL_TYPE)
381 /* okay, we're now doing a lisp callback to incrementally generate
383 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
384 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
386 XtCallCallbacks ((Widget)widget,
387 XmNcascadingCallback,
388 (XtPointer)val->contents);
391 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
392 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
397 #endif /* LWLIB_MENUBARS_MOTIF */
399 \f/* update toggle and radiobox */
401 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
404 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
405 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
407 XtSetArg (al [0], XmNset, val->selected);
408 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
409 XtSetValues (widget, al, 1);
413 xm_update_radiobox (widget_instance* instance, Widget widget,
419 /* update the callback */
420 XtRemoveAllCallbacks (widget, XmNentryCallback);
421 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
423 /* first update all the toggles */
424 /* Energize kernel interface is currently bad. It sets the selected widget
425 with the selected flag but returns it by its name. So we currently
426 have to support both setting the selection with the selected slot
427 of val contents and setting it with the "value" slot of val. The latter
428 has a higher priority. This to be removed when the kernel is fixed. */
429 for (cur = val->contents; cur; cur = cur->next)
431 toggle = XtNameToWidget (widget, cur->value);
435 XtSetArg (al [0], XmNsensitive, cur->enabled);
436 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
437 XtSetValues (toggle, al, 2);
441 /* The selected was specified by the value slot */
444 toggle = XtNameToWidget (widget, val->value);
448 XtSetArg (al [0], XmNset, True);
449 XtSetValues (toggle, al, 1);
454 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
455 /* update of combo box */
457 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
461 XtRemoveAllCallbacks (widget, XmNselectionCallback);
462 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
464 for (cur = val->contents, i = 0; cur; cur = cur->next)
467 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
469 XmListAddItem (CB_List (widget), xmstr, 0);
471 XmListSelectPos (CB_List (widget), i, False);
472 XmStringFree (xmstr);
477 #ifdef LWLIB_MENUBARS_MOTIF
479 \f/* update a popup menu, pulldown menu or a menubar */
481 make_menu_in_widget (widget_instance* instance, Widget widget,
484 Widget* children = 0;
492 Boolean menubar_p = False;
494 /* Allocate the children array */
495 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
496 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
498 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
499 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
500 XtGetValues (widget, al, 1);
502 /* add the unmap callback for popups and pulldowns */
503 /*** this sounds bogus ***/
504 /* probably because it is -- cet */
507 XtAddCallback (XtParent (widget), XmNpopdownCallback,
508 xm_pop_down_callback, (XtPointer)instance);
512 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
516 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
517 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
518 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
523 /* A pushright marker which is not needed for the real Motif
530 /* #### - xlwmenu.h supports several types that motif does
531 not. Also, motif supports pixmaps w/ type NO_LINE and
532 lwlib provides no way to access that functionality. --Stig */
533 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
535 button = XmCreateSeparator (widget, "separator", al, ac);
538 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
539 make_menu_in_widget (instance, menu, cur->contents);
540 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
541 button = XmCreateCascadeButton (widget, cur->name, al, ac);
543 xm_update_label (instance, button, cur);
545 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
546 (XtPointer)instance);
550 button = XmCreateCascadeButton (widget, cur->name, al, ac);
551 else if (!cur->call_data)
552 button = XmCreateLabel (widget, cur->name, al, ac);
553 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
555 XtSetArg (al [ac], XmNindicatorType,
556 (cur->type == TOGGLE_TYPE ?
557 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
558 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
559 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
562 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
564 xm_update_label (instance, button, cur);
566 /* don't add a callback to a simple label */
567 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
568 xm_update_toggle (instance, button, cur);
569 else if (cur->call_data)
570 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
571 (XtPointer)instance);
572 } /* switch (cur->type) */
575 children [num_children++] = button;
578 /* Last entry is the help button. This used be done after managing
579 the buttons. The comment claimed that it had to be done this way
580 otherwise the menubar ended up only 4 pixels high. That must
581 have been in the Old World. In the New World it stays the proper
582 height if you don't manage them until after you set this and as a
583 bonus the Help menu ends up where it is supposed to. */
587 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
588 XtSetValues (widget, al, ac);
592 XtManageChildren (children, num_children);
594 XtFree ((char *) children);
598 update_one_menu_entry (widget_instance* instance, Widget widget,
599 widget_value* val, Boolean deep_p)
604 widget_value* contents;
606 if (val->change == NO_CHANGE)
609 /* update the sensitivity and userdata */
610 /* Common to all widget types */
611 XtSetArg (al [0], XmNsensitive, val->enabled);
612 XtSetArg (al [1], XmNuserData, val->call_data);
613 XtSetValues (widget, al, 2);
615 /* update the menu button as a label. */
616 if (val->change >= VISIBLE_CHANGE)
618 xm_update_label (instance, widget, val);
619 if (XtClass (widget) == xmToggleButtonWidgetClass
620 || XtClass (widget) == xmToggleButtonGadgetClass)
622 xm_update_toggle (instance, widget, val);
627 /* update the pulldown/pullaside as needed */
629 XtSetArg (al [0], XmNsubMenuId, &menu);
630 XtGetValues (widget, al, 1);
632 contents = val->contents;
638 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
639 make_menu_in_widget (instance, menu, contents);
641 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
642 XtSetValues (widget, al, ac);
648 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
649 XtSetValues (widget, al, ac);
650 XtDestroyWidget (menu);
652 else if (deep_p && contents->change != NO_CHANGE)
653 xm_update_menu (instance, menu, val, 1);
657 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
660 /* Widget is a RowColumn widget whose contents have to be updated
661 * to reflect the list of items in val->contents */
662 if (val->contents->change == STRUCTURAL_CHANGE)
664 destroy_all_children (widget);
665 make_menu_in_widget (instance, widget, val->contents);
669 /* Update all the buttons of the RowColumn in order. */
671 unsigned int num_children;
673 widget_value *cur = 0;
675 children = XtCompositeChildren (widget, &num_children);
678 for (i = 0, cur = val->contents; i < num_children; i++)
682 /* skip if this is a pushright marker or a separator */
683 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
687 /* #### - this could puke if you have a separator as the
688 last item on a pullright menu. */
696 if (children [i]->core.being_destroyed
697 || strcmp (XtName (children [i]), cur->name))
699 update_one_menu_entry (instance, children [i], cur, deep_p);
702 XtFree ((char *) children);
709 #endif /* LWLIB_MENUBARS_MOTIF */
712 /* update text widgets */
715 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
717 XmTextSetString (widget, val->value ? val->value : (char *) "");
718 XtRemoveAllCallbacks (widget, XmNactivateCallback);
719 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
720 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
721 XtAddCallback (widget, XmNvalueChangedCallback,
722 xm_internal_update_other_instances, instance);
726 xm_update_text_field (widget_instance* instance, Widget widget,
729 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
730 XtRemoveAllCallbacks (widget, XmNactivateCallback);
731 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
732 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
733 XtAddCallback (widget, XmNvalueChangedCallback,
734 xm_internal_update_other_instances, instance);
738 #ifdef LWLIB_SCROLLBARS_MOTIF
741 * If this function looks like it does a lot more work than it needs to,
742 * you're right. Blame the Motif scrollbar for not being smart about
743 * updating its appearance.
746 xm_update_scrollbar (widget_instance *instance, Widget widget,
749 if (val->scrollbar_data)
751 scrollbar_values *data = val->scrollbar_data;
752 int widget_sliderSize, widget_val;
753 int new_sliderSize, new_value;
755 double h_water, l_water;
758 /* First size and position the scrollbar widget. */
759 XtSetArg (al [0], XtNx, data->scrollbar_x);
760 XtSetArg (al [1], XtNy, data->scrollbar_y);
761 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
762 XtSetArg (al [3], XtNheight, data->scrollbar_height);
763 XtSetValues (widget, al, 4);
765 /* Now size the scrollbar's slider. */
766 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
767 XtSetArg (al [1], XmNvalue, &widget_val);
768 XtGetValues (widget, al, 2);
770 percent = (double) data->slider_size /
771 (double) (data->maximum - data->minimum);
772 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
774 percent = (double) (data->slider_position - data->minimum) /
775 (double) (data->maximum - data->minimum);
776 new_value = (int) ((double) (INT_MAX - 1) * percent);
778 if (new_sliderSize > (INT_MAX - 1))
779 new_sliderSize = INT_MAX - 1;
780 else if (new_sliderSize < 1)
783 if (new_value > (INT_MAX - new_sliderSize))
784 new_value = INT_MAX - new_sliderSize;
785 else if (new_value < 1)
790 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
792 int force = ((INT_MAX - widget_sliderSize - widget_val)
794 : (INT_MAX - new_sliderSize - new_value));
797 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
798 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
799 || (double)new_value < (l_water * (double)widget_val)
800 || (double)new_value > (h_water * (double)widget_val))
802 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
809 #endif /* LWLIB_SCROLLBARS_MOTIF */
812 /* update a motif widget */
815 xm_update_one_widget (widget_instance* instance, Widget widget,
816 widget_value* val, Boolean deep_p)
822 /* Mark as not edited */
825 /* Common to all widget types */
826 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
827 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
828 XtSetValues (widget, al, ac);
830 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
831 /* Common to all label like widgets */
832 if (XtIsSubclass (widget, xmLabelWidgetClass))
833 xm_update_label (instance, widget, val);
835 class = XtClass (widget);
836 /* Class specific things */
837 if (class == xmPushButtonWidgetClass ||
838 class == xmArrowButtonWidgetClass)
840 xm_update_pushbutton (instance, widget, val);
842 #ifdef LWLIB_MENUBARS_MOTIF
843 else if (class == xmCascadeButtonWidgetClass)
845 xm_update_cascadebutton (instance, widget, val);
848 else if (class == xmToggleButtonWidgetClass
849 || class == xmToggleButtonGadgetClass)
851 xm_update_toggle (instance, widget, val);
853 else if (class == xmRowColumnWidgetClass)
855 Boolean radiobox = 0;
857 XtSetArg (al [0], XmNradioBehavior, &radiobox);
858 XtGetValues (widget, al, 1);
861 xm_update_radiobox (instance, widget, val);
862 #ifdef LWLIB_MENUBARS_MOTIF
864 xm_update_menu (instance, widget, val, deep_p);
867 else if (class == xmTextWidgetClass)
869 xm_update_text (instance, widget, val);
871 else if (class == xmTextFieldWidgetClass)
873 xm_update_text_field (instance, widget, val);
875 else if (class == xmListWidgetClass)
877 xm_update_list (instance, widget, val);
879 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
880 else if (class == xmComboBoxWidgetClass)
882 xm_update_combo_box (instance, widget, val);
885 #ifdef LWLIB_SCROLLBARS_MOTIF
886 else if (class == xmScrollBarWidgetClass)
888 xm_update_scrollbar (instance, widget, val);
891 else if (class == xmScaleWidgetClass)
893 xm_update_progress (instance, widget, val);
895 /* Lastly update our global arg values. */
896 if (val->args && val->args->nargs)
897 XtSetValues (widget, val->args->args, val->args->nargs);
900 \f/* getting the value back */
902 xm_update_one_value (widget_instance* instance, Widget widget,
905 WidgetClass class = XtClass (widget);
906 widget_value *old_wv;
908 /* copy the call_data slot into the "return" widget_value */
909 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
910 if (!strcmp (val->name, old_wv->name))
912 val->call_data = old_wv->call_data;
916 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
919 XtSetArg (al [0], XmNset, &val->selected);
920 XtGetValues (widget, al, 1);
923 else if (class == xmTextWidgetClass)
927 val->value = XmTextGetString (widget);
930 else if (class == xmTextFieldWidgetClass)
934 val->value = XmTextFieldGetString (widget);
937 else if (class == xmRowColumnWidgetClass)
939 Boolean radiobox = 0;
942 XtSetArg (al [0], XmNradioBehavior, &radiobox);
943 XtGetValues (widget, al, 1);
948 CompositeWidget radio = (CompositeWidget)widget;
950 for (i = 0; i < radio->composite.num_children; i++)
953 Widget toggle = radio->composite.children [i];
956 XtSetArg (al [0], XmNset, &set);
957 XtGetValues (toggle, al, 1);
962 val->value = safe_strdup (XtName (toggle));
968 else if (class == xmListWidgetClass
969 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
970 || class == xmComboBoxWidgetClass
976 Widget list = widget;
977 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
978 if (class == xmComboBoxWidgetClass)
979 list = CB_List (widget);
981 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
985 for (cur = val->contents, i = 0; cur; cur = cur->next)
989 cur->selected = False;
991 for (j = 0; j < pos_cnt; j++)
992 if (pos_list [j] == i)
994 cur->selected = True;
995 val->value = safe_strdup (cur->name);
999 XtFree ((char *) pos_list);
1002 #ifdef LWLIB_SCROLLBARS_MOTIF
1003 else if (class == xmScrollBarWidgetClass)
1005 /* This function is not used by the scrollbar. */
1012 /* This function is for activating a button from a program. It's wrong because
1013 we pass a NULL argument in the call_data which is not Motif compatible.
1014 This is used from the XmNdefaultAction callback of the List widgets to
1015 have a double-click put down a dialog box like the button would do.
1016 I could not find a way to do that with accelerators.
1019 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
1021 Widget button = (Widget)closure;
1022 XtCallCallbacks (button, XmNactivateCallback, NULL);
1025 /* creation functions */
1027 #ifdef LWLIB_DIALOGS_MOTIF
1031 #if (XmVersion >= 1002)
1032 # define ARMANDACTIVATE_KLUDGE
1036 #ifdef ARMANDACTIVATE_KLUDGE
1037 /* We want typing Return at a dialog box to select the default button; but
1038 we're satisfied with having it select the leftmost button instead.
1040 In Motif 1.1.5 we could do this by putting this resource in the
1043 *dialog*button1.accelerators:#override\
1044 <KeyPress>Return: ArmAndActivate()\n\
1045 <KeyPress>KP_Enter: ArmAndActivate()\n\
1046 Ctrl<KeyPress>m: ArmAndActivate()\n
1048 but that doesn't work with 1.2.1 and I don't understand why. However,
1049 doing the equivalent C code does work, with the notable disadvantage that
1050 the user can't override it. So that's what we do until we figure out
1051 something better....
1053 static char button_trans[] = "\
1054 <KeyPress>Return: ArmAndActivate()\n\
1055 <KeyPress>KP_Enter: ArmAndActivate()\n\
1056 Ctrl<KeyPress>m: ArmAndActivate()\n";
1058 #endif /* ARMANDACTIVATE_KLUDGE */
1062 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1063 was a segv down in libXm somewhere if you used the middle button on a
1064 dialog box to begin a drag; when you released the button to make a drop
1065 things would lose if you were not over the button where you started the
1066 drag (canceling the operation). This was probably due to the fact that
1067 the dialog boxes were not set up to handle a drag but were trying to do
1068 so anyway for some reason.
1070 So we disable drag-and-drop in dialog boxes by turning off the binding for
1071 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1072 solution as it only works in default configurations, but...
1074 static char disable_dnd_trans[] = "<Btn2Down>: ";
1075 #endif /* DND_KLUDGE */
1079 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1080 const char* shell_title, const char* icon_name,
1081 Boolean text_input_slot, Boolean radio_box, Boolean list,
1082 int left_buttons, int right_buttons)
1088 Widget icon_separator;
1093 Widget children [16]; /* for the final XtManageChildren */
1095 Arg al[64]; /* Arg List */
1096 int ac; /* Arg Count */
1100 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1101 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1102 #else /* ! DND_KLUDGE */
1103 # define DO_DND_KLUDGE(widget)
1104 #endif /* ! DND_KLUDGE */
1109 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1110 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1111 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1112 result = XmCreateDialogShell (parent, "dialog", al, ac);
1114 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1115 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1116 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1117 form = XmCreateForm (result, (char *) shell_title, al, ac);
1122 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1123 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1124 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1129 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1130 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1131 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1132 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1133 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1134 XtSetArg(al[ac], XmNspacing, 13); ac++;
1135 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1136 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1137 XtSetArg(al[ac], XmNisAligned, True); ac++;
1138 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1139 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1140 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1141 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1142 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1143 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1144 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1145 row = XmCreateRowColumn (form, "row", al, ac);
1148 for (i = 0; i < left_buttons; i++)
1150 char button_name [16];
1151 sprintf (button_name, "button%d", i + 1);
1155 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1156 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1158 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1159 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1160 DO_DND_KLUDGE (children [n_children]);
1164 button = children [n_children];
1166 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1167 XtSetValues (row, al, ac);
1169 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1171 XtTranslations losers = XtParseTranslationTable (button_trans);
1172 XtOverrideTranslations (button, losers);
1173 XtFree ((char *) losers);
1175 #endif /* ARMANDACTIVATE_KLUDGE */
1181 /* invisible separator button */
1183 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1184 children [n_children] = XmCreateLabel (row, "separator_button",
1186 DO_DND_KLUDGE (children [n_children]);
1189 for (i = 0; i < right_buttons; i++)
1191 char button_name [16];
1192 sprintf (button_name, "button%d", left_buttons + i + 1);
1194 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1195 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1196 DO_DND_KLUDGE (children [n_children]);
1197 if (! button) button = children [n_children];
1201 XtManageChildren (children, n_children);
1204 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1205 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1206 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1207 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1208 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1209 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1210 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1211 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1212 separator = XmCreateSeparator (form, "", al, ac);
1215 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1216 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1217 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1218 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1219 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1220 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1221 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1222 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1223 DO_DND_KLUDGE (icon);
1226 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1227 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1228 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1229 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1230 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1231 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1232 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1233 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1234 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1235 icon_separator = XmCreateLabel (form, "", al, ac);
1236 DO_DND_KLUDGE (icon_separator);
1238 if (text_input_slot)
1241 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1242 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1243 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1244 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1245 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1246 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1247 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1248 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1249 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1250 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1251 value = XmCreateTextField (form, "value", al, ac);
1252 DO_DND_KLUDGE (value);
1258 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1259 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1260 XtSetArg(al[ac], XmNspacing, 13); ac++;
1261 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1262 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1263 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1264 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1265 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1266 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1267 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1268 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1269 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1270 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1271 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1274 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1275 children [i++] = radio_butt;
1276 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1277 children [i++] = radio_butt;
1278 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1279 children [i++] = radio_butt;
1280 XtManageChildren (children, i);
1285 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1286 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1287 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1288 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1289 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1290 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1291 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1292 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1293 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1294 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1295 value = XmCreateScrolledList (form, "list", al, ac);
1297 /* this is the easiest way I found to have the double click in the
1298 list activate the default button */
1299 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1301 /* else add nothing; it's a separator */
1304 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1305 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1306 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1307 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1308 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1309 XtSetArg(al[ac], XmNbottomWidget,
1310 text_input_slot || radio_box || list ? value : separator); ac++;
1311 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1312 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1313 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1314 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1315 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1316 message = XmCreateLabel (form, "message", al, ac);
1317 DO_DND_KLUDGE (message);
1320 XtManageChild (value);
1323 children [i] = row; i++;
1324 children [i] = separator; i++;
1325 if (text_input_slot || radio_box)
1327 children [i] = value; i++;
1329 children [i] = message; i++;
1330 children [i] = icon; i++;
1331 children [i] = icon_separator; i++;
1332 XtManageChildren (children, i);
1334 if (text_input_slot || list)
1336 XtInstallAccelerators (value, button);
1337 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1341 XtInstallAccelerators (form, button);
1342 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1344 /* else we don' need no STEENKIN' assellerators. */
1347 XtFree ((char *) dnd_override);
1349 #undef DO_DND_KLUDGE
1354 static destroyed_instance*
1355 find_matching_instance (widget_instance* instance)
1357 destroyed_instance* cur;
1358 destroyed_instance* prev;
1359 char* type = instance->info->type;
1360 char* name = instance->info->name;
1362 for (prev = NULL, cur = all_destroyed_instances;
1364 prev = cur, cur = cur->next)
1366 if (!strcmp (cur->name, name)
1367 && !strcmp (cur->type, type)
1368 && cur->parent == instance->parent
1369 && cur->pop_up_p == instance->pop_up_p)
1372 prev->next = cur->next;
1374 all_destroyed_instances = cur->next;
1377 /* do some cleanup */
1378 else if (!cur->widget)
1381 prev->next = cur->next;
1383 all_destroyed_instances = cur->next;
1384 free_destroyed_instance (cur);
1385 cur = prev ? prev : all_destroyed_instances;
1392 recenter_widget (Widget widget)
1394 Widget parent = XtParent (widget);
1395 Screen* screen = XtScreen (widget);
1396 Dimension screen_width = WidthOfScreen (screen);
1397 Dimension screen_height = HeightOfScreen (screen);
1398 Dimension parent_width = 0;
1399 Dimension parent_height = 0;
1400 Dimension child_width = 0;
1401 Dimension child_height = 0;
1406 XtSetArg (al [0], XtNwidth, &child_width);
1407 XtSetArg (al [1], XtNheight, &child_height);
1408 XtGetValues (widget, al, 2);
1410 XtSetArg (al [0], XtNwidth, &parent_width);
1411 XtSetArg (al [1], XtNheight, &parent_height);
1412 XtGetValues (parent, al, 2);
1414 x = (Position) ((parent_width - child_width) / 2);
1415 y = (Position) ((parent_height - child_height) / 2);
1417 XtTranslateCoords (parent, x, y, &x, &y);
1419 if ((Dimension) (x + child_width) > screen_width)
1420 x = screen_width - child_width;
1424 if ((Dimension) (y + child_height) > screen_height)
1425 y = screen_height - child_height;
1429 XtSetArg (al [0], XtNx, x);
1430 XtSetArg (al [1], XtNy, y);
1431 XtSetValues (widget, al, 2);
1435 recycle_instance (destroyed_instance* instance)
1437 Widget widget = instance->widget;
1439 /* widget is NULL if the parent was destroyed. */
1445 /* Remove the destroy callback as the instance is not in the list
1447 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1448 mark_dead_instance_destroyed,
1449 (XtPointer)instance);
1451 /* Give the focus to the initial item */
1452 focus = XtNameToWidget (widget, "*value");
1454 focus = XtNameToWidget (widget, "*button1");
1456 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1458 /* shrink the separator label back to their original size */
1459 separator = XtNameToWidget (widget, "*separator_button");
1463 XtSetArg (al [0], XtNwidth, 5);
1464 XtSetArg (al [1], XtNheight, 5);
1465 XtSetValues (separator, al, 2);
1468 /* Center the dialog in its parent */
1469 recenter_widget (widget);
1471 free_destroyed_instance (instance);
1476 xm_create_dialog (widget_instance* instance)
1478 char* name = instance->info->type;
1479 Widget parent = instance->parent;
1481 Boolean pop_up_p = instance->pop_up_p;
1482 const char* shell_name = 0;
1483 const char* icon_name = 0;
1484 Boolean text_input_slot = False;
1485 Boolean radio_box = False;
1486 Boolean list = False;
1488 int left_buttons = 0;
1489 int right_buttons = 1;
1490 destroyed_instance* dead_one;
1492 /* try to find a widget to recycle */
1493 dead_one = find_matching_instance (instance);
1496 Widget recycled_widget = recycle_instance (dead_one);
1497 if (recycled_widget)
1498 return recycled_widget;
1503 icon_name = "dbox-error";
1504 shell_name = "Error";
1508 icon_name = "dbox-info";
1509 shell_name = "Information";
1514 icon_name = "dbox-question";
1515 shell_name = "Prompt";
1519 text_input_slot = True;
1520 icon_name = "dbox-question";
1521 shell_name = "Prompt";
1525 icon_name = "dbox-question";
1526 shell_name = "Question";
1530 total_buttons = name [1] - '0';
1532 if (name [3] == 'T' || name [3] == 't')
1534 text_input_slot = False;
1538 right_buttons = name [4] - '0';
1540 left_buttons = total_buttons - right_buttons;
1542 widget = make_dialog (name, parent, pop_up_p,
1543 shell_name, icon_name, text_input_slot, radio_box,
1544 list, left_buttons, right_buttons);
1546 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1547 (XtPointer) instance);
1551 #endif /* LWLIB_DIALOGS_MOTIF */
1553 #ifdef LWLIB_MENUBARS_MOTIF
1555 make_menubar (widget_instance* instance)
1560 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1561 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1563 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1567 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1569 Widget menu = (Widget) closure;
1570 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1574 make_popup_menu (widget_instance* instance)
1576 Widget parent = instance->parent;
1577 Window parent_window = parent->core.window;
1580 /* sets the parent window to 0 to fool Motif into not generating a grab */
1581 parent->core.window = 0;
1582 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1583 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1585 parent->core.window = parent_window;
1588 #endif /* LWLIB_MENUBARS_MOTIF */
1590 #ifdef LWLIB_SCROLLBARS_MOTIF
1592 make_scrollbar (widget_instance *instance, int vertical)
1596 static XtCallbackRec callbacks[2] =
1597 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1599 callbacks[0].closure = (XtPointer) instance;
1601 XtSetArg (al[ac], XmNminimum, 1); ac++;
1602 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1603 XtSetArg (al[ac], XmNincrement, 1); ac++;
1604 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1605 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1606 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1608 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1609 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1610 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1611 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1612 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1613 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1614 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1615 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1617 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1621 make_vertical_scrollbar (widget_instance *instance)
1623 return make_scrollbar (instance, 1);
1627 make_horizontal_scrollbar (widget_instance *instance)
1629 return make_scrollbar (instance, 0);
1632 #endif /* LWLIB_SCROLLBARS_MOTIF */
1634 #ifdef LWLIB_WIDGETS_MOTIF
1637 xm_create_button (widget_instance *instance)
1642 widget_value* val = instance->info->val;
1644 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1645 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1646 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1647 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1648 /* The highlight doesn't appear to be dynamically set which makes it
1649 look ugly. I think this may be a LessTif bug but for now we just
1651 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1653 /* add any args the user supplied for creation time */
1654 lw_add_value_args_to_args (val, al, &ac);
1656 if (!val->call_data)
1657 button = XmCreateLabel (instance->parent, val->name, al, ac);
1659 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1661 XtSetArg (al [ac], XmNset, val->selected); ac++;
1662 XtSetArg (al [ac], XmNindicatorType,
1663 (val->type == TOGGLE_TYPE ?
1664 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1665 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1666 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1667 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1668 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1669 (XtPointer)instance);
1673 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1674 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1675 (XtPointer)instance);
1678 XtManageChild (button);
1684 xm_create_progress (widget_instance *instance)
1688 Dimension height = 0;
1689 Dimension width = 0;
1691 widget_value* val = instance->info->val;
1692 if (!val->call_data)
1694 XtSetArg (al [ac], XmNeditable, False); ac++;
1698 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
1700 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1701 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1702 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1703 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1704 /* The highlight doesn't appear to be dynamically set which makes it
1705 look ugly. I think this may be a LessTif bug but for now we just
1707 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1709 height = (Dimension)lw_get_value_arg (val, XtNheight);
1710 width = (Dimension)lw_get_value_arg (val, XtNwidth);
1713 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
1717 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
1720 /* add any args the user supplied for creation time */
1721 lw_add_value_args_to_args (val, al, &ac);
1723 scale = XmCreateScale (instance->parent, val->name, al, ac);
1725 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1726 (XtPointer)instance);
1728 XtManageChild (scale);
1734 xm_create_text_field (widget_instance *instance)
1739 widget_value* val = instance->info->val;
1741 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1742 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1743 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1744 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1745 /* The highlight doesn't appear to be dynamically set which makes it
1746 look ugly. I think this may be a LessTif bug but for now we just
1748 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1750 /* add any args the user supplied for creation time */
1751 lw_add_value_args_to_args (val, al, &ac);
1753 text = XmCreateTextField (instance->parent, val->name, al, ac);
1755 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1756 (XtPointer)instance);
1758 XtManageChild (text);
1764 xm_create_label_field (widget_instance *instance)
1766 return xm_create_label (instance->parent, instance->info->val);
1770 xm_create_label (Widget parent, widget_value* val)
1776 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1777 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1778 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1779 /* The highlight doesn't appear to be dynamically set which makes it
1780 look ugly. I think this may be a LessTif bug but for now we just
1782 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1784 /* add any args the user supplied for creation time */
1785 lw_add_value_args_to_args (val, al, &ac);
1787 label = XmCreateLabel (parent, val->name, al, ac);
1789 XtManageChild (label);
1791 /* Do it again for arguments that have no effect until the widget is realized. */
1793 lw_add_value_args_to_args (val, al, &ac);
1794 XtSetValues (label, al, ac);
1801 xm_create_combo_box (widget_instance *instance)
1806 widget_value* val = instance->info->val;
1808 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1809 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1810 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1811 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1812 /* The highlight doesn't appear to be dynamically set which makes it
1813 look ugly. I think this may be a LessTif bug but for now we just
1815 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1817 /* add any args the user supplied for creation time */
1818 lw_add_value_args_to_args (val, al, &ac);
1820 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1822 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1823 (XtPointer)instance);
1825 XtManageChild (combo);
1830 #endif /* LWLIB_WIDGETS_MOTIF */
1833 /* Table of functions to create widgets */
1835 const widget_creation_entry
1836 xm_creation_table [] =
1838 #ifdef LWLIB_MENUBARS_MOTIF
1839 {"menubar", make_menubar},
1840 {"popup", make_popup_menu},
1842 #ifdef LWLIB_SCROLLBARS_MOTIF
1843 {"vertical-scrollbar", make_vertical_scrollbar},
1844 {"horizontal-scrollbar", make_horizontal_scrollbar},
1846 #ifdef LWLIB_WIDGETS_MOTIF
1847 {"button", xm_create_button},
1848 {"progress", xm_create_progress},
1849 {"text-field", xm_create_text_field},
1850 {"label", xm_create_label_field},
1852 {"combo-box", xm_create_combo_box},
1858 \f/* Destruction of instances */
1860 xm_destroy_instance (widget_instance* instance)
1862 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1863 /* It appears that this is used only for dialog boxes. */
1864 Widget widget = instance->widget;
1865 /* recycle the dialog boxes */
1866 /* Disable the recycling until we can find a way to have the dialog box
1867 get reasonable layout after we modify its contents. */
1869 && XtClass (widget) == xmDialogShellWidgetClass)
1871 destroyed_instance* dead_instance =
1872 make_destroyed_instance (instance->info->name,
1873 instance->info->type,
1876 instance->pop_up_p);
1877 dead_instance->next = all_destroyed_instances;
1878 all_destroyed_instances = dead_instance;
1879 XtUnmanageChild (first_child (instance->widget));
1880 XFlush (XtDisplay (instance->widget));
1881 XtAddCallback (instance->parent, XtNdestroyCallback,
1882 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1886 /* This might not be necessary now that the nosel is attached to
1887 popdown instead of destroy, but it can't hurt. */
1888 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1889 xm_nosel_callback, (XtPointer)instance);
1891 XtDestroyWidget (instance->widget);
1893 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1896 \f/* popup utility */
1897 #ifdef LWLIB_MENUBARS_MOTIF
1900 xm_popup_menu (Widget widget, XEvent *event)
1902 if (event->type == ButtonPress || event->type == ButtonRelease)
1904 /* This is so totally ridiculous: there's NO WAY to tell Motif
1905 that *any* button can select a menu item. Only one button
1906 can have that honor.
1909 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1910 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1911 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1912 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1913 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1917 XtSetArg (al [0], XmNmenuPost, trans);
1918 XtSetValues (widget, al, 1);
1920 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1922 XtManageChild (widget);
1927 #ifdef LWLIB_DIALOGS_MOTIF
1930 set_min_dialog_size (Widget w)
1936 XtSetArg (al [0], XmNwidth, &width);
1937 XtSetArg (al [1], XmNheight, &height);
1938 XtGetValues (w, al, 2);
1940 XtSetArg (al [0], XmNminWidth, width);
1941 XtSetArg (al [1], XmNminHeight, height);
1942 XtSetValues (w, al, 2);
1948 xm_pop_instance (widget_instance* instance, Boolean up)
1950 Widget widget = instance->widget;
1952 #ifdef LWLIB_DIALOGS_MOTIF
1953 if (XtClass (widget) == xmDialogShellWidgetClass)
1955 Widget widget_to_manage = first_child (widget);
1958 XtManageChild (widget_to_manage);
1959 set_min_dialog_size (widget);
1960 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1963 XtUnmanageChild (widget_to_manage);
1969 XtManageChild (widget);
1971 XtUnmanageChild (widget);
1976 /* motif callback */
1978 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1981 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1983 XtPointer user_data;
1984 widget_instance* instance = (widget_instance*)closure;
1985 Widget instance_widget;
1991 if (widget->core.being_destroyed)
1994 instance_widget = instance->widget;
1995 if (!instance_widget)
1998 id = instance->info->id;
2000 XtSetArg(al [0], XmNuserData, &user_data);
2001 XtGetValues (widget, al, 1);
2005 if (instance->info->pre_activate_cb)
2006 instance->info->pre_activate_cb (widget, id, user_data);
2009 if (instance->info->selection_cb)
2010 instance->info->selection_cb (widget, id, user_data);
2013 if (instance->info->selection_cb)
2014 instance->info->selection_cb (widget, id, (XtPointer) -1);
2017 if (instance->info->post_activate_cb)
2018 instance->info->post_activate_cb (widget, id, user_data);
2025 /* Like lw_internal_update_other_instances except that it does not do
2026 anything if its shell parent is not managed. This is to protect
2027 lw_internal_update_other_instances to dereference freed memory
2028 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2031 xm_internal_update_other_instances (Widget widget, XtPointer closure,
2032 XtPointer call_data)
2035 for (parent = widget; parent; parent = XtParent (parent))
2036 if (XtIsShell (parent))
2038 else if (!XtIsManaged (parent))
2040 lw_internal_update_other_instances (widget, closure, call_data);
2044 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
2046 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2047 /* We want the selected status to change only when we decide it
2048 should change. Yuck but correct. */
2049 if (XtClass (widget) == xmToggleButtonWidgetClass
2050 || XtClass (widget) == xmToggleButtonGadgetClass)
2055 XtSetArg (al [0], XmNset, &check);
2056 XtGetValues (widget, al, 1);
2058 XtSetArg (al [0], XmNset, !check);
2059 XtSetValues (widget, al, 1);
2062 lw_internal_update_other_instances (widget, closure, call_data);
2063 do_call (widget, closure, selection);
2067 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2069 do_call (widget, closure, post_activate);
2072 #ifdef LWLIB_MENUBARS_MOTIF
2075 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2080 /* new behavior for incremental menu construction */
2085 do_call (widget, closure, pre_activate);
2088 #endif /* LWLIB_MENUBARS_MOTIF */
2090 #ifdef LWLIB_SCROLLBARS_MOTIF
2092 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2094 widget_instance *instance = (widget_instance *) closure;
2096 XmScrollBarCallbackStruct *data =
2097 (XmScrollBarCallbackStruct *) call_data;
2098 scroll_event event_data;
2099 scrollbar_values *val =
2100 (scrollbar_values *) instance->info->val->scrollbar_data;
2103 if (!instance || widget->core.being_destroyed)
2106 id = instance->info->id;
2108 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2109 event_data.slider_value =
2110 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2112 if (event_data.slider_value > (val->maximum - val->slider_size))
2113 event_data.slider_value = val->maximum - val->slider_size;
2114 else if (event_data.slider_value < 1)
2115 event_data.slider_value = 1;
2119 switch (data->event->xany.type)
2123 event_data.time = data->event->xkey.time;
2127 event_data.time = data->event->xbutton.time;
2130 event_data.time = data->event->xmotion.time;
2134 event_data.time = data->event->xcrossing.time;
2137 event_data.time = 0;
2142 event_data.time = 0;
2144 switch (data->reason)
2146 case XmCR_DECREMENT:
2147 event_data.action = SCROLLBAR_LINE_UP;
2149 case XmCR_INCREMENT:
2150 event_data.action = SCROLLBAR_LINE_DOWN;
2152 case XmCR_PAGE_DECREMENT:
2153 event_data.action = SCROLLBAR_PAGE_UP;
2155 case XmCR_PAGE_INCREMENT:
2156 event_data.action = SCROLLBAR_PAGE_DOWN;
2159 event_data.action = SCROLLBAR_TOP;
2161 case XmCR_TO_BOTTOM:
2162 event_data.action = SCROLLBAR_BOTTOM;
2165 event_data.action = SCROLLBAR_DRAG;
2167 case XmCR_VALUE_CHANGED:
2168 event_data.action = SCROLLBAR_CHANGE;
2171 event_data.action = SCROLLBAR_CHANGE;
2175 if (instance->info->pre_activate_cb)
2176 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2178 #endif /* LWLIB_SCROLLBARS_MOTIF */
2180 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2182 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2183 XtPointer call_data)
2185 destroyed_instance* instance = (destroyed_instance*)closure;
2186 instance->widget = NULL;
2190 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2192 /* This callback is only called when a dialog box is dismissed with the wm's
2193 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2194 in that case, not just unmapped, so that it releases its keyboard grabs.
2195 But there are problems with running our callbacks while the widget is in
2196 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2197 instead of XmDESTROY and then destroy it ourself after having run the
2200 do_call (widget, closure, no_selection);
2201 XtDestroyWidget (widget);
2206 /* set the keyboard focus */
2208 xm_set_keyboard_focus (Widget parent, Widget w)
2210 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2211 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2212 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2213 Presumably the problem was elsewhere, and is now gone...