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 else if (class == xmScaleWidgetClass)
901 xm_update_progress (instance, widget, val);
903 /* Lastly update our global arg values. */
904 if (val->args && val->args->nargs)
905 XtSetValues (widget, val->args->args, val->args->nargs);
908 \f/* getting the value back */
910 xm_update_one_value (widget_instance* instance, Widget widget,
913 WidgetClass class = XtClass (widget);
914 widget_value *old_wv;
916 /* copy the call_data slot into the "return" widget_value */
917 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
918 if (!strcmp (val->name, old_wv->name))
920 val->call_data = old_wv->call_data;
924 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
927 XtSetArg (al [0], XmNset, &val->selected);
928 XtGetValues (widget, al, 1);
931 else if (class == xmTextWidgetClass)
935 val->value = XmTextGetString (widget);
938 else if (class == xmTextFieldWidgetClass)
942 val->value = XmTextFieldGetString (widget);
945 else if (class == xmRowColumnWidgetClass)
947 Boolean radiobox = 0;
950 XtSetArg (al [0], XmNradioBehavior, &radiobox);
951 XtGetValues (widget, al, 1);
956 CompositeWidget radio = (CompositeWidget)widget;
958 for (i = 0; i < radio->composite.num_children; i++)
961 Widget toggle = radio->composite.children [i];
964 XtSetArg (al [0], XmNset, &set);
965 XtGetValues (toggle, al, 1);
970 val->value = safe_strdup (XtName (toggle));
976 else if (class == xmListWidgetClass
977 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
978 || class == xmComboBoxWidgetClass
984 Widget list = widget;
985 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
986 if (class == xmComboBoxWidgetClass)
987 list = CB_List (widget);
989 if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
993 for (cur = val->contents, i = 0; cur; cur = cur->next)
997 cur->selected = False;
999 for (j = 0; j < pos_cnt; j++)
1000 if (pos_list [j] == i)
1002 cur->selected = True;
1003 val->value = safe_strdup (cur->name);
1007 XtFree ((char *) pos_list);
1010 #ifdef LWLIB_SCROLLBARS_MOTIF
1011 else if (class == xmScrollBarWidgetClass)
1013 /* This function is not used by the scrollbar. */
1020 /* This function is for activating a button from a program. It's wrong because
1021 we pass a NULL argument in the call_data which is not Motif compatible.
1022 This is used from the XmNdefaultAction callback of the List widgets to
1023 have a double-click put down a dialog box like the button would do.
1024 I could not find a way to do that with accelerators.
1027 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
1029 Widget button = (Widget)closure;
1030 XtCallCallbacks (button, XmNactivateCallback, NULL);
1033 /* creation functions */
1035 #ifdef LWLIB_DIALOGS_MOTIF
1039 #if (XmVersion >= 1002)
1040 # define ARMANDACTIVATE_KLUDGE
1044 #ifdef ARMANDACTIVATE_KLUDGE
1045 /* We want typing Return at a dialog box to select the default button; but
1046 we're satisfied with having it select the leftmost button instead.
1048 In Motif 1.1.5 we could do this by putting this resource in the
1051 *dialog*button1.accelerators:#override\
1052 <KeyPress>Return: ArmAndActivate()\n\
1053 <KeyPress>KP_Enter: ArmAndActivate()\n\
1054 Ctrl<KeyPress>m: ArmAndActivate()\n
1056 but that doesn't work with 1.2.1 and I don't understand why. However,
1057 doing the equivalent C code does work, with the notable disadvantage that
1058 the user can't override it. So that's what we do until we figure out
1059 something better....
1061 static char button_trans[] = "\
1062 <KeyPress>Return: ArmAndActivate()\n\
1063 <KeyPress>KP_Enter: ArmAndActivate()\n\
1064 Ctrl<KeyPress>m: ArmAndActivate()\n";
1066 #endif /* ARMANDACTIVATE_KLUDGE */
1070 /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom
1071 was a segv down in libXm somewhere if you used the middle button on a
1072 dialog box to begin a drag; when you released the button to make a drop
1073 things would lose if you were not over the button where you started the
1074 drag (canceling the operation). This was probably due to the fact that
1075 the dialog boxes were not set up to handle a drag but were trying to do
1076 so anyway for some reason.
1078 So we disable drag-and-drop in dialog boxes by turning off the binding for
1079 Btn2Down which, by default, initiates a drag. Clearly this is a shitty
1080 solution as it only works in default configurations, but...
1082 static char disable_dnd_trans[] = "<Btn2Down>: ";
1083 #endif /* DND_KLUDGE */
1087 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1088 const char* shell_title, const char* icon_name,
1089 Boolean text_input_slot, Boolean radio_box, Boolean list,
1090 int left_buttons, int right_buttons)
1096 Widget icon_separator;
1101 Widget children [16]; /* for the final XtManageChildren */
1103 Arg al[64]; /* Arg List */
1104 int ac; /* Arg Count */
1108 XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1109 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1110 #else /* ! DND_KLUDGE */
1111 # define DO_DND_KLUDGE(widget)
1112 #endif /* ! DND_KLUDGE */
1117 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1118 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1119 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1120 result = XmCreateDialogShell (parent, "dialog", al, ac);
1122 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1123 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1124 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1125 form = XmCreateForm (result, (char *) shell_title, al, ac);
1130 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1131 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1132 form = XmCreateForm (parent, (char *) shell_title, al, ac);
1137 XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++;
1138 XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++;
1139 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1140 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1141 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1142 XtSetArg(al[ac], XmNspacing, 13); ac++;
1143 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1144 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1145 XtSetArg(al[ac], XmNisAligned, True); ac++;
1146 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1147 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1148 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1149 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1150 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1151 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1152 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1153 row = XmCreateRowColumn (form, "row", al, ac);
1156 for (i = 0; i < left_buttons; i++)
1158 char button_name [16];
1159 sprintf (button_name, "button%d", i + 1);
1163 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1164 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1166 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1167 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1168 DO_DND_KLUDGE (children [n_children]);
1172 button = children [n_children];
1174 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1175 XtSetValues (row, al, ac);
1177 #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */
1179 XtTranslations losers = XtParseTranslationTable (button_trans);
1180 XtOverrideTranslations (button, losers);
1181 XtFree ((char *) losers);
1183 #endif /* ARMANDACTIVATE_KLUDGE */
1189 /* invisible separator button */
1191 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1192 children [n_children] = XmCreateLabel (row, "separator_button",
1194 DO_DND_KLUDGE (children [n_children]);
1197 for (i = 0; i < right_buttons; i++)
1199 char button_name [16];
1200 sprintf (button_name, "button%d", left_buttons + i + 1);
1202 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1203 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1204 DO_DND_KLUDGE (children [n_children]);
1205 if (! button) button = children [n_children];
1209 XtManageChildren (children, n_children);
1212 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1213 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1214 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1215 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1216 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1217 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1218 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1219 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1220 separator = XmCreateSeparator (form, "", al, ac);
1223 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1224 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1225 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1226 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1227 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1228 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1229 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1230 icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1231 DO_DND_KLUDGE (icon);
1234 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1235 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1236 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1237 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1238 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1239 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1240 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1241 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1242 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1243 icon_separator = XmCreateLabel (form, "", al, ac);
1244 DO_DND_KLUDGE (icon_separator);
1246 if (text_input_slot)
1249 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1250 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1251 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1252 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1253 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1254 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1255 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1256 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1257 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1258 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1259 value = XmCreateTextField (form, "value", al, ac);
1260 DO_DND_KLUDGE (value);
1266 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1267 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1268 XtSetArg(al[ac], XmNspacing, 13); ac++;
1269 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1270 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1271 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1272 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1273 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1274 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1275 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1276 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1277 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1278 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1279 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1282 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1283 children [i++] = radio_butt;
1284 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1285 children [i++] = radio_butt;
1286 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1287 children [i++] = radio_butt;
1288 XtManageChildren (children, i);
1293 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1294 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1295 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1296 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1297 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1298 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1299 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1300 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1301 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1302 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1303 value = XmCreateScrolledList (form, "list", al, ac);
1305 /* this is the easiest way I found to have the double click in the
1306 list activate the default button */
1307 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1309 /* else add nothing; it's a separator */
1312 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1313 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1314 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1315 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1316 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1317 XtSetArg(al[ac], XmNbottomWidget,
1318 text_input_slot || radio_box || list ? value : separator); ac++;
1319 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1320 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1321 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1322 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1323 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1324 message = XmCreateLabel (form, "message", al, ac);
1325 DO_DND_KLUDGE (message);
1328 XtManageChild (value);
1331 children [i] = row; i++;
1332 children [i] = separator; i++;
1333 if (text_input_slot || radio_box)
1335 children [i] = value; i++;
1337 children [i] = message; i++;
1338 children [i] = icon; i++;
1339 children [i] = icon_separator; i++;
1340 XtManageChildren (children, i);
1342 if (text_input_slot || list)
1344 XtInstallAccelerators (value, button);
1345 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1349 XtInstallAccelerators (form, button);
1350 XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1352 /* else we don' need no STEENKIN' assellerators. */
1355 XtFree ((char *) dnd_override);
1357 #undef DO_DND_KLUDGE
1362 static destroyed_instance*
1363 find_matching_instance (widget_instance* instance)
1365 destroyed_instance* cur;
1366 destroyed_instance* prev;
1367 char* type = instance->info->type;
1368 char* name = instance->info->name;
1370 for (prev = NULL, cur = all_destroyed_instances;
1372 prev = cur, cur = cur->next)
1374 if (!strcmp (cur->name, name)
1375 && !strcmp (cur->type, type)
1376 && cur->parent == instance->parent
1377 && cur->pop_up_p == instance->pop_up_p)
1380 prev->next = cur->next;
1382 all_destroyed_instances = cur->next;
1385 /* do some cleanup */
1386 else if (!cur->widget)
1389 prev->next = cur->next;
1391 all_destroyed_instances = cur->next;
1392 free_destroyed_instance (cur);
1393 cur = prev ? prev : all_destroyed_instances;
1400 recenter_widget (Widget widget)
1402 Widget parent = XtParent (widget);
1403 Screen* screen = XtScreen (widget);
1404 Dimension screen_width = WidthOfScreen (screen);
1405 Dimension screen_height = HeightOfScreen (screen);
1406 Dimension parent_width = 0;
1407 Dimension parent_height = 0;
1408 Dimension child_width = 0;
1409 Dimension child_height = 0;
1414 XtSetArg (al [0], XtNwidth, &child_width);
1415 XtSetArg (al [1], XtNheight, &child_height);
1416 XtGetValues (widget, al, 2);
1418 XtSetArg (al [0], XtNwidth, &parent_width);
1419 XtSetArg (al [1], XtNheight, &parent_height);
1420 XtGetValues (parent, al, 2);
1422 x = (Position) ((parent_width - child_width) / 2);
1423 y = (Position) ((parent_height - child_height) / 2);
1425 XtTranslateCoords (parent, x, y, &x, &y);
1427 if ((Dimension) (x + child_width) > screen_width)
1428 x = screen_width - child_width;
1432 if ((Dimension) (y + child_height) > screen_height)
1433 y = screen_height - child_height;
1437 XtSetArg (al [0], XtNx, x);
1438 XtSetArg (al [1], XtNy, y);
1439 XtSetValues (widget, al, 2);
1443 recycle_instance (destroyed_instance* instance)
1445 Widget widget = instance->widget;
1447 /* widget is NULL if the parent was destroyed. */
1453 /* Remove the destroy callback as the instance is not in the list
1455 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1456 mark_dead_instance_destroyed,
1457 (XtPointer)instance);
1459 /* Give the focus to the initial item */
1460 focus = XtNameToWidget (widget, "*value");
1462 focus = XtNameToWidget (widget, "*button1");
1464 XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1466 /* shrink the separator label back to their original size */
1467 separator = XtNameToWidget (widget, "*separator_button");
1471 XtSetArg (al [0], XtNwidth, 5);
1472 XtSetArg (al [1], XtNheight, 5);
1473 XtSetValues (separator, al, 2);
1476 /* Center the dialog in its parent */
1477 recenter_widget (widget);
1479 free_destroyed_instance (instance);
1484 xm_create_dialog (widget_instance* instance)
1486 char* name = instance->info->type;
1487 Widget parent = instance->parent;
1489 Boolean pop_up_p = instance->pop_up_p;
1490 const char* shell_name = 0;
1491 const char* icon_name = 0;
1492 Boolean text_input_slot = False;
1493 Boolean radio_box = False;
1494 Boolean list = False;
1496 int left_buttons = 0;
1497 int right_buttons = 1;
1498 destroyed_instance* dead_one;
1500 /* try to find a widget to recycle */
1501 dead_one = find_matching_instance (instance);
1504 Widget recycled_widget = recycle_instance (dead_one);
1505 if (recycled_widget)
1506 return recycled_widget;
1511 icon_name = "dbox-error";
1512 shell_name = "Error";
1516 icon_name = "dbox-info";
1517 shell_name = "Information";
1522 icon_name = "dbox-question";
1523 shell_name = "Prompt";
1527 text_input_slot = True;
1528 icon_name = "dbox-question";
1529 shell_name = "Prompt";
1533 icon_name = "dbox-question";
1534 shell_name = "Question";
1538 total_buttons = name [1] - '0';
1540 if (name [3] == 'T' || name [3] == 't')
1542 text_input_slot = False;
1546 right_buttons = name [4] - '0';
1548 left_buttons = total_buttons - right_buttons;
1550 widget = make_dialog (name, parent, pop_up_p,
1551 shell_name, icon_name, text_input_slot, radio_box,
1552 list, left_buttons, right_buttons);
1554 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1555 (XtPointer) instance);
1559 #endif /* LWLIB_DIALOGS_MOTIF */
1561 #ifdef LWLIB_MENUBARS_MOTIF
1563 make_menubar (widget_instance* instance)
1568 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1569 XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1571 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1575 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1577 Widget menu = (Widget) closure;
1578 XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1582 make_popup_menu (widget_instance* instance)
1584 Widget parent = instance->parent;
1585 Window parent_window = parent->core.window;
1588 /* sets the parent window to 0 to fool Motif into not generating a grab */
1589 parent->core.window = 0;
1590 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1591 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1593 parent->core.window = parent_window;
1596 #endif /* LWLIB_MENUBARS_MOTIF */
1598 #ifdef LWLIB_SCROLLBARS_MOTIF
1600 make_scrollbar (widget_instance *instance, int vertical)
1604 static XtCallbackRec callbacks[2] =
1605 { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1607 callbacks[0].closure = (XtPointer) instance;
1609 XtSetArg (al[ac], XmNminimum, 1); ac++;
1610 XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1611 XtSetArg (al[ac], XmNincrement, 1); ac++;
1612 XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1613 XtSetArg (al[ac], XmNborderWidth, 0); ac++;
1614 XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1616 XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++;
1617 XtSetArg (al[ac], XmNdragCallback, callbacks); ac++;
1618 XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++;
1619 XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++;
1620 XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++;
1621 XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++;
1622 XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++;
1623 XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++;
1625 return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1629 make_vertical_scrollbar (widget_instance *instance)
1631 return make_scrollbar (instance, 1);
1635 make_horizontal_scrollbar (widget_instance *instance)
1637 return make_scrollbar (instance, 0);
1640 #endif /* LWLIB_SCROLLBARS_MOTIF */
1642 #ifdef LWLIB_WIDGETS_MOTIF
1645 xm_create_button (widget_instance *instance)
1650 widget_value* val = instance->info->val;
1652 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1653 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1654 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1655 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1656 /* The highlight doesn't appear to be dynamically set which makes it
1657 look ugly. I think this may be a LessTif bug but for now we just
1659 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1661 /* add any args the user supplied for creation time */
1662 lw_add_value_args_to_args (val, al, &ac);
1664 if (!val->call_data)
1665 button = XmCreateLabel (instance->parent, val->name, al, ac);
1667 else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1669 XtSetArg (al [ac], XmNset, val->selected); ac++;
1670 XtSetArg (al [ac], XmNindicatorType,
1671 (val->type == TOGGLE_TYPE ?
1672 XmN_OF_MANY : XmONE_OF_MANY)); ac++;
1673 XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1674 button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1675 XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1676 XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1677 (XtPointer)instance);
1681 button = XmCreatePushButton (instance->parent, val->name, al, ac);
1682 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1683 (XtPointer)instance);
1686 XtManageChild (button);
1692 xm_create_progress (widget_instance *instance)
1696 Dimension height = 0;
1697 Dimension width = 0;
1699 widget_value* val = instance->info->val;
1700 if (!val->call_data)
1702 XtSetArg (al [ac], XmNeditable, False); ac++;
1706 XtSetArg (al [ac], XmNeditable, val->enabled); ac++;
1708 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1709 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1710 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1711 XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++;
1712 /* The highlight doesn't appear to be dynamically set which makes it
1713 look ugly. I think this may be a LessTif bug but for now we just
1715 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1717 height = (Dimension)lw_get_value_arg (val, XtNheight);
1718 width = (Dimension)lw_get_value_arg (val, XtNwidth);
1721 XtSetArg (al [ac], XmNscaleHeight, height); ac++;
1725 XtSetArg (al [ac], XmNscaleWidth, width); ac++;
1728 /* add any args the user supplied for creation time */
1729 lw_add_value_args_to_args (val, al, &ac);
1731 scale = XmCreateScale (instance->parent, val->name, al, ac);
1733 XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1734 (XtPointer)instance);
1736 XtManageChild (scale);
1742 xm_create_text_field (widget_instance *instance)
1747 widget_value* val = instance->info->val;
1749 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1750 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1751 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1752 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1753 /* The highlight doesn't appear to be dynamically set which makes it
1754 look ugly. I think this may be a LessTif bug but for now we just
1756 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1758 /* add any args the user supplied for creation time */
1759 lw_add_value_args_to_args (val, al, &ac);
1761 text = XmCreateTextField (instance->parent, val->name, al, ac);
1763 XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1764 (XtPointer)instance);
1766 XtManageChild (text);
1772 xm_create_label_field (widget_instance *instance)
1774 return xm_create_label (instance->parent, instance->info->val);
1778 xm_create_label (Widget parent, widget_value* val)
1784 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1785 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1786 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1787 /* The highlight doesn't appear to be dynamically set which makes it
1788 look ugly. I think this may be a LessTif bug but for now we just
1790 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1792 /* add any args the user supplied for creation time */
1793 lw_add_value_args_to_args (val, al, &ac);
1795 label = XmCreateLabel (parent, val->name, al, ac);
1797 XtManageChild (label);
1799 /* Do it again for arguments that have no effect until the widget is realized. */
1801 lw_add_value_args_to_args (val, al, &ac);
1802 XtSetValues (label, al, ac);
1809 xm_create_combo_box (widget_instance *instance)
1814 widget_value* val = instance->info->val;
1816 XtSetArg (al [ac], XmNsensitive, val->enabled); ac++;
1817 XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1818 XtSetArg (al [ac], XmNuserData, val->call_data); ac++;
1819 XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++;
1820 /* The highlight doesn't appear to be dynamically set which makes it
1821 look ugly. I think this may be a LessTif bug but for now we just
1823 XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1825 /* add any args the user supplied for creation time */
1826 lw_add_value_args_to_args (val, al, &ac);
1828 combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1830 XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1831 (XtPointer)instance);
1833 XtManageChild (combo);
1838 #endif /* LWLIB_WIDGETS_MOTIF */
1841 /* Table of functions to create widgets */
1843 const widget_creation_entry
1844 xm_creation_table [] =
1846 #ifdef LWLIB_MENUBARS_MOTIF
1847 {"menubar", make_menubar},
1848 {"popup", make_popup_menu},
1850 #ifdef LWLIB_SCROLLBARS_MOTIF
1851 {"vertical-scrollbar", make_vertical_scrollbar},
1852 {"horizontal-scrollbar", make_horizontal_scrollbar},
1854 #ifdef LWLIB_WIDGETS_MOTIF
1855 {"button", xm_create_button},
1856 {"progress", xm_create_progress},
1857 {"text-field", xm_create_text_field},
1858 {"label", xm_create_label_field},
1860 {"combo-box", xm_create_combo_box},
1866 \f/* Destruction of instances */
1868 xm_destroy_instance (widget_instance* instance)
1870 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1871 /* It appears that this is used only for dialog boxes. */
1872 Widget widget = instance->widget;
1873 /* recycle the dialog boxes */
1874 /* Disable the recycling until we can find a way to have the dialog box
1875 get reasonable layout after we modify its contents. */
1877 && XtClass (widget) == xmDialogShellWidgetClass)
1879 destroyed_instance* dead_instance =
1880 make_destroyed_instance (instance->info->name,
1881 instance->info->type,
1884 instance->pop_up_p);
1885 dead_instance->next = all_destroyed_instances;
1886 all_destroyed_instances = dead_instance;
1887 XtUnmanageChild (first_child (instance->widget));
1888 XFlush (XtDisplay (instance->widget));
1889 XtAddCallback (instance->parent, XtNdestroyCallback,
1890 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1894 /* This might not be necessary now that the nosel is attached to
1895 popdown instead of destroy, but it can't hurt. */
1896 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1897 xm_nosel_callback, (XtPointer)instance);
1899 XtDestroyWidget (instance->widget);
1901 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1904 \f/* popup utility */
1905 #ifdef LWLIB_MENUBARS_MOTIF
1908 xm_popup_menu (Widget widget, XEvent *event)
1910 if (event->type == ButtonPress || event->type == ButtonRelease)
1912 /* This is so totally ridiculous: there's NO WAY to tell Motif
1913 that *any* button can select a menu item. Only one button
1914 can have that honor.
1917 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1918 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1919 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1920 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1921 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1925 XtSetArg (al [0], XmNmenuPost, trans);
1926 XtSetValues (widget, al, 1);
1928 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1930 XtManageChild (widget);
1935 #ifdef LWLIB_DIALOGS_MOTIF
1938 set_min_dialog_size (Widget w)
1944 XtSetArg (al [0], XmNwidth, &width);
1945 XtSetArg (al [1], XmNheight, &height);
1946 XtGetValues (w, al, 2);
1948 XtSetArg (al [0], XmNminWidth, width);
1949 XtSetArg (al [1], XmNminHeight, height);
1950 XtSetValues (w, al, 2);
1956 xm_pop_instance (widget_instance* instance, Boolean up)
1958 Widget widget = instance->widget;
1960 #ifdef LWLIB_DIALOGS_MOTIF
1961 if (XtClass (widget) == xmDialogShellWidgetClass)
1963 Widget widget_to_manage = first_child (widget);
1966 XtManageChild (widget_to_manage);
1967 set_min_dialog_size (widget);
1968 XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1971 XtUnmanageChild (widget_to_manage);
1977 XtManageChild (widget);
1979 XtUnmanageChild (widget);
1984 /* motif callback */
1986 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1989 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1991 XtPointer user_data;
1992 widget_instance* instance = (widget_instance*)closure;
1993 Widget instance_widget;
1999 if (widget->core.being_destroyed)
2002 instance_widget = instance->widget;
2003 if (!instance_widget)
2006 id = instance->info->id;
2008 XtSetArg(al [0], XmNuserData, &user_data);
2009 XtGetValues (widget, al, 1);
2013 if (instance->info->pre_activate_cb)
2014 instance->info->pre_activate_cb (widget, id, user_data);
2017 if (instance->info->selection_cb)
2018 instance->info->selection_cb (widget, id, user_data);
2021 if (instance->info->selection_cb)
2022 instance->info->selection_cb (widget, id, (XtPointer) -1);
2025 if (instance->info->post_activate_cb)
2026 instance->info->post_activate_cb (widget, id, user_data);
2033 /* Like lw_internal_update_other_instances except that it does not do
2034 anything if its shell parent is not managed. This is to protect
2035 lw_internal_update_other_instances to dereference freed memory
2036 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
2039 xm_internal_update_other_instances (Widget widget, XtPointer closure,
2040 XtPointer call_data)
2043 for (parent = widget; parent; parent = XtParent (parent))
2044 if (XtIsShell (parent))
2046 else if (!XtIsManaged (parent))
2048 lw_internal_update_other_instances (widget, closure, call_data);
2052 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
2054 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
2055 /* We want the selected status to change only when we decide it
2056 should change. Yuck but correct. */
2057 if (XtClass (widget) == xmToggleButtonWidgetClass
2058 || XtClass (widget) == xmToggleButtonGadgetClass)
2063 XtSetArg (al [0], XmNset, &check);
2064 XtGetValues (widget, al, 1);
2066 XtSetArg (al [0], XmNset, !check);
2067 XtSetValues (widget, al, 1);
2070 lw_internal_update_other_instances (widget, closure, call_data);
2071 do_call (widget, closure, selection);
2075 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2077 do_call (widget, closure, post_activate);
2080 #ifdef LWLIB_MENUBARS_MOTIF
2083 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2088 /* new behavior for incremental menu construction */
2093 do_call (widget, closure, pre_activate);
2096 #endif /* LWLIB_MENUBARS_MOTIF */
2098 #ifdef LWLIB_SCROLLBARS_MOTIF
2100 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2102 widget_instance *instance = (widget_instance *) closure;
2104 XmScrollBarCallbackStruct *data =
2105 (XmScrollBarCallbackStruct *) call_data;
2106 scroll_event event_data;
2107 scrollbar_values *val =
2108 (scrollbar_values *) instance->info->val->scrollbar_data;
2111 if (!instance || widget->core.being_destroyed)
2114 id = instance->info->id;
2116 percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2117 event_data.slider_value =
2118 (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2120 if (event_data.slider_value > (val->maximum - val->slider_size))
2121 event_data.slider_value = val->maximum - val->slider_size;
2122 else if (event_data.slider_value < 1)
2123 event_data.slider_value = 1;
2127 switch (data->event->xany.type)
2131 event_data.time = data->event->xkey.time;
2135 event_data.time = data->event->xbutton.time;
2138 event_data.time = data->event->xmotion.time;
2142 event_data.time = data->event->xcrossing.time;
2145 event_data.time = 0;
2150 event_data.time = 0;
2152 switch (data->reason)
2154 case XmCR_DECREMENT:
2155 event_data.action = SCROLLBAR_LINE_UP;
2157 case XmCR_INCREMENT:
2158 event_data.action = SCROLLBAR_LINE_DOWN;
2160 case XmCR_PAGE_DECREMENT:
2161 event_data.action = SCROLLBAR_PAGE_UP;
2163 case XmCR_PAGE_INCREMENT:
2164 event_data.action = SCROLLBAR_PAGE_DOWN;
2167 event_data.action = SCROLLBAR_TOP;
2169 case XmCR_TO_BOTTOM:
2170 event_data.action = SCROLLBAR_BOTTOM;
2173 event_data.action = SCROLLBAR_DRAG;
2175 case XmCR_VALUE_CHANGED:
2176 event_data.action = SCROLLBAR_CHANGE;
2179 event_data.action = SCROLLBAR_CHANGE;
2183 if (instance->info->pre_activate_cb)
2184 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2186 #endif /* LWLIB_SCROLLBARS_MOTIF */
2188 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2190 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2191 XtPointer call_data)
2193 destroyed_instance* instance = (destroyed_instance*)closure;
2194 instance->widget = NULL;
2198 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2200 /* This callback is only called when a dialog box is dismissed with the wm's
2201 destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed
2202 in that case, not just unmapped, so that it releases its keyboard grabs.
2203 But there are problems with running our callbacks while the widget is in
2204 the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2205 instead of XmDESTROY and then destroy it ourself after having run the
2208 do_call (widget, closure, no_selection);
2209 XtDestroyWidget (widget);
2214 /* set the keyboard focus */
2216 xm_set_keyboard_focus (Widget parent, Widget w)
2218 XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2219 /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2220 instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2221 Presumably the problem was elsewhere, and is now gone...