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);
348 xm_update_progress (widget_instance* instance, Widget scale,
353 Dimension height = 0;
357 XtSetArg (al [ac], XmNeditable, False); ac++;
361 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
363 height = (Dimension)lw_get_value_arg (val, XtNheight);
364 width = (Dimension)lw_get_value_arg (val, XtNwidth);
367 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
371 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
374 XtSetValues (scale, al, 1);
377 #ifdef LWLIB_MENUBARS_MOTIF
380 xm_update_cascadebutton (widget_instance* instance, Widget widget,
383 /* Should also rebuild the menu by calling ...update_menu... */
385 && val->type == CASCADE_TYPE
387 && val->contents->type == INCREMENTAL_TYPE)
389 /* okay, we're now doing a lisp callback to incrementally generate
391 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
392 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
394 XtCallCallbacks ((Widget)widget,
395 XmNcascadingCallback,
396 (XtPointer)val->contents);
399 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
400 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
405 #endif /* LWLIB_MENUBARS_MOTIF */
407 \f/* update toggle and radiobox */
409 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
412 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
413 XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
415 XtSetArg (al [0], XmNset, val->selected);
416 XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
417 XtSetValues (widget, al, 1);
421 xm_update_radiobox (widget_instance* instance, Widget widget,
427 /* update the callback */
428 XtRemoveAllCallbacks (widget, XmNentryCallback);
429 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
431 /* first update all the toggles */
432 /* Energize kernel interface is currently bad. It sets the selected widget
433 with the selected flag but returns it by its name. So we currently
434 have to support both setting the selection with the selected slot
435 of val contents and setting it with the "value" slot of val. The latter
436 has a higher priority. This to be removed when the kernel is fixed. */
437 for (cur = val->contents; cur; cur = cur->next)
439 toggle = XtNameToWidget (widget, cur->value);
443 XtSetArg (al [0], XmNsensitive, cur->enabled);
444 XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
445 XtSetValues (toggle, al, 2);
449 /* The selected was specified by the value slot */
452 toggle = XtNameToWidget (widget, val->value);
456 XtSetArg (al [0], XmNset, True);
457 XtSetValues (toggle, al, 1);
462 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
463 /* update of combo box */
465 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
469 XtRemoveAllCallbacks (widget, XmNselectionCallback);
470 XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
472 for (cur = val->contents, i = 0; cur; cur = cur->next)
475 XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
477 XmListAddItem (CB_List (widget), xmstr, 0);
479 XmListSelectPos (CB_List (widget), i, False);
480 XmStringFree (xmstr);
485 #ifdef LWLIB_MENUBARS_MOTIF
487 \f/* update a popup menu, pulldown menu or a menubar */
489 make_menu_in_widget (widget_instance* instance, Widget widget,
492 Widget* children = 0;
500 Boolean menubar_p = False;
502 /* Allocate the children array */
503 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
504 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
506 /* tricky way to know if this RowColumn is a menubar or a pulldown... */
507 XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
508 XtGetValues (widget, al, 1);
510 /* add the unmap callback for popups and pulldowns */
511 /*** this sounds bogus ***/
512 /* probably because it is -- cet */
515 XtAddCallback (XtParent (widget), XmNpopdownCallback,
516 xm_pop_down_callback, (XtPointer)instance);
520 for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
524 XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++;
525 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
526 XtSetArg (al [ac], XmNuserData, cur->call_data); ac++;
531 /* A pushright marker which is not needed for the real Motif
538 /* #### - xlwmenu.h supports several types that motif does
539 not. Also, motif supports pixmaps w/ type NO_LINE and
540 lwlib provides no way to access that functionality. --Stig */
541 XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
543 button = XmCreateSeparator (widget, "separator", al, ac);
546 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
547 make_menu_in_widget (instance, menu, cur->contents);
548 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
549 button = XmCreateCascadeButton (widget, cur->name, al, ac);
551 xm_safe_update_label (instance, button, cur);
553 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
554 (XtPointer)instance);
558 button = XmCreateCascadeButton (widget, cur->name, al, ac);
559 else if (!cur->call_data)
560 button = XmCreateLabel (widget, cur->name, al, ac);
561 else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
563 XtSetArg (al [ac], XmNindicatorType,
564 (cur->type == TOGGLE_TYPE ?
565 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
566 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
567 button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
570 button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
572 xm_safe_update_label (instance, button, cur);
574 /* don't add a callback to a simple label */
575 if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
576 xm_update_toggle (instance, button, cur);
577 else if (cur->call_data)
578 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
579 (XtPointer)instance);
580 } /* switch (cur->type) */
583 children [num_children++] = button;
586 /* Last entry is the help button. This used be done after managing
587 the buttons. The comment claimed that it had to be done this way
588 otherwise the menubar ended up only 4 pixels high. That must
589 have been in the Old World. In the New World it stays the proper
590 height if you don't manage them until after you set this and as a
591 bonus the Help menu ends up where it is supposed to. */
595 XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
596 XtSetValues (widget, al, ac);
600 XtManageChildren (children, num_children);
602 XtFree ((char *) children);
606 update_one_menu_entry (widget_instance* instance, Widget widget,
607 widget_value* val, Boolean deep_p)
612 widget_value* contents;
614 if (val->change == NO_CHANGE)
617 /* update the sensitivity and userdata */
618 /* Common to all widget types */
619 XtSetArg (al [0], XmNsensitive, val->enabled);
620 XtSetArg (al [1], XmNuserData, val->call_data);
621 XtSetValues (widget, al, 2);
623 /* update the menu button as a label. */
624 if (val->change >= VISIBLE_CHANGE)
626 xm_safe_update_label (instance, widget, val);
628 if (XtClass (widget) == xmToggleButtonWidgetClass
629 || XtClass (widget) == xmToggleButtonGadgetClass)
631 xm_update_toggle (instance, widget, val);
636 /* update the pulldown/pullaside as needed */
638 XtSetArg (al [0], XmNsubMenuId, &menu);
639 XtGetValues (widget, al, 1);
641 contents = val->contents;
647 menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
648 make_menu_in_widget (instance, menu, contents);
650 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
651 XtSetValues (widget, al, ac);
657 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
658 XtSetValues (widget, al, ac);
659 XtDestroyWidget (menu);
661 else if (deep_p && contents->change != NO_CHANGE)
662 xm_update_menu (instance, menu, val, 1);
666 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
669 /* Widget is a RowColumn widget whose contents have to be updated
670 * to reflect the list of items in val->contents */
671 if (val->contents->change == STRUCTURAL_CHANGE)
673 destroy_all_children (widget);
674 make_menu_in_widget (instance, widget, val->contents);
678 /* Update all the buttons of the RowColumn in order. */
680 unsigned int num_children;
682 widget_value *cur = 0;
684 children = XtCompositeChildren (widget, &num_children);
687 for (i = 0, cur = val->contents; i < num_children; i++)
691 /* skip if this is a pushright marker or a separator */
692 if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
696 /* #### - this could puke if you have a separator as the
697 last item on a pullright menu. */
705 if (children [i]->core.being_destroyed
706 || strcmp (XtName (children [i]), cur->name))
708 update_one_menu_entry (instance, children [i], cur, deep_p);
711 XtFree ((char *) children);
718 #endif /* LWLIB_MENUBARS_MOTIF */
721 /* update text widgets */
724 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
726 XmTextSetString (widget, val->value ? val->value : (char *) "");
727 XtRemoveAllCallbacks (widget, XmNactivateCallback);
728 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
729 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
730 XtAddCallback (widget, XmNvalueChangedCallback,
731 xm_internal_update_other_instances, instance);
735 xm_update_text_field (widget_instance* instance, Widget widget,
738 XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
739 XtRemoveAllCallbacks (widget, XmNactivateCallback);
740 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
741 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
742 XtAddCallback (widget, XmNvalueChangedCallback,
743 xm_internal_update_other_instances, instance);
747 #ifdef LWLIB_SCROLLBARS_MOTIF
750 * If this function looks like it does a lot more work than it needs to,
751 * you're right. Blame the Motif scrollbar for not being smart about
752 * updating its appearance.
755 xm_update_scrollbar (widget_instance *instance, Widget widget,
758 if (val->scrollbar_data)
760 scrollbar_values *data = val->scrollbar_data;
761 int widget_sliderSize, widget_val;
762 int new_sliderSize, new_value;
764 double h_water, l_water;
767 /* First size and position the scrollbar widget. */
768 XtSetArg (al [0], XtNx, data->scrollbar_x);
769 XtSetArg (al [1], XtNy, data->scrollbar_y);
770 XtSetArg (al [2], XtNwidth, data->scrollbar_width);
771 XtSetArg (al [3], XtNheight, data->scrollbar_height);
772 XtSetValues (widget, al, 4);
774 /* Now size the scrollbar's slider. */
775 XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
776 XtSetArg (al [1], XmNvalue, &widget_val);
777 XtGetValues (widget, al, 2);
779 percent = (double) data->slider_size /
780 (double) (data->maximum - data->minimum);
781 new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
783 percent = (double) (data->slider_position - data->minimum) /
784 (double) (data->maximum - data->minimum);
785 new_value = (int) ((double) (INT_MAX - 1) * percent);
787 if (new_sliderSize > (INT_MAX - 1))
788 new_sliderSize = INT_MAX - 1;
789 else if (new_sliderSize < 1)
792 if (new_value > (INT_MAX - new_sliderSize))
793 new_value = INT_MAX - new_sliderSize;
794 else if (new_value < 1)
799 if (new_sliderSize != widget_sliderSize || new_value != widget_val)
801 int force = ((INT_MAX - widget_sliderSize - widget_val)
803 : (INT_MAX - new_sliderSize - new_value));
806 || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
807 || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
808 || (double)new_value < (l_water * (double)widget_val)
809 || (double)new_value > (h_water * (double)widget_val))
811 XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
818 #endif /* LWLIB_SCROLLBARS_MOTIF */
821 /* update a motif widget */
824 xm_update_one_widget (widget_instance* instance, Widget widget,
825 widget_value* val, Boolean deep_p)
831 /* Mark as not edited */
834 /* Common to all widget types */
835 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
836 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
837 XtSetValues (widget, al, ac);
839 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
840 /* Common to all label like widgets */
841 xm_safe_update_label (instance, widget, val);
843 class = XtClass (widget);
844 /* Class specific things */
845 if (class == xmPushButtonWidgetClass ||
846 class == xmArrowButtonWidgetClass)
848 xm_update_pushbutton (instance, widget, val);
850 #ifdef LWLIB_MENUBARS_MOTIF
851 else if (class == xmCascadeButtonWidgetClass)
853 xm_update_cascadebutton (instance, widget, val);
856 else if (class == xmToggleButtonWidgetClass
857 || class == xmToggleButtonGadgetClass)
859 xm_update_toggle (instance, widget, val);
861 else if (class == xmRowColumnWidgetClass)
863 Boolean radiobox = 0;
865 XtSetArg (al [0], XmNradioBehavior, &radiobox);
866 XtGetValues (widget, al, 1);
869 xm_update_radiobox (instance, widget, val);
870 #ifdef LWLIB_MENUBARS_MOTIF
872 xm_update_menu (instance, widget, val, deep_p);
875 else if (class == xmTextWidgetClass)
877 xm_update_text (instance, widget, val);
879 else if (class == xmTextFieldWidgetClass)
881 xm_update_text_field (instance, widget, val);
883 else if (class == xmListWidgetClass)
885 xm_update_list (instance, widget, val);
887 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
888 else if (class == xmComboBoxWidgetClass)
890 xm_update_combo_box (instance, widget, val);
893 #ifdef LWLIB_SCROLLBARS_MOTIF
894 else if (class == xmScrollBarWidgetClass)
896 xm_update_scrollbar (instance, widget, val);
899 #ifdef LWLIB_WIDGETS_MOTIF
900 else if (class == xmScaleWidgetClass)
902 xm_update_progress (instance, widget, val);
905 /* Lastly update our global arg values. */
906 if (val->args && val->args->nargs)
907 XtSetValues (widget, val->args->args, val->args->nargs);
910 \f/* getting the value back */
912 xm_update_one_value (widget_instance* instance, Widget widget,
915 WidgetClass class = XtClass (widget);
916 widget_value *old_wv;
918 /* copy the call_data slot into the "return" widget_value */
919 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
920 if (!strcmp (val->name, old_wv->name))
922 val->call_data = old_wv->call_data;
926 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
929 XtSetArg (al [0], XmNset, &val->selected);
930 XtGetValues (widget, al, 1);
933 else if (class == xmTextWidgetClass)
937 val->value = XmTextGetString (widget);
940 else if (class == xmTextFieldWidgetClass)
944 val->value = XmTextFieldGetString (widget);
947 else if (class == xmRowColumnWidgetClass)
949 Boolean radiobox = 0;
952 XtSetArg (al [0], XmNradioBehavior, &radiobox);
953 XtGetValues (widget, al, 1);
958 CompositeWidget radio = (CompositeWidget)widget;
960 for (i = 0; i < radio->composite.num_children; i++)
963 Widget toggle = radio->composite.children [i];
966 XtSetArg (al [0], XmNset, &set);
967 XtGetValues (toggle, al, 1);
972 val->value = safe_strdup (XtName (toggle));
978 else if (class == xmListWidgetClass
979 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
980 || class == xmComboBoxWidgetClass
986 Widget list = widget;
987 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
988 if (class == xmComboBoxWidgetClass)
989 list = CB_List (widget);
991 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
995 for (cur = val->contents, i = 0; cur; cur = cur->next)
999 cur->selected = False;
1001 for (j = 0; j < pos_cnt; j++)
1002 if (pos_list [j] == i)
1004 cur->selected = True;
1005 val->value = safe_strdup (cur->name);
1009 XtFree ((char *) pos_list);
1012 #ifdef LWLIB_SCROLLBARS_MOTIF
1013 else if (class == xmScrollBarWidgetClass)
1015 /* This function is not used by the scrollbar. */
1022 /* This function is for activating a button from a program. It's wrong because
1023 we pass a NULL argument in the call_data which is not Motif compatible.
1024 This is used from the XmNdefaultAction callback of the List widgets to
1025 have a double-click put down a dialog box like the button would do.
1026 I could not find a way to do that with accelerators.
1029 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
1031 Widget button = (Widget)closure;
1032 XtCallCallbacks (button, XmNactivateCallback, NULL);
1035 /* creation functions */
1037 #ifdef LWLIB_DIALOGS_MOTIF
1041 #if (XmVersion >= 1002)
1042 # define ARMANDACTIVATE_KLUDGE
1046 #ifdef ARMANDACTIVATE_KLUDGE
1047 /* We want typing Return at a dialog box to select the default button; but
1048 we're satisfied with having it select the leftmost button instead.
1050 In Motif 1.1.5 we could do this by putting this resource in the
1053 *dialog*button1.accelerators:#override\
1054 <KeyPress>Return: ArmAndActivate()\n\
1055 <KeyPress>KP_Enter: ArmAndActivate()\n\
1056 Ctrl<KeyPress>m: ArmAndActivate()\n
1058 but that doesn't work with 1.2.1 and I don't understand why. However,
1059 doing the equivalent C code does work, with the notable disadvantage that
1060 the user can't override it. So that's what we do until we figure out
1061 something better....
1063 static char button_trans[] = "\
1064 <KeyPress>Return: ArmAndActivate()\n\
1065 <KeyPress>KP_Enter: ArmAndActivate()\n\
1066 Ctrl<KeyPress>m: ArmAndActivate()\n";
1068 #endif /* ARMANDACTIVATE_KLUDGE */
1072 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1073 was a segv down in libXm somewhere if you used the middle button on a
1074 dialog box to begin a drag; when you released the button to make a drop
1075 things would lose if you were not over the button where you started the
1076 drag (canceling the operation). This was probably due to the fact that
1077 the dialog boxes were not set up to handle a drag but were trying to do
1078 so anyway for some reason.
1080 So we disable drag-and-drop in dialog boxes by turning off the binding for
1081 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1082 solution as it only works in default configurations, but...
1084 static char disable_dnd_trans[] = "<Btn2Down>: ";
1085 #endif /* DND_KLUDGE */
1089 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1090 const char* shell_title, const char* icon_name,
1091 Boolean text_input_slot, Boolean radio_box, Boolean list,
1092 int left_buttons, int right_buttons)
1098 Widget icon_separator;
1103 Widget children [16]; /* for the final XtManageChildren */
1105 Arg al[64]; /* Arg List */
1106 int ac; /* Arg Count */
1110 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1111 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1112 #else /* ! DND_KLUDGE */
1113 # define DO_DND_KLUDGE(widget)
1114 #endif /* ! DND_KLUDGE */
1119 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1120 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1121 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1122 result = XmCreateDialogShell (parent, "dialog", al, ac);
1124 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1125 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1126 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1127 form = XmCreateForm (result, (char *) shell_title, al, ac);
1132 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1133 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1134 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1139 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1140 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1141 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1142 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1143 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1144 XtSetArg(al[ac], XmNspacing, 13); ac++;
1145 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1146 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1147 XtSetArg(al[ac], XmNisAligned, True); ac++;
1148 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1149 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1150 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1151 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1152 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1153 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1154 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1155 row = XmCreateRowColumn (form, "row", al, ac);
1158 for (i = 0; i < left_buttons; i++)
1160 char button_name [16];
1161 sprintf (button_name, "button%d", i + 1);
1165 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1166 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1168 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1169 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1170 DO_DND_KLUDGE (children [n_children]);
1174 button = children [n_children];
1176 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1177 XtSetValues (row, al, ac);
1179 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1181 XtTranslations losers = XtParseTranslationTable (button_trans);
1182 XtOverrideTranslations (button, losers);
1183 XtFree ((char *) losers);
1185 #endif /* ARMANDACTIVATE_KLUDGE */
1191 /* invisible separator button */
1193 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1194 children [n_children] = XmCreateLabel (row, "separator_button",
1196 DO_DND_KLUDGE (children [n_children]);
1199 for (i = 0; i < right_buttons; i++)
1201 char button_name [16];
1202 sprintf (button_name, "button%d", left_buttons + i + 1);
1204 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1205 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1206 DO_DND_KLUDGE (children [n_children]);
1207 if (! button) button = children [n_children];
1211 XtManageChildren (children, n_children);
1214 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1215 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1216 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1217 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1218 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1219 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1220 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1221 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1222 separator = XmCreateSeparator (form, "", al, ac);
1225 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1226 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1227 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1228 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1229 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1230 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1231 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1232 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1233 DO_DND_KLUDGE (icon);
1236 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1237 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1238 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1239 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1240 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1241 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1242 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1243 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1244 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1245 icon_separator = XmCreateLabel (form, "", al, ac);
1246 DO_DND_KLUDGE (icon_separator);
1248 if (text_input_slot)
1251 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1252 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1253 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1254 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1255 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1256 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1257 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1258 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1259 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1260 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1261 value = XmCreateTextField (form, "value", al, ac);
1262 DO_DND_KLUDGE (value);
1268 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1269 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1270 XtSetArg(al[ac], XmNspacing, 13); ac++;
1271 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1272 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1273 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1274 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1275 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1276 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1277 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1278 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1279 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1280 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1281 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1284 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1285 children [i++] = radio_butt;
1286 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1287 children [i++] = radio_butt;
1288 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1289 children [i++] = radio_butt;
1290 XtManageChildren (children, i);
1295 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1296 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1297 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1298 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1299 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1300 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1301 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1302 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1303 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1304 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1305 value = XmCreateScrolledList (form, "list", al, ac);
1307 /* this is the easiest way I found to have the double click in the
1308 list activate the default button */
1309 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1311 /* else add nothing; it's a separator */
1314 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1315 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1316 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1317 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1318 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1319 XtSetArg(al[ac], XmNbottomWidget,
1320 text_input_slot || radio_box || list ? value : separator); ac++;
1321 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1322 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1323 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1324 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1325 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1326 message = XmCreateLabel (form, "message", al, ac);
1327 DO_DND_KLUDGE (message);
1330 XtManageChild (value);
1333 children [i] = row; i++;
1334 children [i] = separator; i++;
1335 if (text_input_slot || radio_box)
1337 children [i] = value; i++;
1339 children [i] = message; i++;
1340 children [i] = icon; i++;
1341 children [i] = icon_separator; i++;
1342 XtManageChildren (children, i);
1344 if (text_input_slot || list)
1346 XtInstallAccelerators (value, button);
1347 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1351 XtInstallAccelerators (form, button);
1352 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1354 /* else we don' need no STEENKIN' assellerators. */
1357 XtFree ((char *) dnd_override);
1359 #undef DO_DND_KLUDGE
1364 static destroyed_instance*
1365 find_matching_instance (widget_instance* instance)
1367 destroyed_instance* cur;
1368 destroyed_instance* prev;
1369 char* type = instance->info->type;
1370 char* name = instance->info->name;
1372 for (prev = NULL, cur = all_destroyed_instances;
1374 prev = cur, cur = cur->next)
1376 if (!strcmp (cur->name, name)
1377 && !strcmp (cur->type, type)
1378 && cur->parent == instance->parent
1379 && cur->pop_up_p == instance->pop_up_p)
1382 prev->next = cur->next;
1384 all_destroyed_instances = cur->next;
1387 /* do some cleanup */
1388 else if (!cur->widget)
1391 prev->next = cur->next;
1393 all_destroyed_instances = cur->next;
1394 free_destroyed_instance (cur);
1395 cur = prev ? prev : all_destroyed_instances;
1402 recenter_widget (Widget widget)
1404 Widget parent = XtParent (widget);
1405 Screen* screen = XtScreen (widget);
1406 Dimension screen_width = WidthOfScreen (screen);
1407 Dimension screen_height = HeightOfScreen (screen);
1408 Dimension parent_width = 0;
1409 Dimension parent_height = 0;
1410 Dimension child_width = 0;
1411 Dimension child_height = 0;
1416 XtSetArg (al [0], XtNwidth, &child_width);
1417 XtSetArg (al [1], XtNheight, &child_height);
1418 XtGetValues (widget, al, 2);
1420 XtSetArg (al [0], XtNwidth, &parent_width);
1421 XtSetArg (al [1], XtNheight, &parent_height);
1422 XtGetValues (parent, al, 2);
1424 x = (Position) ((parent_width - child_width) / 2);
1425 y = (Position) ((parent_height - child_height) / 2);
1427 XtTranslateCoords (parent, x, y, &x, &y);
1429 if ((Dimension) (x + child_width) > screen_width)
1430 x = screen_width - child_width;
1434 if ((Dimension) (y + child_height) > screen_height)
1435 y = screen_height - child_height;
1439 XtSetArg (al [0], XtNx, x);
1440 XtSetArg (al [1], XtNy, y);
1441 XtSetValues (widget, al, 2);
1445 recycle_instance (destroyed_instance* instance)
1447 Widget widget = instance->widget;
1449 /* widget is NULL if the parent was destroyed. */
1455 /* Remove the destroy callback as the instance is not in the list
1457 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1458 mark_dead_instance_destroyed,
1459 (XtPointer)instance);
1461 /* Give the focus to the initial item */
1462 focus = XtNameToWidget (widget, "*value");
1464 focus = XtNameToWidget (widget, "*button1");
1466 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1468 /* shrink the separator label back to their original size */
1469 separator = XtNameToWidget (widget, "*separator_button");
1473 XtSetArg (al [0], XtNwidth, 5);
1474 XtSetArg (al [1], XtNheight, 5);
1475 XtSetValues (separator, al, 2);
1478 /* Center the dialog in its parent */
1479 recenter_widget (widget);
1481 free_destroyed_instance (instance);
1486 xm_create_dialog (widget_instance* instance)
1488 char* name = instance->info->type;
1489 Widget parent = instance->parent;
1491 Boolean pop_up_p = instance->pop_up_p;
1492 const char* shell_name = 0;
1493 const char* icon_name = 0;
1494 Boolean text_input_slot = False;
1495 Boolean radio_box = False;
1496 Boolean list = False;
1498 int left_buttons = 0;
1499 int right_buttons = 1;
1500 destroyed_instance* dead_one;
1502 /* try to find a widget to recycle */
1503 dead_one = find_matching_instance (instance);
1506 Widget recycled_widget = recycle_instance (dead_one);
1507 if (recycled_widget)
1508 return recycled_widget;
1513 icon_name = "dbox-error";
1514 shell_name = "Error";
1518 icon_name = "dbox-info";
1519 shell_name = "Information";
1524 icon_name = "dbox-question";
1525 shell_name = "Prompt";
1529 text_input_slot = True;
1530 icon_name = "dbox-question";
1531 shell_name = "Prompt";
1535 icon_name = "dbox-question";
1536 shell_name = "Question";
1540 total_buttons = name [1] - '0';
1542 if (name [3] == 'T' || name [3] == 't')
1544 text_input_slot = False;
1548 right_buttons = name [4] - '0';
1550 left_buttons = total_buttons - right_buttons;
1552 widget = make_dialog (name, parent, pop_up_p,
1553 shell_name, icon_name, text_input_slot, radio_box,
1554 list, left_buttons, right_buttons);
1556 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1557 (XtPointer) instance);
1561 #endif /* LWLIB_DIALOGS_MOTIF */
1563 #ifdef LWLIB_MENUBARS_MOTIF
1565 make_menubar (widget_instance* instance)
1570 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1571 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1573 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1577 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1579 Widget menu = (Widget) closure;
1580 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1584 make_popup_menu (widget_instance* instance)
1586 Widget parent = instance->parent;
1587 Window parent_window = parent->core.window;
1590 /* sets the parent window to 0 to fool Motif into not generating a grab */
1591 parent->core.window = 0;
1592 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1593 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1595 parent->core.window = parent_window;
1598 #endif /* LWLIB_MENUBARS_MOTIF */
1600 #ifdef LWLIB_SCROLLBARS_MOTIF
1602 make_scrollbar (widget_instance *instance, int vertical)
1606 static XtCallbackRec callbacks[2] =
1607 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1609 callbacks[0].closure = (XtPointer) instance;
1611 XtSetArg (al[ac], XmNminimum, 1); ac++;
1612 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1613 XtSetArg (al[ac], XmNincrement, 1); ac++;
1614 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1615 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1616 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1618 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1619 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1620 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1621 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1622 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1623 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1624 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1625 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1627 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1631 make_vertical_scrollbar (widget_instance *instance)
1633 return make_scrollbar (instance, 1);
1637 make_horizontal_scrollbar (widget_instance *instance)
1639 return make_scrollbar (instance, 0);
1642 #endif /* LWLIB_SCROLLBARS_MOTIF */
1644 #ifdef LWLIB_WIDGETS_MOTIF
1647 xm_create_button (widget_instance *instance)
1652 widget_value* val = instance->info->val;
1654 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1655 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1656 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1657 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1658 /* The highlight doesn't appear to be dynamically set which makes it
1659 look ugly. I think this may be a LessTif bug but for now we just
1661 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1663 /* add any args the user supplied for creation time */
1664 lw_add_value_args_to_args (val, al, &ac);
1666 if (!val->call_data)
1667 button = XmCreateLabel (instance->parent, val->name, al, ac);
1669 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1671 XtSetArg (al [ac], XmNset, val->selected); ac++;
1672 XtSetArg (al [ac], XmNindicatorType,
1673 (val->type == TOGGLE_TYPE ?
1674 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1675 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1676 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1677 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1678 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1679 (XtPointer)instance);
1683 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1684 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1685 (XtPointer)instance);
1688 XtManageChild (button);
1694 xm_create_progress (widget_instance *instance)
1698 Dimension height = 0;
1699 Dimension width = 0;
1701 widget_value* val = instance->info->val;
1702 if (!val->call_data)
1704 XtSetArg (al [ac], XmNeditable, False); ac++;
1708 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
1710 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1711 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1712 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1713 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1714 /* The highlight doesn't appear to be dynamically set which makes it
1715 look ugly. I think this may be a LessTif bug but for now we just
1717 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1719 height = (Dimension)lw_get_value_arg (val, XtNheight);
1720 width = (Dimension)lw_get_value_arg (val, XtNwidth);
1723 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
1727 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
1730 /* add any args the user supplied for creation time */
1731 lw_add_value_args_to_args (val, al, &ac);
1733 scale = XmCreateScale (instance->parent, val->name, al, ac);
1735 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1736 (XtPointer)instance);
1738 XtManageChild (scale);
1744 xm_create_text_field (widget_instance *instance)
1749 widget_value* val = instance->info->val;
1751 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1752 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1753 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1754 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1755 /* The highlight doesn't appear to be dynamically set which makes it
1756 look ugly. I think this may be a LessTif bug but for now we just
1758 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1760 /* add any args the user supplied for creation time */
1761 lw_add_value_args_to_args (val, al, &ac);
1763 text = XmCreateTextField (instance->parent, val->name, al, ac);
1765 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1766 (XtPointer)instance);
1768 XtManageChild (text);
1774 xm_create_label_field (widget_instance *instance)
1776 return xm_create_label (instance->parent, instance->info->val);
1780 xm_create_label (Widget parent, widget_value* val)
1786 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1787 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1788 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1789 /* The highlight doesn't appear to be dynamically set which makes it
1790 look ugly. I think this may be a LessTif bug but for now we just
1792 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1794 /* add any args the user supplied for creation time */
1795 lw_add_value_args_to_args (val, al, &ac);
1797 label = XmCreateLabel (parent, val->name, al, ac);
1799 XtManageChild (label);
1801 /* Do it again for arguments that have no effect until the widget is realized. */
1803 lw_add_value_args_to_args (val, al, &ac);
1804 XtSetValues (label, al, ac);
1811 xm_create_combo_box (widget_instance *instance)
1816 widget_value* val = instance->info->val;
1818 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1819 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1820 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1821 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1822 /* The highlight doesn't appear to be dynamically set which makes it
1823 look ugly. I think this may be a LessTif bug but for now we just
1825 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1827 /* add any args the user supplied for creation time */
1828 lw_add_value_args_to_args (val, al, &ac);
1830 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1832 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1833 (XtPointer)instance);
1835 XtManageChild (combo);
1840 #endif /* LWLIB_WIDGETS_MOTIF */
1843 /* Table of functions to create widgets */
1845 const widget_creation_entry
1846 xm_creation_table [] =
1848 #ifdef LWLIB_MENUBARS_MOTIF
1849 {"menubar", make_menubar},
1850 {"popup", make_popup_menu},
1852 #ifdef LWLIB_SCROLLBARS_MOTIF
1853 {"vertical-scrollbar", make_vertical_scrollbar},
1854 {"horizontal-scrollbar", make_horizontal_scrollbar},
1856 #ifdef LWLIB_WIDGETS_MOTIF
1857 {"button", xm_create_button},
1858 {"progress", xm_create_progress},
1859 {"text-field", xm_create_text_field},
1860 {"label", xm_create_label_field},
1862 {"combo-box", xm_create_combo_box},
1868 \f/* Destruction of instances */
1870 xm_destroy_instance (widget_instance* instance)
1872 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1873 /* It appears that this is used only for dialog boxes. */
1874 Widget widget = instance->widget;
1875 /* recycle the dialog boxes */
1876 /* Disable the recycling until we can find a way to have the dialog box
1877 get reasonable layout after we modify its contents. */
1879 && XtClass (widget) == xmDialogShellWidgetClass)
1881 destroyed_instance* dead_instance =
1882 make_destroyed_instance (instance->info->name,
1883 instance->info->type,
1886 instance->pop_up_p);
1887 dead_instance->next = all_destroyed_instances;
1888 all_destroyed_instances = dead_instance;
1889 XtUnmanageChild (first_child (instance->widget));
1890 XFlush (XtDisplay (instance->widget));
1891 XtAddCallback (instance->parent, XtNdestroyCallback,
1892 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1896 /* This might not be necessary now that the nosel is attached to
1897 popdown instead of destroy, but it can't hurt. */
1898 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1899 xm_nosel_callback, (XtPointer)instance);
1901 XtDestroyWidget (instance->widget);
1903 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1906 \f/* popup utility */
1907 #ifdef LWLIB_MENUBARS_MOTIF
1910 xm_popup_menu (Widget widget, XEvent *event)
1912 if (event->type == ButtonPress || event->type == ButtonRelease)
1914 /* This is so totally ridiculous: there's NO WAY to tell Motif
1915 that *any* button can select a menu item. Only one button
1916 can have that honor.
1919 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1920 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1921 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1922 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1923 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1927 XtSetArg (al [0], XmNmenuPost, trans);
1928 XtSetValues (widget, al, 1);
1930 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1932 XtManageChild (widget);
1937 #ifdef LWLIB_DIALOGS_MOTIF
1940 set_min_dialog_size (Widget w)
1946 XtSetArg (al [0], XmNwidth, &width);
1947 XtSetArg (al [1], XmNheight, &height);
1948 XtGetValues (w, al, 2);
1950 XtSetArg (al [0], XmNminWidth, width);
1951 XtSetArg (al [1], XmNminHeight, height);
1952 XtSetValues (w, al, 2);
1958 xm_pop_instance (widget_instance* instance, Boolean up)
1960 Widget widget = instance->widget;
1962 #ifdef LWLIB_DIALOGS_MOTIF
1963 if (XtClass (widget) == xmDialogShellWidgetClass)
1965 Widget widget_to_manage = first_child (widget);
1968 XtManageChild (widget_to_manage);
1969 set_min_dialog_size (widget);
1970 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1973 XtUnmanageChild (widget_to_manage);
1979 XtManageChild (widget);
1981 XtUnmanageChild (widget);
1986 /* motif callback */
1988 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1991 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1993 XtPointer user_data;
1994 widget_instance* instance = (widget_instance*)closure;
1995 Widget instance_widget;
2001 if (widget->core.being_destroyed)
2004 instance_widget = instance->widget;
2005 if (!instance_widget)
2008 id = instance->info->id;
2010 XtSetArg(al [0], XmNuserData, &user_data);
2011 XtGetValues (widget, al, 1);
2015 if (instance->info->pre_activate_cb)
2016 instance->info->pre_activate_cb (widget, id, user_data);
2019 if (instance->info->selection_cb)
2020 instance->info->selection_cb (widget, id, user_data);
2023 if (instance->info->selection_cb)
2024 instance->info->selection_cb (widget, id, (XtPointer) -1);
2027 if (instance->info->post_activate_cb)
2028 instance->info->post_activate_cb (widget, id, user_data);
2035 /* Like lw_internal_update_other_instances except that it does not do
2036 anything if its shell parent is not managed. This is to protect
2037 lw_internal_update_other_instances to dereference freed memory
2038 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2041 xm_internal_update_other_instances (Widget widget, XtPointer closure,
2042 XtPointer call_data)
2045 for (parent = widget; parent; parent = XtParent (parent))
2046 if (XtIsShell (parent))
2048 else if (!XtIsManaged (parent))
2050 lw_internal_update_other_instances (widget, closure, call_data);
2054 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
2056 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2057 /* We want the selected status to change only when we decide it
2058 should change. Yuck but correct. */
2059 if (XtClass (widget) == xmToggleButtonWidgetClass
2060 || XtClass (widget) == xmToggleButtonGadgetClass)
2065 XtSetArg (al [0], XmNset, &check);
2066 XtGetValues (widget, al, 1);
2068 XtSetArg (al [0], XmNset, !check);
2069 XtSetValues (widget, al, 1);
2072 lw_internal_update_other_instances (widget, closure, call_data);
2073 do_call (widget, closure, selection);
2077 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2079 do_call (widget, closure, post_activate);
2082 #ifdef LWLIB_MENUBARS_MOTIF
2085 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2090 /* new behavior for incremental menu construction */
2095 do_call (widget, closure, pre_activate);
2098 #endif /* LWLIB_MENUBARS_MOTIF */
2100 #ifdef LWLIB_SCROLLBARS_MOTIF
2102 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2104 widget_instance *instance = (widget_instance *) closure;
2106 XmScrollBarCallbackStruct *data =
2107 (XmScrollBarCallbackStruct *) call_data;
2108 scroll_event event_data;
2109 scrollbar_values *val =
2110 (scrollbar_values *) instance->info->val->scrollbar_data;
2113 if (!instance || widget->core.being_destroyed)
2116 id = instance->info->id;
2118 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2119 event_data.slider_value =
2120 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2122 if (event_data.slider_value > (val->maximum - val->slider_size))
2123 event_data.slider_value = val->maximum - val->slider_size;
2124 else if (event_data.slider_value < 1)
2125 event_data.slider_value = 1;
2129 switch (data->event->xany.type)
2133 event_data.time = data->event->xkey.time;
2137 event_data.time = data->event->xbutton.time;
2140 event_data.time = data->event->xmotion.time;
2144 event_data.time = data->event->xcrossing.time;
2147 event_data.time = 0;
2152 event_data.time = 0;
2154 switch (data->reason)
2156 case XmCR_DECREMENT:
2157 event_data.action = SCROLLBAR_LINE_UP;
2159 case XmCR_INCREMENT:
2160 event_data.action = SCROLLBAR_LINE_DOWN;
2162 case XmCR_PAGE_DECREMENT:
2163 event_data.action = SCROLLBAR_PAGE_UP;
2165 case XmCR_PAGE_INCREMENT:
2166 event_data.action = SCROLLBAR_PAGE_DOWN;
2169 event_data.action = SCROLLBAR_TOP;
2171 case XmCR_TO_BOTTOM:
2172 event_data.action = SCROLLBAR_BOTTOM;
2175 event_data.action = SCROLLBAR_DRAG;
2177 case XmCR_VALUE_CHANGED:
2178 event_data.action = SCROLLBAR_CHANGE;
2181 event_data.action = SCROLLBAR_CHANGE;
2185 if (instance->info->pre_activate_cb)
2186 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2188 #endif /* LWLIB_SCROLLBARS_MOTIF */
2190 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2192 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2193 XtPointer call_data)
2195 destroyed_instance* instance = (destroyed_instance*)closure;
2196 instance->widget = NULL;
2200 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2202 /* This callback is only called when a dialog box is dismissed with the wm's
2203 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2204 in that case, not just unmapped, so that it releases its keyboard grabs.
2205 But there are problems with running our callbacks while the widget is in
2206 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2207 instead of XmDESTROY and then destroy it ourself after having run the
2210 do_call (widget, closure, no_selection);
2211 XtDestroyWidget (widget);
2216 /* set the keyboard focus */
2218 xm_set_keyboard_focus (Widget parent, Widget w)
2220 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2221 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2222 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2223 Presumably the problem was elsewhere, and is now gone...