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);
305 xm_safe_update_label (widget_instance* instance, Widget widget, widget_value* val)
307 /* Don't clobber non-labels. */
308 if (XtIsSubclass (widget, xmLabelWidgetClass))
309 xm_update_label (instance, widget, val);
312 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
314 \f/* update of list */
316 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
320 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
321 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
323 for (cur = val->contents, i = 0; cur; cur = cur->next)
326 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
328 XmListAddItem (widget, xmstr, 0);
330 XmListSelectPos (widget, i, False);
331 XmStringFree (xmstr);
335 \f/* update of buttons */
337 xm_update_pushbutton (widget_instance* instance, Widget widget,
341 XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
342 XtSetValues (widget, al, 1);
343 XtRemoveAllCallbacks (widget, XmNactivateCallback);
344 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
347 #ifdef LWLIB_WIDGETS_MOTIF
349 xm_update_progress (widget_instance* instance, Widget scale,
354 Dimension height = 0;
358 XtSetArg (al [ac], XmNeditable, False); ac++;
362 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
364 height = (Dimension)lw_get_value_arg (val, XtNheight);
365 width = (Dimension)lw_get_value_arg (val, XtNwidth);
368 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
372 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
375 XtSetValues (scale, al, 1);
377 #endif /* LWLIB_WIDGETS_MOTIF */
379 #ifdef LWLIB_MENUBARS_MOTIF
382 xm_update_cascadebutton (widget_instance* instance, Widget widget,
385 /* Should also rebuild the menu by calling ...update_menu... */
387 && val->type == CASCADE_TYPE
389 && val->contents->type == INCREMENTAL_TYPE)
391 /* okay, we're now doing a lisp callback to incrementally generate
393 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
394 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
396 XtCallCallbacks ((Widget)widget,
397 XmNcascadingCallback,
398 (XtPointer)val->contents);
401 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
402 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
407 #endif /* LWLIB_MENUBARS_MOTIF */
409 \f/* update toggle and radiobox */
411 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
414 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
415 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
417 XtSetArg (al [0], XmNset, val->selected);
418 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
419 XtSetValues (widget, al, 1);
423 xm_update_radiobox (widget_instance* instance, Widget widget,
429 /* update the callback */
430 XtRemoveAllCallbacks (widget, XmNentryCallback);
431 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
433 /* first update all the toggles */
434 /* Energize kernel interface is currently bad. It sets the selected widget
435 with the selected flag but returns it by its name. So we currently
436 have to support both setting the selection with the selected slot
437 of val contents and setting it with the "value" slot of val. The latter
438 has a higher priority. This to be removed when the kernel is fixed. */
439 for (cur = val->contents; cur; cur = cur->next)
441 toggle = XtNameToWidget (widget, cur->value);
445 XtSetArg (al [0], XmNsensitive, cur->enabled);
446 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
447 XtSetValues (toggle, al, 2);
451 /* The selected was specified by the value slot */
454 toggle = XtNameToWidget (widget, val->value);
458 XtSetArg (al [0], XmNset, True);
459 XtSetValues (toggle, al, 1);
464 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
465 /* update of combo box */
467 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
471 XtRemoveAllCallbacks (widget, XmNselectionCallback);
472 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
474 for (cur = val->contents, i = 0; cur; cur = cur->next)
477 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
479 XmListAddItem (CB_List (widget), xmstr, 0);
481 XmListSelectPos (CB_List (widget), i, False);
482 XmStringFree (xmstr);
487 #ifdef LWLIB_MENUBARS_MOTIF
489 \f/* update a popup menu, pulldown menu or a menubar */
491 make_menu_in_widget (widget_instance* instance, Widget widget,
494 Widget* children = 0;
502 Boolean menubar_p = False;
504 /* Allocate the children array */
505 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
506 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
508 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
509 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
510 XtGetValues (widget, al, 1);
512 /* add the unmap callback for popups and pulldowns */
513 /*** this sounds bogus ***/
514 /* probably because it is -- cet */
517 XtAddCallback (XtParent (widget), XmNpopdownCallback,
518 xm_pop_down_callback, (XtPointer)instance);
522 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
526 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
527 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
528 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
533 /* A pushright marker which is not needed for the real Motif
540 /* #### - xlwmenu.h supports several types that motif does
541 not. Also, motif supports pixmaps w/ type NO_LINE and
542 lwlib provides no way to access that functionality. --Stig */
543 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
545 button = XmCreateSeparator (widget, "separator", al, ac);
548 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
549 make_menu_in_widget (instance, menu, cur->contents);
550 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
551 button = XmCreateCascadeButton (widget, cur->name, al, ac);
553 xm_safe_update_label (instance, button, cur);
555 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
556 (XtPointer)instance);
560 button = XmCreateCascadeButton (widget, cur->name, al, ac);
561 else if (!cur->call_data)
562 button = XmCreateLabel (widget, cur->name, al, ac);
563 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
565 XtSetArg (al [ac], XmNindicatorType,
566 (cur->type == TOGGLE_TYPE ?
567 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
568 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
569 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
572 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
574 xm_safe_update_label (instance, button, cur);
576 /* don't add a callback to a simple label */
577 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
578 xm_update_toggle (instance, button, cur);
579 else if (cur->call_data)
580 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
581 (XtPointer)instance);
582 } /* switch (cur->type) */
585 children [num_children++] = button;
588 /* Last entry is the help button. This used be done after managing
589 the buttons. The comment claimed that it had to be done this way
590 otherwise the menubar ended up only 4 pixels high. That must
591 have been in the Old World. In the New World it stays the proper
592 height if you don't manage them until after you set this and as a
593 bonus the Help menu ends up where it is supposed to. */
597 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
598 XtSetValues (widget, al, ac);
602 XtManageChildren (children, num_children);
604 XtFree ((char *) children);
608 update_one_menu_entry (widget_instance* instance, Widget widget,
609 widget_value* val, Boolean deep_p)
614 widget_value* contents;
616 if (val->change == NO_CHANGE)
619 /* update the sensitivity and userdata */
620 /* Common to all widget types */
621 XtSetArg (al [0], XmNsensitive, val->enabled);
622 XtSetArg (al [1], XmNuserData, val->call_data);
623 XtSetValues (widget, al, 2);
625 /* update the menu button as a label. */
626 if (val->change >= VISIBLE_CHANGE)
628 xm_safe_update_label (instance, widget, val);
630 if (XtClass (widget) == xmToggleButtonWidgetClass
631 || XtClass (widget) == xmToggleButtonGadgetClass)
633 xm_update_toggle (instance, widget, val);
638 /* update the pulldown/pullaside as needed */
640 XtSetArg (al [0], XmNsubMenuId, &menu);
641 XtGetValues (widget, al, 1);
643 contents = val->contents;
649 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
650 make_menu_in_widget (instance, menu, contents);
652 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
653 XtSetValues (widget, al, ac);
659 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
660 XtSetValues (widget, al, ac);
661 XtDestroyWidget (menu);
663 else if (deep_p && contents->change != NO_CHANGE)
664 xm_update_menu (instance, menu, val, 1);
668 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
671 /* Widget is a RowColumn widget whose contents have to be updated
672 * to reflect the list of items in val->contents */
673 if (val->contents->change == STRUCTURAL_CHANGE)
675 destroy_all_children (widget);
676 make_menu_in_widget (instance, widget, val->contents);
680 /* Update all the buttons of the RowColumn in order. */
682 unsigned int num_children;
684 widget_value *cur = 0;
686 children = XtCompositeChildren (widget, &num_children);
689 for (i = 0, cur = val->contents; i < num_children; i++)
693 /* skip if this is a pushright marker or a separator */
694 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
698 /* #### - this could puke if you have a separator as the
699 last item on a pullright menu. */
707 if (children [i]->core.being_destroyed
708 || strcmp (XtName (children [i]), cur->name))
710 update_one_menu_entry (instance, children [i], cur, deep_p);
713 XtFree ((char *) children);
720 #endif /* LWLIB_MENUBARS_MOTIF */
723 /* update text widgets */
726 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
728 XmTextSetString (widget, val->value ? val->value : (char *) "");
729 XtRemoveAllCallbacks (widget, XmNactivateCallback);
730 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
731 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
732 XtAddCallback (widget, XmNvalueChangedCallback,
733 xm_internal_update_other_instances, instance);
737 xm_update_text_field (widget_instance* instance, Widget widget,
740 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
741 XtRemoveAllCallbacks (widget, XmNactivateCallback);
742 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
743 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
744 XtAddCallback (widget, XmNvalueChangedCallback,
745 xm_internal_update_other_instances, instance);
749 #ifdef LWLIB_SCROLLBARS_MOTIF
752 * If this function looks like it does a lot more work than it needs to,
753 * you're right. Blame the Motif scrollbar for not being smart about
754 * updating its appearance.
757 xm_update_scrollbar (widget_instance *instance, Widget widget,
760 if (val->scrollbar_data)
762 scrollbar_values *data = val->scrollbar_data;
763 int widget_sliderSize, widget_val;
764 int new_sliderSize, new_value;
766 double h_water, l_water;
769 /* First size and position the scrollbar widget. */
770 XtSetArg (al [0], XtNx, data->scrollbar_x);
771 XtSetArg (al [1], XtNy, data->scrollbar_y);
772 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
773 XtSetArg (al [3], XtNheight, data->scrollbar_height);
774 XtSetValues (widget, al, 4);
776 /* Now size the scrollbar's slider. */
777 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
778 XtSetArg (al [1], XmNvalue, &widget_val);
779 XtGetValues (widget, al, 2);
781 percent = (double) data->slider_size /
782 (double) (data->maximum - data->minimum);
783 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
785 percent = (double) (data->slider_position - data->minimum) /
786 (double) (data->maximum - data->minimum);
787 new_value = (int) ((double) (INT_MAX - 1) * percent);
789 if (new_sliderSize > (INT_MAX - 1))
790 new_sliderSize = INT_MAX - 1;
791 else if (new_sliderSize < 1)
794 if (new_value > (INT_MAX - new_sliderSize))
795 new_value = INT_MAX - new_sliderSize;
796 else if (new_value < 1)
801 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
803 int force = ((INT_MAX - widget_sliderSize - widget_val)
805 : (INT_MAX - new_sliderSize - new_value));
808 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
809 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
810 || (double)new_value < (l_water * (double)widget_val)
811 || (double)new_value > (h_water * (double)widget_val))
813 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
820 #endif /* LWLIB_SCROLLBARS_MOTIF */
823 /* update a motif widget */
826 xm_update_one_widget (widget_instance* instance, Widget widget,
827 widget_value* val, Boolean deep_p)
833 /* Mark as not edited */
836 /* Common to all widget types */
837 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
838 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
839 XtSetValues (widget, al, ac);
841 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
842 /* Common to all label like widgets */
843 xm_safe_update_label (instance, widget, val);
845 class = XtClass (widget);
846 /* Class specific things */
847 if (class == xmPushButtonWidgetClass ||
848 class == xmArrowButtonWidgetClass)
850 xm_update_pushbutton (instance, widget, val);
852 #ifdef LWLIB_MENUBARS_MOTIF
853 else if (class == xmCascadeButtonWidgetClass)
855 xm_update_cascadebutton (instance, widget, val);
858 else if (class == xmToggleButtonWidgetClass
859 || class == xmToggleButtonGadgetClass)
861 xm_update_toggle (instance, widget, val);
863 else if (class == xmRowColumnWidgetClass)
865 Boolean radiobox = 0;
867 XtSetArg (al [0], XmNradioBehavior, &radiobox);
868 XtGetValues (widget, al, 1);
871 xm_update_radiobox (instance, widget, val);
872 #ifdef LWLIB_MENUBARS_MOTIF
874 xm_update_menu (instance, widget, val, deep_p);
877 else if (class == xmTextWidgetClass)
879 xm_update_text (instance, widget, val);
881 else if (class == xmTextFieldWidgetClass)
883 xm_update_text_field (instance, widget, val);
885 else if (class == xmListWidgetClass)
887 xm_update_list (instance, widget, val);
889 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
890 else if (class == xmComboBoxWidgetClass)
892 xm_update_combo_box (instance, widget, val);
895 #ifdef LWLIB_SCROLLBARS_MOTIF
896 else if (class == xmScrollBarWidgetClass)
898 xm_update_scrollbar (instance, widget, val);
901 #ifdef LWLIB_WIDGETS_MOTIF
902 else if (class == xmScaleWidgetClass)
904 xm_update_progress (instance, widget, val);
907 /* Lastly update our global arg values. */
908 if (val->args && val->args->nargs)
909 XtSetValues (widget, val->args->args, val->args->nargs);
912 \f/* getting the value back */
914 xm_update_one_value (widget_instance* instance, Widget widget,
917 WidgetClass class = XtClass (widget);
918 widget_value *old_wv;
920 /* copy the call_data slot into the "return" widget_value */
921 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
922 if (!strcmp (val->name, old_wv->name))
924 val->call_data = old_wv->call_data;
928 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
931 XtSetArg (al [0], XmNset, &val->selected);
932 XtGetValues (widget, al, 1);
935 else if (class == xmTextWidgetClass)
939 val->value = XmTextGetString (widget);
942 else if (class == xmTextFieldWidgetClass)
946 val->value = XmTextFieldGetString (widget);
949 else if (class == xmRowColumnWidgetClass)
951 Boolean radiobox = 0;
954 XtSetArg (al [0], XmNradioBehavior, &radiobox);
955 XtGetValues (widget, al, 1);
960 CompositeWidget radio = (CompositeWidget)widget;
962 for (i = 0; i < radio->composite.num_children; i++)
965 Widget toggle = radio->composite.children [i];
968 XtSetArg (al [0], XmNset, &set);
969 XtGetValues (toggle, al, 1);
974 val->value = safe_strdup (XtName (toggle));
980 else if (class == xmListWidgetClass
981 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
982 || class == xmComboBoxWidgetClass
988 Widget list = widget;
989 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
990 if (class == xmComboBoxWidgetClass)
991 list = CB_List (widget);
993 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
997 for (cur = val->contents, i = 0; cur; cur = cur->next)
1001 cur->selected = False;
1003 for (j = 0; j < pos_cnt; j++)
1004 if (pos_list [j] == i)
1006 cur->selected = True;
1007 val->value = safe_strdup (cur->name);
1011 XtFree ((char *) pos_list);
1014 #ifdef LWLIB_SCROLLBARS_MOTIF
1015 else if (class == xmScrollBarWidgetClass)
1017 /* This function is not used by the scrollbar. */
1024 /* This function is for activating a button from a program. It's wrong because
1025 we pass a NULL argument in the call_data which is not Motif compatible.
1026 This is used from the XmNdefaultAction callback of the List widgets to
1027 have a double-click put down a dialog box like the button would do.
1028 I could not find a way to do that with accelerators.
1031 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
1033 Widget button = (Widget)closure;
1034 XtCallCallbacks (button, XmNactivateCallback, NULL);
1037 /* creation functions */
1039 #ifdef LWLIB_DIALOGS_MOTIF
1043 #if (XmVersion >= 1002)
1044 # define ARMANDACTIVATE_KLUDGE
1048 #ifdef ARMANDACTIVATE_KLUDGE
1049 /* We want typing Return at a dialog box to select the default button; but
1050 we're satisfied with having it select the leftmost button instead.
1052 In Motif 1.1.5 we could do this by putting this resource in the
1055 *dialog*button1.accelerators:#override\
1056 <KeyPress>Return: ArmAndActivate()\n\
1057 <KeyPress>KP_Enter: ArmAndActivate()\n\
1058 Ctrl<KeyPress>m: ArmAndActivate()\n
1060 but that doesn't work with 1.2.1 and I don't understand why. However,
1061 doing the equivalent C code does work, with the notable disadvantage that
1062 the user can't override it. So that's what we do until we figure out
1063 something better....
1065 static char button_trans[] = "\
1066 <KeyPress>Return: ArmAndActivate()\n\
1067 <KeyPress>KP_Enter: ArmAndActivate()\n\
1068 Ctrl<KeyPress>m: ArmAndActivate()\n";
1070 #endif /* ARMANDACTIVATE_KLUDGE */
1074 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1075 was a segv down in libXm somewhere if you used the middle button on a
1076 dialog box to begin a drag; when you released the button to make a drop
1077 things would lose if you were not over the button where you started the
1078 drag (canceling the operation). This was probably due to the fact that
1079 the dialog boxes were not set up to handle a drag but were trying to do
1080 so anyway for some reason.
1082 So we disable drag-and-drop in dialog boxes by turning off the binding for
1083 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1084 solution as it only works in default configurations, but...
1086 static char disable_dnd_trans[] = "<Btn2Down>: ";
1087 #endif /* DND_KLUDGE */
1091 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1092 const char* shell_title, const char* icon_name,
1093 Boolean text_input_slot, Boolean radio_box, Boolean list,
1094 int left_buttons, int right_buttons)
1100 Widget icon_separator;
1105 Widget children [16]; /* for the final XtManageChildren */
1107 Arg al[64]; /* Arg List */
1108 int ac; /* Arg Count */
1112 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1113 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1114 #else /* ! DND_KLUDGE */
1115 # define DO_DND_KLUDGE(widget)
1116 #endif /* ! DND_KLUDGE */
1121 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1122 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1123 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1124 result = XmCreateDialogShell (parent, "dialog", al, ac);
1126 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1127 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1128 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1129 form = XmCreateForm (result, (char *) shell_title, al, ac);
1134 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1135 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1136 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1141 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1142 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1143 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1144 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1145 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1146 XtSetArg(al[ac], XmNspacing, 13); ac++;
1147 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1148 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1149 XtSetArg(al[ac], XmNisAligned, True); ac++;
1150 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1151 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1152 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1153 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1154 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1155 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1156 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1157 row = XmCreateRowColumn (form, "row", al, ac);
1160 for (i = 0; i < left_buttons; i++)
1162 char button_name [16];
1163 sprintf (button_name, "button%d", i + 1);
1167 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1168 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1170 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1171 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1172 DO_DND_KLUDGE (children [n_children]);
1176 button = children [n_children];
1178 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1179 XtSetValues (row, al, ac);
1181 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1183 XtTranslations losers = XtParseTranslationTable (button_trans);
1184 XtOverrideTranslations (button, losers);
1185 XtFree ((char *) losers);
1187 #endif /* ARMANDACTIVATE_KLUDGE */
1193 /* invisible separator button */
1195 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1196 children [n_children] = XmCreateLabel (row, "separator_button",
1198 DO_DND_KLUDGE (children [n_children]);
1201 for (i = 0; i < right_buttons; i++)
1203 char button_name [16];
1204 sprintf (button_name, "button%d", left_buttons + i + 1);
1206 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1207 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1208 DO_DND_KLUDGE (children [n_children]);
1209 if (! button) button = children [n_children];
1213 XtManageChildren (children, n_children);
1216 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1217 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1218 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1219 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1220 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1221 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1222 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1223 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1224 separator = XmCreateSeparator (form, "", al, ac);
1227 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1228 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1229 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1230 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1231 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1232 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1233 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1234 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1235 DO_DND_KLUDGE (icon);
1238 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1239 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1240 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1241 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1242 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1243 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1244 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1245 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1246 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1247 icon_separator = XmCreateLabel (form, "", al, ac);
1248 DO_DND_KLUDGE (icon_separator);
1250 if (text_input_slot)
1253 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1254 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1255 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1256 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1257 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1258 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1259 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1260 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1261 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1262 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1263 value = XmCreateTextField (form, "value", al, ac);
1264 DO_DND_KLUDGE (value);
1270 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1271 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1272 XtSetArg(al[ac], XmNspacing, 13); ac++;
1273 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1274 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1275 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1276 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1277 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1278 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1279 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1280 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1281 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1282 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1283 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1286 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1287 children [i++] = radio_butt;
1288 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1289 children [i++] = radio_butt;
1290 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1291 children [i++] = radio_butt;
1292 XtManageChildren (children, i);
1297 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1298 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1299 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1300 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1301 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1302 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1303 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1304 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1305 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1306 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1307 value = XmCreateScrolledList (form, "list", al, ac);
1309 /* this is the easiest way I found to have the double click in the
1310 list activate the default button */
1311 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1313 /* else add nothing; it's a separator */
1316 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1317 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1318 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1319 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1320 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1321 XtSetArg(al[ac], XmNbottomWidget,
1322 text_input_slot || radio_box || list ? value : separator); ac++;
1323 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1324 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1325 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1326 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1327 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1328 message = XmCreateLabel (form, "message", al, ac);
1329 DO_DND_KLUDGE (message);
1332 XtManageChild (value);
1335 children [i] = row; i++;
1336 children [i] = separator; i++;
1337 if (text_input_slot || radio_box)
1339 children [i] = value; i++;
1341 children [i] = message; i++;
1342 children [i] = icon; i++;
1343 children [i] = icon_separator; i++;
1344 XtManageChildren (children, i);
1346 if (text_input_slot || list)
1348 XtInstallAccelerators (value, button);
1349 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1353 XtInstallAccelerators (form, button);
1354 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1356 /* else we don' need no STEENKIN' assellerators. */
1359 XtFree ((char *) dnd_override);
1361 #undef DO_DND_KLUDGE
1366 static destroyed_instance*
1367 find_matching_instance (widget_instance* instance)
1369 destroyed_instance* cur;
1370 destroyed_instance* prev;
1371 char* type = instance->info->type;
1372 char* name = instance->info->name;
1374 for (prev = NULL, cur = all_destroyed_instances;
1376 prev = cur, cur = cur->next)
1378 if (!strcmp (cur->name, name)
1379 && !strcmp (cur->type, type)
1380 && cur->parent == instance->parent
1381 && cur->pop_up_p == instance->pop_up_p)
1384 prev->next = cur->next;
1386 all_destroyed_instances = cur->next;
1389 /* do some cleanup */
1390 else if (!cur->widget)
1393 prev->next = cur->next;
1395 all_destroyed_instances = cur->next;
1396 free_destroyed_instance (cur);
1397 cur = prev ? prev : all_destroyed_instances;
1404 recenter_widget (Widget widget)
1406 Widget parent = XtParent (widget);
1407 Screen* screen = XtScreen (widget);
1408 Dimension screen_width = WidthOfScreen (screen);
1409 Dimension screen_height = HeightOfScreen (screen);
1410 Dimension parent_width = 0;
1411 Dimension parent_height = 0;
1412 Dimension child_width = 0;
1413 Dimension child_height = 0;
1418 XtSetArg (al [0], XtNwidth, &child_width);
1419 XtSetArg (al [1], XtNheight, &child_height);
1420 XtGetValues (widget, al, 2);
1422 XtSetArg (al [0], XtNwidth, &parent_width);
1423 XtSetArg (al [1], XtNheight, &parent_height);
1424 XtGetValues (parent, al, 2);
1426 x = (Position) ((parent_width - child_width) / 2);
1427 y = (Position) ((parent_height - child_height) / 2);
1429 XtTranslateCoords (parent, x, y, &x, &y);
1431 if ((Dimension) (x + child_width) > screen_width)
1432 x = screen_width - child_width;
1436 if ((Dimension) (y + child_height) > screen_height)
1437 y = screen_height - child_height;
1441 XtSetArg (al [0], XtNx, x);
1442 XtSetArg (al [1], XtNy, y);
1443 XtSetValues (widget, al, 2);
1447 recycle_instance (destroyed_instance* instance)
1449 Widget widget = instance->widget;
1451 /* widget is NULL if the parent was destroyed. */
1457 /* Remove the destroy callback as the instance is not in the list
1459 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1460 mark_dead_instance_destroyed,
1461 (XtPointer)instance);
1463 /* Give the focus to the initial item */
1464 focus = XtNameToWidget (widget, "*value");
1466 focus = XtNameToWidget (widget, "*button1");
1468 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1470 /* shrink the separator label back to their original size */
1471 separator = XtNameToWidget (widget, "*separator_button");
1475 XtSetArg (al [0], XtNwidth, 5);
1476 XtSetArg (al [1], XtNheight, 5);
1477 XtSetValues (separator, al, 2);
1480 /* Center the dialog in its parent */
1481 recenter_widget (widget);
1483 free_destroyed_instance (instance);
1488 xm_create_dialog (widget_instance* instance)
1490 char* name = instance->info->type;
1491 Widget parent = instance->parent;
1493 Boolean pop_up_p = instance->pop_up_p;
1494 const char* shell_name = 0;
1495 const char* icon_name = 0;
1496 Boolean text_input_slot = False;
1497 Boolean radio_box = False;
1498 Boolean list = False;
1500 int left_buttons = 0;
1501 int right_buttons = 1;
1502 destroyed_instance* dead_one;
1504 /* try to find a widget to recycle */
1505 dead_one = find_matching_instance (instance);
1508 Widget recycled_widget = recycle_instance (dead_one);
1509 if (recycled_widget)
1510 return recycled_widget;
1515 icon_name = "dbox-error";
1516 shell_name = "Error";
1520 icon_name = "dbox-info";
1521 shell_name = "Information";
1526 icon_name = "dbox-question";
1527 shell_name = "Prompt";
1531 text_input_slot = True;
1532 icon_name = "dbox-question";
1533 shell_name = "Prompt";
1537 icon_name = "dbox-question";
1538 shell_name = "Question";
1542 total_buttons = name [1] - '0';
1544 if (name [3] == 'T' || name [3] == 't')
1546 text_input_slot = False;
1550 right_buttons = name [4] - '0';
1552 left_buttons = total_buttons - right_buttons;
1554 widget = make_dialog (name, parent, pop_up_p,
1555 shell_name, icon_name, text_input_slot, radio_box,
1556 list, left_buttons, right_buttons);
1558 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1559 (XtPointer) instance);
1563 #endif /* LWLIB_DIALOGS_MOTIF */
1565 #ifdef LWLIB_MENUBARS_MOTIF
1567 make_menubar (widget_instance* instance)
1572 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1573 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1575 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1579 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1581 Widget menu = (Widget) closure;
1582 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1586 make_popup_menu (widget_instance* instance)
1588 Widget parent = instance->parent;
1589 Window parent_window = parent->core.window;
1592 /* sets the parent window to 0 to fool Motif into not generating a grab */
1593 parent->core.window = 0;
1594 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1595 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1597 parent->core.window = parent_window;
1600 #endif /* LWLIB_MENUBARS_MOTIF */
1602 #ifdef LWLIB_SCROLLBARS_MOTIF
1604 make_scrollbar (widget_instance *instance, int vertical)
1608 static XtCallbackRec callbacks[2] =
1609 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1611 callbacks[0].closure = (XtPointer) instance;
1613 XtSetArg (al[ac], XmNminimum, 1); ac++;
1614 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1615 XtSetArg (al[ac], XmNincrement, 1); ac++;
1616 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1617 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1618 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1620 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1621 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1622 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1623 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1624 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1625 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1626 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1627 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1629 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1633 make_vertical_scrollbar (widget_instance *instance)
1635 return make_scrollbar (instance, 1);
1639 make_horizontal_scrollbar (widget_instance *instance)
1641 return make_scrollbar (instance, 0);
1644 #endif /* LWLIB_SCROLLBARS_MOTIF */
1646 #ifdef LWLIB_WIDGETS_MOTIF
1649 xm_create_button (widget_instance *instance)
1654 widget_value* val = instance->info->val;
1656 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1657 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1658 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1659 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1660 /* The highlight doesn't appear to be dynamically set which makes it
1661 look ugly. I think this may be a LessTif bug but for now we just
1663 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1665 /* add any args the user supplied for creation time */
1666 lw_add_value_args_to_args (val, al, &ac);
1668 if (!val->call_data)
1669 button = XmCreateLabel (instance->parent, val->name, al, ac);
1671 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1673 XtSetArg (al [ac], XmNset, val->selected); ac++;
1674 XtSetArg (al [ac], XmNindicatorType,
1675 (val->type == TOGGLE_TYPE ?
1676 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1677 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1678 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1679 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1680 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1681 (XtPointer)instance);
1685 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1686 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1687 (XtPointer)instance);
1690 XtManageChild (button);
1696 xm_create_progress (widget_instance *instance)
1700 Dimension height = 0;
1701 Dimension width = 0;
1703 widget_value* val = instance->info->val;
1704 if (!val->call_data)
1706 XtSetArg (al [ac], XmNeditable, False); ac++;
1710 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
1712 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1713 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1714 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1715 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1716 /* The highlight doesn't appear to be dynamically set which makes it
1717 look ugly. I think this may be a LessTif bug but for now we just
1719 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1721 height = (Dimension)lw_get_value_arg (val, XtNheight);
1722 width = (Dimension)lw_get_value_arg (val, XtNwidth);
1725 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
1729 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
1732 /* add any args the user supplied for creation time */
1733 lw_add_value_args_to_args (val, al, &ac);
1735 scale = XmCreateScale (instance->parent, val->name, al, ac);
1737 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1738 (XtPointer)instance);
1740 XtManageChild (scale);
1746 xm_create_text_field (widget_instance *instance)
1751 widget_value* val = instance->info->val;
1753 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1754 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1755 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1756 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1757 /* The highlight doesn't appear to be dynamically set which makes it
1758 look ugly. I think this may be a LessTif bug but for now we just
1760 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1762 /* add any args the user supplied for creation time */
1763 lw_add_value_args_to_args (val, al, &ac);
1765 text = XmCreateTextField (instance->parent, val->name, al, ac);
1767 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1768 (XtPointer)instance);
1770 XtManageChild (text);
1776 xm_create_label_field (widget_instance *instance)
1778 return xm_create_label (instance->parent, instance->info->val);
1782 xm_create_label (Widget parent, widget_value* val)
1788 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1789 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1790 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1791 /* The highlight doesn't appear to be dynamically set which makes it
1792 look ugly. I think this may be a LessTif bug but for now we just
1794 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1796 /* add any args the user supplied for creation time */
1797 lw_add_value_args_to_args (val, al, &ac);
1799 label = XmCreateLabel (parent, val->name, al, ac);
1801 XtManageChild (label);
1803 /* Do it again for arguments that have no effect until the widget is realized. */
1805 lw_add_value_args_to_args (val, al, &ac);
1806 XtSetValues (label, al, ac);
1813 xm_create_combo_box (widget_instance *instance)
1818 widget_value* val = instance->info->val;
1820 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1821 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1822 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1823 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1824 /* The highlight doesn't appear to be dynamically set which makes it
1825 look ugly. I think this may be a LessTif bug but for now we just
1827 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1829 /* add any args the user supplied for creation time */
1830 lw_add_value_args_to_args (val, al, &ac);
1832 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1834 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1835 (XtPointer)instance);
1837 XtManageChild (combo);
1842 #endif /* LWLIB_WIDGETS_MOTIF */
1845 /* Table of functions to create widgets */
1847 const widget_creation_entry
1848 xm_creation_table [] =
1850 #ifdef LWLIB_MENUBARS_MOTIF
1851 {"menubar", make_menubar},
1852 {"popup", make_popup_menu},
1854 #ifdef LWLIB_SCROLLBARS_MOTIF
1855 {"vertical-scrollbar", make_vertical_scrollbar},
1856 {"horizontal-scrollbar", make_horizontal_scrollbar},
1858 #ifdef LWLIB_WIDGETS_MOTIF
1859 {"button", xm_create_button},
1860 {"progress", xm_create_progress},
1861 {"text-field", xm_create_text_field},
1862 {"label", xm_create_label_field},
1864 {"combo-box", xm_create_combo_box},
1870 \f/* Destruction of instances */
1872 xm_destroy_instance (widget_instance* instance)
1874 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1875 /* It appears that this is used only for dialog boxes. */
1876 Widget widget = instance->widget;
1877 /* recycle the dialog boxes */
1878 /* Disable the recycling until we can find a way to have the dialog box
1879 get reasonable layout after we modify its contents. */
1881 && XtClass (widget) == xmDialogShellWidgetClass)
1883 destroyed_instance* dead_instance =
1884 make_destroyed_instance (instance->info->name,
1885 instance->info->type,
1888 instance->pop_up_p);
1889 dead_instance->next = all_destroyed_instances;
1890 all_destroyed_instances = dead_instance;
1891 XtUnmanageChild (first_child (instance->widget));
1892 XFlush (XtDisplay (instance->widget));
1893 XtAddCallback (instance->parent, XtNdestroyCallback,
1894 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1898 /* This might not be necessary now that the nosel is attached to
1899 popdown instead of destroy, but it can't hurt. */
1900 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1901 xm_nosel_callback, (XtPointer)instance);
1903 XtDestroyWidget (instance->widget);
1905 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1908 \f/* popup utility */
1909 #ifdef LWLIB_MENUBARS_MOTIF
1912 xm_popup_menu (Widget widget, XEvent *event)
1914 if (event->type == ButtonPress || event->type == ButtonRelease)
1916 /* This is so totally ridiculous: there's NO WAY to tell Motif
1917 that *any* button can select a menu item. Only one button
1918 can have that honor.
1921 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1922 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1923 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1924 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1925 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1929 XtSetArg (al [0], XmNmenuPost, trans);
1930 XtSetValues (widget, al, 1);
1932 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1934 XtManageChild (widget);
1939 #ifdef LWLIB_DIALOGS_MOTIF
1942 set_min_dialog_size (Widget w)
1948 XtSetArg (al [0], XmNwidth, &width);
1949 XtSetArg (al [1], XmNheight, &height);
1950 XtGetValues (w, al, 2);
1952 XtSetArg (al [0], XmNminWidth, width);
1953 XtSetArg (al [1], XmNminHeight, height);
1954 XtSetValues (w, al, 2);
1960 xm_pop_instance (widget_instance* instance, Boolean up)
1962 Widget widget = instance->widget;
1964 #ifdef LWLIB_DIALOGS_MOTIF
1965 if (XtClass (widget) == xmDialogShellWidgetClass)
1967 Widget widget_to_manage = first_child (widget);
1970 XtManageChild (widget_to_manage);
1971 set_min_dialog_size (widget);
1972 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1975 XtUnmanageChild (widget_to_manage);
1981 XtManageChild (widget);
1983 XtUnmanageChild (widget);
1988 /* motif callback */
1990 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1993 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1995 XtPointer user_data;
1996 widget_instance* instance = (widget_instance*)closure;
1997 Widget instance_widget;
2003 if (widget->core.being_destroyed)
2006 instance_widget = instance->widget;
2007 if (!instance_widget)
2010 id = instance->info->id;
2012 XtSetArg(al [0], XmNuserData, &user_data);
2013 XtGetValues (widget, al, 1);
2017 if (instance->info->pre_activate_cb)
2018 instance->info->pre_activate_cb (widget, id, user_data);
2021 if (instance->info->selection_cb)
2022 instance->info->selection_cb (widget, id, user_data);
2025 if (instance->info->selection_cb)
2026 instance->info->selection_cb (widget, id, (XtPointer) -1);
2029 if (instance->info->post_activate_cb)
2030 instance->info->post_activate_cb (widget, id, user_data);
2037 /* Like lw_internal_update_other_instances except that it does not do
2038 anything if its shell parent is not managed. This is to protect
2039 lw_internal_update_other_instances to dereference freed memory
2040 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2043 xm_internal_update_other_instances (Widget widget, XtPointer closure,
2044 XtPointer call_data)
2047 for (parent = widget; parent; parent = XtParent (parent))
2048 if (XtIsShell (parent))
2050 else if (!XtIsManaged (parent))
2052 lw_internal_update_other_instances (widget, closure, call_data);
2056 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
2058 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2059 /* We want the selected status to change only when we decide it
2060 should change. Yuck but correct. */
2061 if (XtClass (widget) == xmToggleButtonWidgetClass
2062 || XtClass (widget) == xmToggleButtonGadgetClass)
2067 XtSetArg (al [0], XmNset, &check);
2068 XtGetValues (widget, al, 1);
2070 XtSetArg (al [0], XmNset, !check);
2071 XtSetValues (widget, al, 1);
2074 lw_internal_update_other_instances (widget, closure, call_data);
2075 do_call (widget, closure, selection);
2079 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2081 do_call (widget, closure, post_activate);
2084 #ifdef LWLIB_MENUBARS_MOTIF
2087 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2092 /* new behavior for incremental menu construction */
2097 do_call (widget, closure, pre_activate);
2100 #endif /* LWLIB_MENUBARS_MOTIF */
2102 #ifdef LWLIB_SCROLLBARS_MOTIF
2104 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2106 widget_instance *instance = (widget_instance *) closure;
2108 XmScrollBarCallbackStruct *data =
2109 (XmScrollBarCallbackStruct *) call_data;
2110 scroll_event event_data;
2111 scrollbar_values *val =
2112 (scrollbar_values *) instance->info->val->scrollbar_data;
2115 if (!instance || widget->core.being_destroyed)
2118 id = instance->info->id;
2120 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2121 event_data.slider_value =
2122 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2124 if (event_data.slider_value > (val->maximum - val->slider_size))
2125 event_data.slider_value = val->maximum - val->slider_size;
2126 else if (event_data.slider_value < 1)
2127 event_data.slider_value = 1;
2131 switch (data->event->xany.type)
2135 event_data.time = data->event->xkey.time;
2139 event_data.time = data->event->xbutton.time;
2142 event_data.time = data->event->xmotion.time;
2146 event_data.time = data->event->xcrossing.time;
2149 event_data.time = 0;
2154 event_data.time = 0;
2156 switch (data->reason)
2158 case XmCR_DECREMENT:
2159 event_data.action = SCROLLBAR_LINE_UP;
2161 case XmCR_INCREMENT:
2162 event_data.action = SCROLLBAR_LINE_DOWN;
2164 case XmCR_PAGE_DECREMENT:
2165 event_data.action = SCROLLBAR_PAGE_UP;
2167 case XmCR_PAGE_INCREMENT:
2168 event_data.action = SCROLLBAR_PAGE_DOWN;
2171 event_data.action = SCROLLBAR_TOP;
2173 case XmCR_TO_BOTTOM:
2174 event_data.action = SCROLLBAR_BOTTOM;
2177 event_data.action = SCROLLBAR_DRAG;
2179 case XmCR_VALUE_CHANGED:
2180 event_data.action = SCROLLBAR_CHANGE;
2183 event_data.action = SCROLLBAR_CHANGE;
2187 if (instance->info->pre_activate_cb)
2188 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2190 #endif /* LWLIB_SCROLLBARS_MOTIF */
2192 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2194 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2195 XtPointer call_data)
2197 destroyed_instance* instance = (destroyed_instance*)closure;
2198 instance->widget = NULL;
2202 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2204 /* This callback is only called when a dialog box is dismissed with the wm's
2205 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2206 in that case, not just unmapped, so that it releases its keyboard grabs.
2207 But there are problems with running our callbacks while the widget is in
2208 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2209 instead of XmDESTROY and then destroy it ourself after having run the
2212 do_call (widget, closure, no_selection);
2213 XtDestroyWidget (widget);
2218 /* set the keyboard focus */
2220 xm_set_keyboard_focus (Widget parent, Widget w)
2222 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2223 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2224 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2225 Presumably the problem was elsewhere, and is now gone...