XEmacs 21.2.30 "Hygeia".
[chise/xemacs-chise.git.1] / lwlib / lwlib-Xm.c
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.
4
5 This file is part of the Lucid Widget Library.
6
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)
10 any later version.
11
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.
16
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.  */
21
22 #include <config.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <limits.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
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>
36
37 #include "lwlib-Xm.h"
38 #include "lwlib-utils.h"
39
40 #include <Xm/Xm.h>
41 #include <Xm/BulletinB.h>
42 #include <Xm/CascadeB.h>
43 #include <Xm/DrawingA.h>
44 #include <Xm/FileSB.h>
45 #include <Xm/Label.h>
46 #include <Xm/List.h>
47 #include <Xm/MenuShell.h>
48 #include <Xm/MessageB.h>
49 #include <Xm/PushB.h>
50 #include <Xm/PushBG.h>
51 #include <Xm/ArrowB.h>
52 #include <Xm/ScrollBar.h>
53 #include <Xm/SelectioB.h>
54 #include <Xm/Text.h>
55 #include <Xm/TextF.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>
62 #include <Xm/Form.h>
63 #ifdef LWLIB_WIDGETS_MOTIF
64 #include <Xm/Scale.h>
65 #if XmVERSION > 1
66 #include <Xm/ComboBoxP.h>
67 #endif
68 #endif
69
70 #ifdef LWLIB_MENUBARS_MOTIF
71 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
72 #endif
73 static void xm_internal_update_other_instances (Widget, XtPointer,
74                                                 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,
78                                           XtPointer call_data);
79 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
80 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
81 #endif
82 #ifdef LWLIB_SCROLLBARS_MOTIF
83 static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
84 #endif
85
86 #ifdef LWLIB_MENUBARS_MOTIF
87 static void
88 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
89                 Boolean deep_p);
90 #endif
91
92 \f/* Structures to keep destroyed instances */
93 typedef struct _destroyed_instance
94 {
95   char*         name;
96   char*         type;
97   Widget        widget;
98   Widget        parent;
99   Boolean       pop_up_p;
100   struct _destroyed_instance*   next;
101 } destroyed_instance;
102
103 static destroyed_instance*
104 all_destroyed_instances = NULL;
105
106 /* Utility function. */
107 static char *
108 safe_strdup (char* s)
109 {
110   char *result;
111   if (! s) return 0;
112   result = (char *) malloc (strlen (s) + 1);
113   if (! result)
114     return 0;
115   strcpy (result, s);
116   return result;
117 }
118
119 static destroyed_instance*
120 make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
121                          Boolean pop_up_p)
122 {
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;
131   return instance;
132 }
133
134 static void
135 free_destroyed_instance (destroyed_instance* instance)
136 {
137   free (instance->name);
138   free (instance->type);
139   free (instance);
140 }
141
142 \f/* motif utility functions */
143 Widget
144 first_child (Widget widget)
145 {
146   return ((CompositeWidget)widget)->composite.children [0];
147 }
148
149 Boolean
150 lw_motif_widget_p (Widget widget)
151 {
152   return
153 #ifdef LWLIB_DIALOGS_MOTIF
154     XtClass (widget) == xmDialogShellWidgetClass ||
155 #endif
156       XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
157 }
158
159 static char *
160 resource_string (Widget widget, char *name)
161 {
162   XtResource resource;
163   char *result = NULL;
164
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;
172
173   XtGetSubresources (widget, (XtPointer)&result, name,
174                      name, &resource, 1, NULL, 0);
175   return result;
176 }
177
178 \f
179
180 #ifdef LWLIB_DIALOGS_MOTIF
181
182 static Boolean
183 is_in_dialog_box (Widget w)
184 {
185   Widget wmshell;
186
187   wmshell = XtParent (w);
188   while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
189     wmshell = XtParent (wmshell);
190
191   if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
192     return True;
193   else
194     return False;
195 }
196
197 #endif /* LWLIB_DIALOGS_MOTIF */
198
199 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
200
201 /* update the label of anything subclass of a label */
202 static void
203 xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
204 {
205   XmString built_string = NULL;
206   XmString key_string   = NULL;
207   XmString val_string   = NULL;
208   XmString name_string  = NULL;
209   Arg al [20];
210   int ac = 0;
211   int type;
212
213   /* Don't clobber pixmap types. */
214   XtSetArg (al [0], XmNlabelType, &type);
215   XtGetValues (widget, al, 1);
216
217   if (type == XmPIXMAP)
218     return;
219
220   if (val->value)
221     {
222 #ifdef LWLIB_DIALOGS_MOTIF
223       /*
224        * Sigh.  The main text of a label is the name field for menubar
225        * entries.  The value field is a possible additional field to be
226        * concatenated on to the name field.  HOWEVER, with dialog boxes
227        * the value field is the complete text which is supposed to be
228        * displayed as the label.  Yuck.
229        */
230       if (is_in_dialog_box (widget))
231         {
232           char *value_name = NULL;
233
234           value_name = resource_string (widget, val->value);
235           if (!value_name)
236             value_name = val->value;
237
238           built_string =
239             XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
240         }
241       else
242 #endif /* LWLIB_DIALOGS_MOTIF */
243         {
244           char *value_name = NULL;
245           char *res_name = NULL;
246
247           res_name = resource_string (widget, val->name);
248           /* Concatenating the value with itself seems just plain daft. */
249           if (!res_name)
250             {
251               built_string =
252                 XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
253             }
254           else
255             {
256               name_string =
257                 XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);
258               
259               value_name = XtMalloc (strlen (val->value) + 2);
260               *value_name = 0;
261               strcat (value_name, " ");
262               strcat (value_name, val->value);
263               
264               val_string =
265                 XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
266               
267               built_string =
268                 XmStringConcat (name_string, val_string);
269               
270               XtFree (value_name);
271             }
272         }
273
274       XtSetArg (al [ac], XmNlabelString, built_string); ac++;
275       XtSetArg (al [ac], XmNlabelType, XmSTRING);       ac++;
276     }
277
278   if (val->key)
279     {
280       key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
281       XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
282     }
283
284   if (ac)
285     XtSetValues (widget, al, ac);
286
287   if (built_string)
288     XmStringFree (built_string);
289
290   if (key_string)
291     XmStringFree (key_string);
292
293   if (name_string)
294     XmStringFree (name_string);
295
296   if (val_string)
297     XmStringFree (val_string);
298 }
299
300 #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */
301
302 \f/* update of list */
303 static void
304 xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
305 {
306   widget_value* cur;
307   int i;
308   XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
309   XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
310                  instance);
311   for (cur = val->contents, i = 0; cur; cur = cur->next)
312     if (cur->value)
313       {
314         XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
315         i += 1;
316         XmListAddItem (widget, xmstr, 0);
317         if (cur->selected)
318           XmListSelectPos (widget, i, False);
319         XmStringFree (xmstr);
320       }
321 }
322
323 \f/* update of buttons */
324 static void
325 xm_update_pushbutton (widget_instance* instance, Widget widget,
326                       widget_value* val)
327 {
328   Arg al [1];
329   XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
330   XtSetValues (widget, al, 1);
331   XtRemoveAllCallbacks (widget, XmNactivateCallback);
332   XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
333 }
334
335 #ifdef LWLIB_MENUBARS_MOTIF
336
337 static void
338 xm_update_cascadebutton (widget_instance* instance, Widget widget,
339                          widget_value* val)
340 {
341   /* Should also rebuild the menu by calling ...update_menu... */
342   if (val
343       && val->type == CASCADE_TYPE
344       && val->contents
345       && val->contents->type == INCREMENTAL_TYPE)
346     {
347       /* okay, we're now doing a lisp callback to incrementally generate
348          more of the menu. */
349       XtRemoveAllCallbacks (widget, XmNcascadingCallback);
350       XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
351                      instance);
352       XtCallCallbacks ((Widget)widget,
353                       XmNcascadingCallback,
354                       (XtPointer)val->contents);
355
356     } else {
357       XtRemoveAllCallbacks (widget, XmNcascadingCallback);
358       XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
359                      instance);
360     }
361 }
362
363 #endif /* LWLIB_MENUBARS_MOTIF */
364
365 \f/* update toggle and radiobox */
366 static void
367 xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
368 {
369   Arg al [2];
370   XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
371   XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
372                  instance);
373   XtSetArg (al [0], XmNset, val->selected);
374   XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
375   XtSetValues (widget, al, 1);
376 }
377
378 static void
379 xm_update_radiobox (widget_instance* instance, Widget widget,
380                     widget_value* val)
381 {
382   Widget toggle;
383   widget_value* cur;
384
385   /* update the callback */
386   XtRemoveAllCallbacks (widget, XmNentryCallback);
387   XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
388
389   /* first update all the toggles */
390   /* Energize kernel interface is currently bad.  It sets the selected widget
391      with the selected flag but returns it by its name.  So we currently
392      have to support both setting the selection with the selected slot
393      of val contents and setting it with the "value" slot of val.  The latter
394      has a higher priority.  This to be removed when the kernel is fixed. */
395   for (cur = val->contents; cur; cur = cur->next)
396     {
397       toggle = XtNameToWidget (widget, cur->value);
398       if (toggle)
399         {
400           Arg al [2];
401           XtSetArg (al [0], XmNsensitive, cur->enabled);
402           XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
403           XtSetValues (toggle, al, 2);
404         }
405     }
406
407   /* The selected was specified by the value slot */
408   if (val->value)
409     {
410       toggle = XtNameToWidget (widget, val->value);
411       if (toggle)
412         {
413           Arg al [1];
414           XtSetArg (al [0], XmNset, True);
415           XtSetValues (toggle, al, 1);
416         }
417     }
418 }
419
420 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
421 /* update of combo box */
422 static void
423 xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
424 {
425   widget_value* cur;
426   int i;
427   XtRemoveAllCallbacks (widget, XmNselectionCallback);
428   XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
429                  instance);
430   for (cur = val->contents, i = 0; cur; cur = cur->next)
431     if (cur->value)
432       {
433         XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
434         i += 1;
435         XmListAddItem (CB_List (widget), xmstr, 0);
436         if (cur->selected)
437           XmListSelectPos (CB_List (widget), i, False);
438         XmStringFree (xmstr);
439       }
440 }
441 #endif
442
443 #ifdef LWLIB_MENUBARS_MOTIF
444
445 \f/* update a popup menu, pulldown menu or a menubar */
446 static void
447 make_menu_in_widget (widget_instance* instance, Widget widget,
448                      widget_value* val)
449 {
450   Widget* children = 0;
451   int num_children;
452   int child_index;
453   widget_value* cur;
454   Widget button = 0;
455   Widget menu;
456   Arg al [256];
457   int ac;
458   Boolean menubar_p = False;
459
460   /* Allocate the children array */
461   for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
462   children = (Widget*)XtMalloc (num_children * sizeof (Widget));
463
464   /* tricky way to know if this RowColumn is a menubar or a pulldown... */
465   XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
466   XtGetValues (widget, al, 1);
467
468   /* add the unmap callback for popups and pulldowns */
469   /*** this sounds bogus ***/
470   /* probably because it is -- cet */
471 /*
472   if (!menubar_p)
473     XtAddCallback (XtParent (widget), XmNpopdownCallback,
474                    xm_pop_down_callback, (XtPointer)instance);
475 */
476
477   num_children = 0;
478   for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
479     {
480       ac = 0;
481       button = 0;
482       XtSetArg (al [ac], XmNsensitive, cur->enabled);           ac++;
483       XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);  ac++;
484       XtSetArg (al [ac], XmNuserData, cur->call_data);          ac++;
485
486       switch (cur->type)
487         {
488         case PUSHRIGHT_TYPE:
489           /* A pushright marker which is not needed for the real Motif
490              menubar. */
491           break;
492         case SEPARATOR_TYPE:
493           ac = 0;
494           if (cur->value)
495             {
496               /* #### - xlwmenu.h supports several types that motif does
497                  not.  Also, motif supports pixmaps w/ type NO_LINE and
498                  lwlib provides no way to access that functionality. --Stig */
499               XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
500             }
501           button = XmCreateSeparator (widget, "separator", al, ac);
502           break;
503         case CASCADE_TYPE:
504           menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
505           make_menu_in_widget (instance, menu, cur->contents);
506           XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
507           button = XmCreateCascadeButton (widget, cur->name, al, ac);
508
509           xm_update_label (instance, button, cur);
510
511           XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
512                          (XtPointer)instance);
513           break;
514         default:
515           if (menubar_p)
516             button = XmCreateCascadeButton (widget, cur->name, al, ac);
517           else if (!cur->call_data)
518             button = XmCreateLabel (widget, cur->name, al, ac);
519           else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
520             {
521               XtSetArg (al [ac], XmNindicatorType,
522                         (cur->type == TOGGLE_TYPE ?
523                          XmN_OF_MANY : XmONE_OF_MANY));    ac++;
524               XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
525               button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
526             }
527           else
528             button = XmCreatePushButtonGadget (widget, cur->name, al, ac);
529
530           xm_update_label (instance, button, cur);
531
532           /* don't add a callback to a simple label */
533           if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
534             xm_update_toggle (instance, button, cur);
535           else if (cur->call_data)
536             XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
537                            (XtPointer)instance);
538         } /* switch (cur->type) */
539
540       if (button)
541         children [num_children++] = button;
542     }
543
544   /* Last entry is the help button.  This used be done after managing
545      the buttons.  The comment claimed that it had to be done this way
546      otherwise the menubar ended up only 4 pixels high.  That must
547      have been in the Old World.  In the New World it stays the proper
548      height if you don't manage them until after you set this and as a
549      bonus the Help menu ends up where it is supposed to. */
550   if (button)
551     {
552       ac = 0;
553       XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
554       XtSetValues (widget, al, ac);
555     }
556
557   if (num_children)
558     XtManageChildren (children, num_children);
559
560   XtFree ((char *) children);
561 }
562
563 static void
564 update_one_menu_entry (widget_instance* instance, Widget widget,
565                        widget_value* val, Boolean deep_p)
566 {
567   Arg al [2];
568   int ac;
569   Widget menu;
570   widget_value* contents;
571
572   if (val->change == NO_CHANGE)
573     return;
574
575   /* update the sensitivity and userdata */
576   /* Common to all widget types */
577   XtSetArg (al [0], XmNsensitive, val->enabled);
578   XtSetArg (al [1], XmNuserData,  val->call_data);
579   XtSetValues (widget, al, 2);
580
581   /* update the menu button as a label. */
582   if (val->change >= VISIBLE_CHANGE)
583     {
584       xm_update_label (instance, widget, val);
585       if (XtClass (widget) == xmToggleButtonWidgetClass
586           || XtClass (widget) == xmToggleButtonGadgetClass)
587         {
588           xm_update_toggle (instance, widget, val);
589         }
590     }
591
592
593   /* update the pulldown/pullaside as needed */
594   menu = NULL;
595   XtSetArg (al [0], XmNsubMenuId, &menu);
596   XtGetValues (widget, al, 1);
597
598   contents = val->contents;
599
600   if (!menu)
601     {
602       if (contents)
603         {
604           menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
605           make_menu_in_widget (instance, menu, contents);
606           ac = 0;
607           XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
608           XtSetValues (widget, al, ac);
609         }
610     }
611   else if (!contents)
612     {
613       ac = 0;
614       XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
615       XtSetValues (widget, al, ac);
616       XtDestroyWidget (menu);
617     }
618   else if (deep_p && contents->change != NO_CHANGE)
619     xm_update_menu (instance, menu, val, 1);
620 }
621
622 static void
623 xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
624                 Boolean deep_p)
625 {
626   /* Widget is a RowColumn widget whose contents have to be updated
627    * to reflect the list of items in val->contents */
628   if (val->contents->change == STRUCTURAL_CHANGE)
629     {
630       destroy_all_children (widget);
631       make_menu_in_widget (instance, widget, val->contents);
632     }
633   else
634     {
635       /* Update all the buttons of the RowColumn in order. */
636       Widget* children;
637       unsigned int num_children;
638       int i;
639       widget_value *cur = 0;
640
641       children = XtCompositeChildren (widget, &num_children);
642       if (children)
643         {
644           for (i = 0, cur = val->contents; i < num_children; i++)
645             {
646               if (!cur)
647                 abort ();
648               /* skip if this is a pushright marker or a separator */
649               if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
650                 {
651                   cur = cur->next;
652 #if 0
653                   /* #### - this could puke if you have a separator as the
654                      last item on a pullright menu. */
655                   if (!cur)
656                     abort ();
657 #else
658                   if (!cur)
659                     continue;
660 #endif
661                 }
662               if (children [i]->core.being_destroyed
663                   || strcmp (XtName (children [i]), cur->name))
664                 continue;
665               update_one_menu_entry (instance, children [i], cur, deep_p);
666               cur = cur->next;
667             }
668           XtFree ((char *) children);
669         }
670       if (cur)
671         abort ();
672     }
673 }
674
675 #endif /* LWLIB_MENUBARS_MOTIF */
676
677 \f
678 /* update text widgets */
679
680 static void
681 xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
682 {
683   XmTextSetString (widget, val->value ? val->value : (char *) "");
684   XtRemoveAllCallbacks (widget, XmNactivateCallback);
685   XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
686   XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
687   XtAddCallback (widget, XmNvalueChangedCallback,
688                  xm_internal_update_other_instances, instance);
689 }
690
691 static void
692 xm_update_text_field (widget_instance* instance, Widget widget,
693                       widget_value* val)
694 {
695   XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
696   XtRemoveAllCallbacks (widget, XmNactivateCallback);
697   XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
698   XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
699   XtAddCallback (widget, XmNvalueChangedCallback,
700                  xm_internal_update_other_instances, instance);
701 }
702
703
704 #ifdef LWLIB_SCROLLBARS_MOTIF
705
706 /*
707  * If this function looks like it does a lot more work than it needs to,
708  * you're right.  Blame the Motif scrollbar for not being smart about
709  * updating its appearance.
710  */
711 static void
712 xm_update_scrollbar (widget_instance *instance, Widget widget,
713                      widget_value *val)
714 {
715   if (val->scrollbar_data)
716     {
717       scrollbar_values *data = val->scrollbar_data;
718       int widget_sliderSize, widget_val;
719       int new_sliderSize, new_value;
720       double percent;
721       double h_water, l_water;
722       Arg al [4];
723
724       /* First size and position the scrollbar widget. */
725       XtSetArg (al [0], XtNx,      data->scrollbar_x);
726       XtSetArg (al [1], XtNy,      data->scrollbar_y);
727       XtSetArg (al [2], XtNwidth,  data->scrollbar_width);
728       XtSetArg (al [3], XtNheight, data->scrollbar_height);
729       XtSetValues (widget, al, 4);
730
731       /* Now size the scrollbar's slider. */
732       XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
733       XtSetArg (al [1], XmNvalue,      &widget_val);
734       XtGetValues (widget, al, 2);
735
736       percent = (double) data->slider_size /
737         (double) (data->maximum - data->minimum);
738       new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);
739
740       percent = (double) (data->slider_position - data->minimum) /
741         (double) (data->maximum - data->minimum);
742       new_value = (int) ((double) (INT_MAX - 1) * percent);
743
744       if (new_sliderSize > (INT_MAX - 1))
745         new_sliderSize = INT_MAX - 1;
746       else if (new_sliderSize < 1)
747         new_sliderSize = 1;
748
749       if (new_value > (INT_MAX - new_sliderSize))
750         new_value = INT_MAX - new_sliderSize;
751       else if (new_value < 1)
752         new_value = 1;
753
754       h_water = 1.05;
755       l_water = 0.95;
756       if (new_sliderSize != widget_sliderSize || new_value != widget_val)
757         {
758           int force = ((INT_MAX - widget_sliderSize - widget_val)
759                        ? 0
760                        : (INT_MAX - new_sliderSize - new_value));
761
762           if (force
763               || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
764               || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
765               || (double)new_value < (l_water * (double)widget_val)
766               || (double)new_value > (h_water * (double)widget_val))
767             {
768               XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
769                                     False);
770             }
771         }
772     }
773 }
774
775 #endif /* LWLIB_SCROLLBARS_MOTIF */
776
777 \f
778 /* update a motif widget */
779
780 void
781 xm_update_one_widget (widget_instance* instance, Widget widget,
782                       widget_value* val, Boolean deep_p)
783 {
784   WidgetClass class;
785   Arg al [20];
786   int ac = 0;
787
788   /* Mark as not edited */
789   val->edited = False;
790
791   /* Common to all widget types */
792   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
793   XtSetArg (al [ac], XmNuserData,  val->call_data);     ac++;
794   lw_add_value_args_to_args (val, al, &ac);
795
796   XtSetValues (widget, al, ac);
797
798 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
799   /* Common to all label like widgets */
800   if (XtIsSubclass (widget, xmLabelWidgetClass))
801     xm_update_label (instance, widget, val);
802 #endif
803   class = XtClass (widget);
804   /* Class specific things */
805   if (class == xmPushButtonWidgetClass ||
806       class == xmArrowButtonWidgetClass)
807     {
808       xm_update_pushbutton (instance, widget, val);
809     }
810 #ifdef LWLIB_MENUBARS_MOTIF
811   else if (class == xmCascadeButtonWidgetClass)
812     {
813       xm_update_cascadebutton (instance, widget, val);
814     }
815 #endif
816   else if (class == xmToggleButtonWidgetClass
817            || class == xmToggleButtonGadgetClass)
818     {
819       xm_update_toggle (instance, widget, val);
820     }
821   else if (class == xmRowColumnWidgetClass)
822     {
823       Boolean radiobox = 0;
824
825       XtSetArg (al [0], XmNradioBehavior, &radiobox);
826       XtGetValues (widget, al, 1);
827
828       if (radiobox)
829         xm_update_radiobox (instance, widget, val);
830 #ifdef LWLIB_MENUBARS_MOTIF
831       else
832         xm_update_menu (instance, widget, val, deep_p);
833 #endif
834     }
835   else if (class == xmTextWidgetClass)
836     {
837       xm_update_text (instance, widget, val);
838     }
839   else if (class == xmTextFieldWidgetClass)
840     {
841       xm_update_text_field (instance, widget, val);
842     }
843   else if (class == xmListWidgetClass)
844     {
845       xm_update_list (instance, widget, val);
846     }
847 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
848   else if (class == xmComboBoxWidgetClass)
849     {
850       xm_update_combo_box (instance, widget, val);
851     }
852 #endif
853 #ifdef LWLIB_SCROLLBARS_MOTIF
854   else if (class == xmScrollBarWidgetClass)
855     {
856       xm_update_scrollbar (instance, widget, val);
857     }
858 #endif
859 }
860
861 \f/* getting the value back */
862 void
863 xm_update_one_value (widget_instance* instance, Widget widget,
864                      widget_value* val)
865 {
866   WidgetClass class = XtClass (widget);
867   widget_value *old_wv;
868
869   /* copy the call_data slot into the "return" widget_value */
870   for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
871     if (!strcmp (val->name, old_wv->name))
872       {
873         val->call_data = old_wv->call_data;
874         break;
875       }
876
877   if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
878     {
879       Arg al [1];
880       XtSetArg (al [0], XmNset, &val->selected);
881       XtGetValues (widget, al, 1);
882       val->edited = True;
883     }
884   else if (class == xmTextWidgetClass)
885     {
886       if (val->value)
887         free (val->value);
888       val->value = XmTextGetString (widget);
889       val->edited = True;
890     }
891   else if (class == xmTextFieldWidgetClass)
892     {
893       if (val->value)
894         free (val->value);
895       val->value = XmTextFieldGetString (widget);
896       val->edited = True;
897     }
898   else if (class == xmRowColumnWidgetClass)
899     {
900       Boolean radiobox = 0;
901       {
902         Arg al [1];
903         XtSetArg (al [0], XmNradioBehavior, &radiobox);
904         XtGetValues (widget, al, 1);
905       }
906
907       if (radiobox)
908         {
909           CompositeWidget radio = (CompositeWidget)widget;
910           int i;
911           for (i = 0; i < radio->composite.num_children; i++)
912             {
913               int set = False;
914               Widget toggle = radio->composite.children [i];
915               Arg al [1];
916
917               XtSetArg (al [0], XmNset, &set);
918               XtGetValues (toggle, al, 1);
919               if (set)
920                 {
921                   if (val->value)
922                     free (val->value);
923                   val->value = safe_strdup (XtName (toggle));
924                 }
925             }
926           val->edited = True;
927         }
928     }
929   else if (class == xmListWidgetClass 
930 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
931            || class == xmComboBoxWidgetClass
932 #endif
933            )
934     {
935       int pos_cnt;
936       int* pos_list;
937       Widget list = widget;
938 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
939       if (class == xmComboBoxWidgetClass)
940         list = CB_List (widget);
941 #endif
942       if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
943         {
944           int i;
945           widget_value* cur;
946           for (cur = val->contents, i = 0; cur; cur = cur->next)
947             if (cur->value)
948               {
949                 int j;
950                 cur->selected = False;
951                 i += 1;
952                 for (j = 0; j < pos_cnt; j++)
953                   if (pos_list [j] == i)
954                     {
955                       cur->selected = True;
956                       val->value = safe_strdup (cur->name);
957                     }
958               }
959           val->edited = 1;
960           XtFree ((char *) pos_list);
961         }
962     }
963 #ifdef LWLIB_SCROLLBARS_MOTIF
964   else if (class == xmScrollBarWidgetClass)
965     {
966       /* This function is not used by the scrollbar. */
967       return;
968     }
969 #endif
970 }
971
972 \f
973 /* This function is for activating a button from a program.  It's wrong because
974    we pass a NULL argument in the call_data which is not Motif compatible.
975    This is used from the XmNdefaultAction callback of the List widgets to
976    have a double-click put down a dialog box like the button would do.
977    I could not find a way to do that with accelerators.
978  */
979 static void
980 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
981 {
982   Widget button = (Widget)closure;
983   XtCallCallbacks (button, XmNactivateCallback, NULL);
984 }
985
986 /* creation functions */
987
988 #ifdef LWLIB_DIALOGS_MOTIF
989
990 /* dialogs */
991
992 #if (XmVersion >= 1002)
993 # define ARMANDACTIVATE_KLUDGE
994 # define DND_KLUDGE
995 #endif
996
997 #ifdef ARMANDACTIVATE_KLUDGE
998  /* We want typing Return at a dialog box to select the default button; but
999     we're satisfied with having it select the leftmost button instead.
1000
1001     In Motif 1.1.5 we could do this by putting this resource in the
1002     app-defaults file:
1003
1004         *dialog*button1.accelerators:#override\
1005         <KeyPress>Return: ArmAndActivate()\n\
1006         <KeyPress>KP_Enter: ArmAndActivate()\n\
1007         Ctrl<KeyPress>m: ArmAndActivate()\n
1008
1009     but that doesn't work with 1.2.1 and I don't understand why. However,
1010     doing the equivalent C code does work, with the notable disadvantage that
1011     the user can't override it.  So that's what we do until we figure out
1012     something better....
1013   */
1014 static char button_trans[] = "\
1015 <KeyPress>Return: ArmAndActivate()\n\
1016 <KeyPress>KP_Enter: ArmAndActivate()\n\
1017 Ctrl<KeyPress>m: ArmAndActivate()\n";
1018
1019 #endif /* ARMANDACTIVATE_KLUDGE */
1020
1021
1022 #ifdef DND_KLUDGE
1023  /* This is a kludge to disable drag-and-drop in dialog boxes.  The symptom
1024     was a segv down in libXm somewhere if you used the middle button on a
1025     dialog box to begin a drag; when you released the button to make a drop
1026     things would lose if you were not over the button where you started the
1027     drag (canceling the operation).  This was probably due to the fact that
1028     the dialog boxes were not set up to handle a drag but were trying to do
1029     so anyway for some reason.
1030
1031     So we disable drag-and-drop in dialog boxes by turning off the binding for
1032     Btn2Down which, by default, initiates a drag.  Clearly this is a shitty
1033     solution as it only works in default configurations, but...
1034   */
1035 static char disable_dnd_trans[] = "<Btn2Down>: ";
1036 #endif /* DND_KLUDGE */
1037
1038
1039 static Widget
1040 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1041              const char* shell_title, const char* icon_name,
1042              Boolean text_input_slot, Boolean radio_box, Boolean list,
1043              int left_buttons, int right_buttons)
1044 {
1045   Widget result;
1046   Widget form;
1047   Widget row;
1048   Widget icon;
1049   Widget icon_separator;
1050   Widget message;
1051   Widget value = 0;
1052   Widget separator;
1053   Widget button = 0;
1054   Widget children [16];         /* for the final XtManageChildren */
1055   int   n_children;
1056   Arg   al[64];                 /* Arg List */
1057   int   ac;                     /* Arg Count */
1058   int   i;
1059
1060 #ifdef DND_KLUDGE
1061   XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1062 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1063 #else  /* ! DND_KLUDGE */
1064 # define DO_DND_KLUDGE(widget)
1065 #endif /* ! DND_KLUDGE */
1066
1067   if (pop_up_p)
1068     {
1069       ac = 0;
1070       XtSetArg(al[ac], XmNtitle, shell_title);          ac++;
1071       XtSetArg(al[ac], XtNallowShellResize, True);      ac++;
1072       XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP);     ac++;
1073       result = XmCreateDialogShell (parent, "dialog", al, ac);
1074
1075       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1076 /*      XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1077       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1078       form = XmCreateForm (result, (char *) shell_title, al, ac);
1079     }
1080   else
1081     {
1082       ac = 0;
1083       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1084       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1085       form = XmCreateForm (parent, (char *) shell_title, al, ac);
1086       result = form;
1087     }
1088
1089   ac = 0;
1090   XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN);          ac++;
1091   XtSetArg(al[ac], XmNorientation, XmVERTICAL);         ac++;
1092   XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1093   XtSetArg(al[ac], XmNmarginWidth, 0);                  ac++;
1094   XtSetArg(al[ac], XmNmarginHeight, 0);                 ac++;
1095   XtSetArg(al[ac], XmNspacing, 13);                     ac++;
1096   XtSetArg(al[ac], XmNadjustLast, False);               ac++;
1097   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);   ac++;
1098   XtSetArg(al[ac], XmNisAligned, True);                 ac++;
1099   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);    ac++;
1100   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1101   XtSetArg(al[ac], XmNbottomOffset, 13);                ac++;
1102   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);   ac++;
1103   XtSetArg(al[ac], XmNleftOffset, 13);                  ac++;
1104   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);  ac++;
1105   XtSetArg(al[ac], XmNrightOffset, 13);                 ac++;
1106   row = XmCreateRowColumn (form, "row", al, ac);
1107
1108   n_children = 0;
1109   for (i = 0; i < left_buttons; i++)
1110     {
1111       char button_name [16];
1112       sprintf (button_name, "button%d", i + 1);
1113       ac = 0;
1114       if (i == 0)
1115         {
1116           XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1117           XtSetArg(al[ac], XmNshowAsDefault, TRUE);   ac++;
1118         }
1119       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1120       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1121       DO_DND_KLUDGE (children [n_children]);
1122
1123       if (i == 0)
1124         {
1125           button = children [n_children];
1126           ac = 0;
1127           XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1128           XtSetValues (row, al, ac);
1129
1130 #ifdef ARMANDACTIVATE_KLUDGE    /* See comment above */
1131           {
1132             XtTranslations losers = XtParseTranslationTable (button_trans);
1133             XtOverrideTranslations (button, losers);
1134             XtFree ((char *) losers);
1135           }
1136 #endif /* ARMANDACTIVATE_KLUDGE */
1137         }
1138
1139       n_children++;
1140     }
1141
1142   /* invisible seperator button */
1143   ac = 0;
1144   XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1145   children [n_children] = XmCreateLabel (row, "separator_button",
1146                                          al, ac);
1147   DO_DND_KLUDGE (children [n_children]);
1148   n_children++;
1149
1150   for (i = 0; i < right_buttons; i++)
1151     {
1152       char button_name [16];
1153       sprintf (button_name, "button%d", left_buttons + i + 1);
1154       ac = 0;
1155       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1156       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1157       DO_DND_KLUDGE (children [n_children]);
1158       if (! button) button = children [n_children];
1159       n_children++;
1160     }
1161
1162   XtManageChildren (children, n_children);
1163
1164   ac = 0;
1165   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);            ac++;
1166   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1167   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1168   XtSetArg(al[ac], XmNbottomWidget, row);                       ac++;
1169   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1170   XtSetArg(al[ac], XmNleftOffset, 0);                           ac++;
1171   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1172   XtSetArg(al[ac], XmNrightOffset, 0);                          ac++;
1173   separator = XmCreateSeparator (form, "", al, ac);
1174
1175   ac = 0;
1176   XtSetArg(al[ac], XmNlabelType, XmPIXMAP);                     ac++;
1177   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1178   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1179   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);         ac++;
1180   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1181   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1182   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1183   icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1184   DO_DND_KLUDGE (icon);
1185
1186   ac = 0;
1187   XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);                ac++;
1188   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);          ac++;
1189   XtSetArg(al[ac], XmNtopOffset, 6);                            ac++;
1190   XtSetArg(al[ac], XmNtopWidget, icon);                         ac++;
1191   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1192   XtSetArg(al[ac], XmNbottomOffset, 6);                         ac++;
1193   XtSetArg(al[ac], XmNbottomWidget, separator);                 ac++;
1194   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);           ac++;
1195   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1196   icon_separator = XmCreateLabel (form, "", al, ac);
1197   DO_DND_KLUDGE (icon_separator);
1198
1199   if (text_input_slot)
1200     {
1201       ac = 0;
1202       XtSetArg(al[ac], XmNcolumns, 50);                         ac++;
1203       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1204       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1205       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1206       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1207       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1208       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1209       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1210       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1211       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1212       value = XmCreateTextField (form, "value", al, ac);
1213       DO_DND_KLUDGE (value);
1214     }
1215   else if (radio_box)
1216     {
1217       Widget radio_butt;
1218       ac = 0;
1219       XtSetArg(al[ac], XmNmarginWidth, 0);                      ac++;
1220       XtSetArg(al[ac], XmNmarginHeight, 0);                     ac++;
1221       XtSetArg(al[ac], XmNspacing, 13);                         ac++;
1222       XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);       ac++;
1223       XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);           ac++;
1224       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1225       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1226       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1227       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1228       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1229       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1230       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1231       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1232       value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1233       ac = 0;
1234       i = 0;
1235       radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1236       children [i++] = radio_butt;
1237       radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1238       children [i++] = radio_butt;
1239       radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1240       children [i++] = radio_butt;
1241       XtManageChildren (children, i);
1242     }
1243   else if (list)
1244     {
1245       ac = 0;
1246       XtSetArg(al[ac], XmNvisibleItemCount, 5);                 ac++;
1247       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1248       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1249       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1250       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1251       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1252       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1253       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1254       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1255       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1256       value = XmCreateScrolledList (form, "list", al, ac);
1257
1258       /* this is the easiest way I found to have the dble click in the
1259          list activate the default button */
1260       XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1261     }
1262
1263   ac = 0;
1264   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);        ac++;
1265   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1266   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1267   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1268   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1269   XtSetArg(al[ac], XmNbottomWidget,
1270            text_input_slot || radio_box || list ? value : separator); ac++;
1271   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);         ac++;
1272   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1273   XtSetArg(al[ac], XmNleftWidget, icon);                        ac++;
1274   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1275   XtSetArg(al[ac], XmNrightOffset, 13);                         ac++;
1276   message = XmCreateLabel (form, "message", al, ac);
1277   DO_DND_KLUDGE (message);
1278
1279   if (list)
1280     XtManageChild (value);
1281
1282   i = 0;
1283   children [i] = row; i++;
1284   children [i] = separator; i++;
1285   if (text_input_slot || radio_box)
1286     {
1287       children [i] = value; i++;
1288     }
1289   children [i] = message; i++;
1290   children [i] = icon; i++;
1291   children [i] = icon_separator; i++;
1292   XtManageChildren (children, i);
1293
1294   if (text_input_slot || list)
1295     {
1296       XtInstallAccelerators (value, button);
1297       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1298     }
1299   else
1300     {
1301       XtInstallAccelerators (form, button);
1302       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1303     }
1304
1305 #ifdef DND_KLUDGE
1306   XtFree ((char *) dnd_override);
1307 #endif
1308 #undef DO_DND_KLUDGE
1309
1310   return result;
1311 }
1312
1313 static destroyed_instance*
1314 find_matching_instance (widget_instance* instance)
1315 {
1316   destroyed_instance*   cur;
1317   destroyed_instance*   prev;
1318   char* type = instance->info->type;
1319   char* name = instance->info->name;
1320
1321   for (prev = NULL, cur = all_destroyed_instances;
1322        cur;
1323        prev = cur, cur = cur->next)
1324     {
1325       if (!strcmp (cur->name, name)
1326           && !strcmp (cur->type, type)
1327           && cur->parent == instance->parent
1328           && cur->pop_up_p == instance->pop_up_p)
1329         {
1330           if (prev)
1331             prev->next = cur->next;
1332           else
1333             all_destroyed_instances = cur->next;
1334           return cur;
1335         }
1336       /* do some cleanup */
1337       else if (!cur->widget)
1338         {
1339           if (prev)
1340             prev->next = cur->next;
1341           else
1342             all_destroyed_instances = cur->next;
1343           free_destroyed_instance (cur);
1344           cur = prev ? prev : all_destroyed_instances;
1345         }
1346     }
1347   return NULL;
1348 }
1349
1350 static void
1351 recenter_widget (Widget widget)
1352 {
1353   Widget parent = XtParent (widget);
1354   Screen* screen = XtScreen (widget);
1355   Dimension screen_width = WidthOfScreen (screen);
1356   Dimension screen_height = HeightOfScreen (screen);
1357   Dimension parent_width = 0;
1358   Dimension parent_height = 0;
1359   Dimension child_width = 0;
1360   Dimension child_height = 0;
1361   Position x;
1362   Position y;
1363   Arg al [2];
1364
1365   XtSetArg (al [0], XtNwidth,  &child_width);
1366   XtSetArg (al [1], XtNheight, &child_height);
1367   XtGetValues (widget, al, 2);
1368
1369   XtSetArg (al [0], XtNwidth,  &parent_width);
1370   XtSetArg (al [1], XtNheight, &parent_height);
1371   XtGetValues (parent, al, 2);
1372
1373   x = (Position) ((parent_width  - child_width)  / 2);
1374   y = (Position) ((parent_height - child_height) / 2);
1375
1376   XtTranslateCoords (parent, x, y, &x, &y);
1377
1378   if ((Dimension) (x + child_width) > screen_width)
1379     x = screen_width - child_width;
1380   if (x < 0)
1381     x = 0;
1382
1383   if ((Dimension) (y + child_height) > screen_height)
1384     y = screen_height - child_height;
1385   if (y < 0)
1386     y = 0;
1387
1388   XtSetArg (al [0], XtNx, x);
1389   XtSetArg (al [1], XtNy, y);
1390   XtSetValues (widget, al, 2);
1391 }
1392
1393 static Widget
1394 recycle_instance (destroyed_instance* instance)
1395 {
1396   Widget widget = instance->widget;
1397
1398   /* widget is NULL if the parent was destroyed. */
1399   if (widget)
1400     {
1401       Widget focus;
1402       Widget separator;
1403
1404       /* Remove the destroy callback as the instance is not in the list
1405          anymore */
1406       XtRemoveCallback (instance->parent, XtNdestroyCallback,
1407                         mark_dead_instance_destroyed,
1408                         (XtPointer)instance);
1409
1410       /* Give the focus to the initial item */
1411       focus = XtNameToWidget (widget, "*value");
1412       if (!focus)
1413         focus = XtNameToWidget (widget, "*button1");
1414       if (focus)
1415         XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1416
1417       /* shrink the separator label back to their original size */
1418       separator = XtNameToWidget (widget, "*separator_button");
1419       if (separator)
1420         {
1421           Arg al [2];
1422           XtSetArg (al [0], XtNwidth,  5);
1423           XtSetArg (al [1], XtNheight, 5);
1424           XtSetValues (separator, al, 2);
1425         }
1426
1427       /* Center the dialog in its parent */
1428       recenter_widget (widget);
1429     }
1430   free_destroyed_instance (instance);
1431   return widget;
1432 }
1433
1434 Widget
1435 xm_create_dialog (widget_instance* instance)
1436 {
1437   char*         name = instance->info->type;
1438   Widget        parent = instance->parent;
1439   Widget        widget;
1440   Boolean       pop_up_p = instance->pop_up_p;
1441   const char*   shell_name = 0;
1442   const char*   icon_name = 0;
1443   Boolean       text_input_slot = False;
1444   Boolean       radio_box = False;
1445   Boolean       list = False;
1446   int           total_buttons;
1447   int           left_buttons = 0;
1448   int           right_buttons = 1;
1449   destroyed_instance*   dead_one;
1450
1451   /* try to find a widget to recycle */
1452   dead_one = find_matching_instance (instance);
1453   if (dead_one)
1454     {
1455       Widget recycled_widget = recycle_instance (dead_one);
1456       if (recycled_widget)
1457         return recycled_widget;
1458     }
1459
1460   switch (name [0]){
1461   case 'E': case 'e':
1462     icon_name = "dbox-error";
1463     shell_name = "Error";
1464     break;
1465
1466   case 'I': case 'i':
1467     icon_name = "dbox-info";
1468     shell_name = "Information";
1469     break;
1470
1471   case 'L': case 'l':
1472     list = True;
1473     icon_name = "dbox-question";
1474     shell_name = "Prompt";
1475     break;
1476
1477   case 'P': case 'p':
1478     text_input_slot = True;
1479     icon_name = "dbox-question";
1480     shell_name = "Prompt";
1481     break;
1482
1483   case 'Q': case 'q':
1484     icon_name = "dbox-question";
1485     shell_name = "Question";
1486     break;
1487   }
1488
1489   total_buttons = name [1] - '0';
1490
1491   if (name [3] == 'T' || name [3] == 't')
1492     {
1493       text_input_slot = False;
1494       radio_box = True;
1495     }
1496   else if (name [3])
1497     right_buttons = name [4] - '0';
1498
1499   left_buttons = total_buttons - right_buttons;
1500
1501   widget = make_dialog (name, parent, pop_up_p,
1502                         shell_name, icon_name, text_input_slot, radio_box,
1503                         list, left_buttons, right_buttons);
1504
1505   XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1506                  (XtPointer) instance);
1507   return widget;
1508 }
1509
1510 #endif /* LWLIB_DIALOGS_MOTIF */
1511
1512 #ifdef LWLIB_MENUBARS_MOTIF
1513 static Widget
1514 make_menubar (widget_instance* instance)
1515 {
1516   Arg al[10];
1517   int ac = 0;
1518
1519   XtSetArg(al[ac], XmNmarginHeight, 0);    ac++;
1520   XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1521
1522   return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1523 }
1524
1525 static void
1526 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1527 {
1528   Widget menu = (Widget) closure;
1529   XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1530 }
1531
1532 static Widget
1533 make_popup_menu (widget_instance* instance)
1534 {
1535   Widget parent = instance->parent;
1536   Window parent_window = parent->core.window;
1537   Widget result;
1538
1539   /* sets the parent window to 0 to fool Motif into not generating a grab */
1540   parent->core.window = 0;
1541   result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1542   XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1543                  (XtPointer)result);
1544   parent->core.window = parent_window;
1545   return result;
1546 }
1547 #endif /* LWLIB_MENUBARS_MOTIF */
1548
1549 #ifdef LWLIB_SCROLLBARS_MOTIF
1550 static Widget
1551 make_scrollbar (widget_instance *instance, int vertical)
1552 {
1553   Arg al[20];
1554   int ac = 0;
1555   static XtCallbackRec callbacks[2] =
1556   { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1557
1558   callbacks[0].closure  = (XtPointer) instance;
1559
1560   XtSetArg (al[ac], XmNminimum,       1); ac++;
1561   XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1562   XtSetArg (al[ac], XmNincrement,     1); ac++;
1563   XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1564   XtSetArg (al[ac], XmNborderWidth,   0); ac++;
1565   XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1566
1567   XtSetArg (al[ac], XmNdecrementCallback,       callbacks); ac++;
1568   XtSetArg (al[ac], XmNdragCallback,            callbacks); ac++;
1569   XtSetArg (al[ac], XmNincrementCallback,       callbacks); ac++;
1570   XtSetArg (al[ac], XmNpageDecrementCallback,   callbacks); ac++;
1571   XtSetArg (al[ac], XmNpageIncrementCallback,   callbacks); ac++;
1572   XtSetArg (al[ac], XmNtoBottomCallback,        callbacks); ac++;
1573   XtSetArg (al[ac], XmNtoTopCallback,           callbacks); ac++;
1574   XtSetArg (al[ac], XmNvalueChangedCallback,    callbacks); ac++;
1575
1576   return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1577 }
1578
1579 static Widget
1580 make_vertical_scrollbar (widget_instance *instance)
1581 {
1582   return make_scrollbar (instance, 1);
1583 }
1584
1585 static Widget
1586 make_horizontal_scrollbar (widget_instance *instance)
1587 {
1588   return make_scrollbar (instance, 0);
1589 }
1590
1591 #endif /* LWLIB_SCROLLBARS_MOTIF */
1592
1593 #ifdef LWLIB_WIDGETS_MOTIF
1594 /* glyph widgets */
1595 static Widget
1596 xm_create_button (widget_instance *instance)
1597 {
1598   Arg al[20];
1599   int ac = 0;
1600   Widget button = 0;
1601   widget_value* val = instance->info->val;
1602
1603   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1604   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1605   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1606   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1607   /* The highlight doesn't appear to be dynamically set which makes it
1608      look ugly.  I think this may be a LessTif bug but for now we just
1609      get rid of it. */
1610   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1611
1612   /* add any args the user supplied for creation time */
1613   lw_add_value_args_to_args (val, al, &ac);
1614
1615   if (!val->call_data)
1616     button = XmCreateLabel (instance->parent, val->name, al, ac);
1617
1618   else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1619     {
1620       XtSetArg (al [ac], XmNset, val->selected);        ac++;
1621       XtSetArg (al [ac], XmNindicatorType,
1622                 (val->type == TOGGLE_TYPE ?
1623                  XmN_OF_MANY : XmONE_OF_MANY));    ac++;
1624       XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1625       button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1626       XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1627       XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1628                      (XtPointer)instance);
1629     }
1630   else
1631     {
1632       button = XmCreatePushButton (instance->parent, val->name, al, ac);
1633       XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1634                      (XtPointer)instance);
1635     }
1636
1637   XtManageChild (button);
1638
1639   return button;
1640 }
1641
1642 static Widget
1643 xm_create_progress (widget_instance *instance)
1644 {
1645   Arg al[20];
1646   int ac = 0;
1647   Widget scale = 0;
1648   widget_value* val = instance->info->val;
1649 #if 0           /* This looks too awful, although more correct. */
1650   if (!val->call_data)
1651     {
1652       XtSetArg (al [ac], XmNsensitive, False);          ac++;
1653     }
1654   else
1655     {
1656       XtSetArg (al [ac], XmNsensitive, val->enabled);           ac++;
1657     }
1658 #else
1659   XtSetArg (al [ac], XmNsensitive, True);               ac++;
1660 #endif
1661   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1662   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1663   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1664   XtSetArg (al [ac], XmNorientation, XmHORIZONTAL);     ac++;
1665   /* The highlight doesn't appear to be dynamically set which makes it
1666      look ugly.  I think this may be a LessTif bug but for now we just
1667      get rid of it. */
1668   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1669   /* add any args the user supplied for creation time */
1670   lw_add_value_args_to_args (val, al, &ac);
1671
1672   scale = XmCreateScale (instance->parent, val->name, al, ac);
1673   if (val->call_data)
1674     XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1675                    (XtPointer)instance);
1676
1677   XtManageChild (scale);
1678
1679   return scale;
1680 }
1681
1682 static Widget
1683 xm_create_text_field (widget_instance *instance)
1684 {
1685   Arg al[20];
1686   int ac = 0;
1687   Widget text = 0;
1688   widget_value* val = instance->info->val;
1689
1690   XtSetArg (al [ac], XmNsensitive, val->enabled && val->call_data);             ac++;
1691   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1692   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1693   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1694   /* The highlight doesn't appear to be dynamically set which makes it
1695      look ugly.  I think this may be a LessTif bug but for now we just
1696      get rid of it. */
1697   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1698
1699   /* add any args the user supplied for creation time */
1700   lw_add_value_args_to_args (val, al, &ac);
1701
1702   text = XmCreateTextField (instance->parent, val->name, al, ac);
1703   if (val->call_data)
1704     XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1705                    (XtPointer)instance);
1706
1707   XtManageChild (text);
1708
1709   return text;
1710 }
1711
1712 static Widget
1713 xm_create_label_field (widget_instance *instance)
1714 {
1715   return xm_create_label (instance->parent, instance->info->val);
1716 }
1717
1718 Widget
1719 xm_create_label (Widget parent, widget_value* val)
1720 {
1721   Arg al[20];
1722   int ac = 0;
1723   Widget label = 0;
1724
1725   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1726   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1727   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1728   /* The highlight doesn't appear to be dynamically set which makes it
1729      look ugly.  I think this may be a LessTif bug but for now we just
1730      get rid of it. */
1731   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1732
1733   /* add any args the user supplied for creation time */
1734   lw_add_value_args_to_args (val, al, &ac);
1735
1736   label = XmCreateLabel (parent, val->name, al, ac);
1737
1738   XtManageChild (label);
1739
1740   /* Do it again for arguments that have no effect until the widget is realized. */
1741   ac = 0;
1742   lw_add_value_args_to_args (val, al, &ac);
1743   XtSetValues (label, al, ac);
1744
1745   return label;
1746 }
1747
1748 #if XmVERSION > 1
1749 static Widget
1750 xm_create_combo_box (widget_instance *instance)
1751 {
1752   Arg al[20];
1753   int ac = 0;
1754   Widget combo = 0;
1755   widget_value* val = instance->info->val;
1756
1757   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1758   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1759   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1760   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1761   /* The highlight doesn't appear to be dynamically set which makes it
1762      look ugly.  I think this may be a LessTif bug but for now we just
1763      get rid of it. */
1764   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1765
1766   /* add any args the user supplied for creation time */
1767   lw_add_value_args_to_args (val, al, &ac);
1768
1769   combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1770   if (val->call_data)
1771     XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1772                    (XtPointer)instance);
1773
1774   XtManageChild (combo);
1775
1776   return combo;
1777 }
1778 #endif
1779 #endif /* LWLIB_WIDGETS_MOTIF */
1780
1781 \f
1782 /* Table of functions to create widgets */
1783
1784 widget_creation_entry
1785 xm_creation_table [] =
1786 {
1787 #ifdef LWLIB_MENUBARS_MOTIF
1788   {"menubar",                   make_menubar},
1789   {"popup",                     make_popup_menu},
1790 #endif
1791 #ifdef LWLIB_SCROLLBARS_MOTIF
1792   {"vertical-scrollbar",        make_vertical_scrollbar},
1793   {"horizontal-scrollbar",      make_horizontal_scrollbar},
1794 #endif
1795 #ifdef LWLIB_WIDGETS_MOTIF
1796   {"button",            xm_create_button},
1797   {"progress",          xm_create_progress},
1798   {"text-field",                xm_create_text_field},
1799   {"label",             xm_create_label_field},
1800 #if XmVERSION > 1
1801   {"combo-box",         xm_create_combo_box},
1802 #endif
1803 #endif
1804   {NULL, NULL}
1805 };
1806
1807 \f/* Destruction of instances */
1808 void
1809 xm_destroy_instance (widget_instance* instance)
1810 {
1811 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1812   /* It appears that this is used only for dialog boxes. */
1813   Widget widget = instance->widget;
1814   /* recycle the dialog boxes */
1815   /* Disable the recycling until we can find a way to have the dialog box
1816      get reasonable layout after we modify its contents. */
1817   if (0
1818       && XtClass (widget) == xmDialogShellWidgetClass)
1819     {
1820       destroyed_instance* dead_instance =
1821         make_destroyed_instance (instance->info->name,
1822                                  instance->info->type,
1823                                  instance->widget,
1824                                  instance->parent,
1825                                  instance->pop_up_p);
1826       dead_instance->next = all_destroyed_instances;
1827       all_destroyed_instances = dead_instance;
1828       XtUnmanageChild (first_child (instance->widget));
1829       XFlush (XtDisplay (instance->widget));
1830       XtAddCallback (instance->parent, XtNdestroyCallback,
1831                      mark_dead_instance_destroyed, (XtPointer)dead_instance);
1832     }
1833   else
1834     {
1835       /* This might not be necessary now that the nosel is attached to
1836          popdown instead of destroy, but it can't hurt. */
1837       XtRemoveCallback (instance->widget, XtNdestroyCallback,
1838                         xm_nosel_callback, (XtPointer)instance);
1839
1840       XtDestroyWidget (instance->widget);
1841     }
1842 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1843 }
1844
1845 \f/* popup utility */
1846 #ifdef LWLIB_MENUBARS_MOTIF
1847
1848 void
1849 xm_popup_menu (Widget widget, XEvent *event)
1850 {
1851   if (event->type == ButtonPress || event->type == ButtonRelease)
1852     {
1853       /* This is so totally ridiculous: there's NO WAY to tell Motif
1854          that *any* button can select a menu item.  Only one button
1855          can have that honor.
1856        */
1857       char *trans = 0;
1858       if      (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1859       else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1860       else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1861       else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1862       else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1863       if (trans)
1864         {
1865           Arg al [1];
1866           XtSetArg (al [0], XmNmenuPost, trans);
1867           XtSetValues (widget, al, 1);
1868         }
1869       XmMenuPosition (widget, (XButtonPressedEvent *) event);
1870     }
1871   XtManageChild (widget);
1872 }
1873
1874 #endif
1875
1876 #ifdef LWLIB_DIALOGS_MOTIF
1877
1878 static void
1879 set_min_dialog_size (Widget w)
1880 {
1881   short width;
1882   short height;
1883   Arg al [2];
1884
1885   XtSetArg (al [0], XmNwidth,  &width);
1886   XtSetArg (al [1], XmNheight, &height);
1887   XtGetValues (w, al, 2);
1888
1889   XtSetArg (al [0], XmNminWidth,  width);
1890   XtSetArg (al [1], XmNminHeight, height);
1891   XtSetValues (w, al, 2);
1892 }
1893
1894 #endif
1895
1896 void
1897 xm_pop_instance (widget_instance* instance, Boolean up)
1898 {
1899   Widget widget = instance->widget;
1900
1901 #ifdef LWLIB_DIALOGS_MOTIF
1902   if (XtClass (widget) == xmDialogShellWidgetClass)
1903     {
1904       Widget widget_to_manage = first_child (widget);
1905       if (up)
1906         {
1907           XtManageChild (widget_to_manage);
1908           set_min_dialog_size (widget);
1909           XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1910         }
1911       else
1912         XtUnmanageChild (widget_to_manage);
1913     }
1914   else
1915 #endif
1916     {
1917       if (up)
1918         XtManageChild (widget);
1919       else
1920         XtUnmanageChild (widget);
1921     }
1922 }
1923
1924 \f
1925 /* motif callback */
1926
1927 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1928
1929 static void
1930 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1931 {
1932   XtPointer user_data;
1933   widget_instance* instance = (widget_instance*)closure;
1934   Widget instance_widget;
1935   LWLIB_ID id;
1936   Arg al [1];
1937
1938   if (!instance)
1939     return;
1940   if (widget->core.being_destroyed)
1941     return;
1942
1943   instance_widget = instance->widget;
1944   if (!instance_widget)
1945     return;
1946
1947   id = instance->info->id;
1948   user_data = NULL;
1949   XtSetArg(al [0], XmNuserData, &user_data);
1950   XtGetValues (widget, al, 1);
1951   switch (type)
1952     {
1953     case pre_activate:
1954       if (instance->info->pre_activate_cb)
1955         instance->info->pre_activate_cb (widget, id, user_data);
1956       break;
1957     case selection:
1958       if (instance->info->selection_cb)
1959         instance->info->selection_cb (widget, id, user_data);
1960       break;
1961     case no_selection:
1962       if (instance->info->selection_cb)
1963         instance->info->selection_cb (widget, id, (XtPointer) -1);
1964       break;
1965     case post_activate:
1966       if (instance->info->post_activate_cb)
1967         instance->info->post_activate_cb (widget, id, user_data);
1968       break;
1969     default:
1970       abort ();
1971     }
1972 }
1973
1974 /* Like lw_internal_update_other_instances except that it does not do
1975    anything if its shell parent is not managed.  This is to protect
1976    lw_internal_update_other_instances to dereference freed memory
1977    if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1978    list */
1979 static void
1980 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1981                                     XtPointer call_data)
1982 {
1983   Widget parent;
1984   for (parent = widget; parent; parent = XtParent (parent))
1985     if (XtIsShell (parent))
1986       break;
1987     else if (!XtIsManaged (parent))
1988       return;
1989    lw_internal_update_other_instances (widget, closure, call_data);
1990 }
1991
1992 static void
1993 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1994 {
1995 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
1996   /* We want the selected status to change only when we decide it
1997      should change.  Yuck but correct. */
1998   if (XtClass (widget) == xmToggleButtonWidgetClass
1999       || XtClass (widget) == xmToggleButtonGadgetClass)
2000     {
2001       Boolean check;
2002       Arg al [1];
2003
2004       XtSetArg (al [0], XmNset, &check);
2005       XtGetValues (widget, al, 1);
2006
2007       XtSetArg (al [0], XmNset, !check);
2008       XtSetValues (widget, al, 1);
2009     }
2010 #endif
2011   lw_internal_update_other_instances (widget, closure, call_data);
2012   do_call (widget, closure, selection);
2013 }
2014
2015 static void
2016 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2017 {
2018   do_call (widget, closure, post_activate);
2019 }
2020
2021 #ifdef LWLIB_MENUBARS_MOTIF
2022
2023 static void
2024 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2025 {
2026 #if 0
2027   if (call_data)
2028     {
2029       /* new behavior for incremental menu construction */
2030
2031     }
2032   else
2033 #endif
2034     do_call (widget, closure, pre_activate);
2035 }
2036
2037 #endif /* LWLIB_MENUBARS_MOTIF */
2038
2039 #ifdef LWLIB_SCROLLBARS_MOTIF
2040 static void
2041 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2042 {
2043   widget_instance *instance = (widget_instance *) closure;
2044   LWLIB_ID id;
2045   XmScrollBarCallbackStruct *data =
2046     (XmScrollBarCallbackStruct *) call_data;
2047   scroll_event event_data;
2048   scrollbar_values *val =
2049     (scrollbar_values *) instance->info->val->scrollbar_data;
2050   double percent;
2051
2052   if (!instance || widget->core.being_destroyed)
2053     return;
2054
2055   id = instance->info->id;
2056
2057   percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2058   event_data.slider_value =
2059     (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2060
2061   if (event_data.slider_value > (val->maximum - val->slider_size))
2062     event_data.slider_value = val->maximum - val->slider_size;
2063   else if (event_data.slider_value < 1)
2064     event_data.slider_value = 1;
2065
2066   if (data->event)
2067     {
2068       switch (data->event->xany.type)
2069         {
2070         case KeyPress:
2071         case KeyRelease:
2072           event_data.time = data->event->xkey.time;
2073           break;
2074         case ButtonPress:
2075         case ButtonRelease:
2076           event_data.time = data->event->xbutton.time;
2077           break;
2078         case MotionNotify:
2079           event_data.time = data->event->xmotion.time;
2080           break;
2081         case EnterNotify:
2082         case LeaveNotify:
2083           event_data.time = data->event->xcrossing.time;
2084           break;
2085         default:
2086           event_data.time = 0;
2087           break;
2088         }
2089     }
2090   else
2091     event_data.time = 0;
2092
2093   switch (data->reason)
2094     {
2095     case XmCR_DECREMENT:
2096       event_data.action = SCROLLBAR_LINE_UP;
2097       break;
2098     case XmCR_INCREMENT:
2099       event_data.action = SCROLLBAR_LINE_DOWN;
2100       break;
2101     case XmCR_PAGE_DECREMENT:
2102       event_data.action = SCROLLBAR_PAGE_UP;
2103       break;
2104     case XmCR_PAGE_INCREMENT:
2105       event_data.action = SCROLLBAR_PAGE_DOWN;
2106       break;
2107     case XmCR_TO_TOP:
2108       event_data.action = SCROLLBAR_TOP;
2109       break;
2110     case XmCR_TO_BOTTOM:
2111       event_data.action = SCROLLBAR_BOTTOM;
2112       break;
2113     case XmCR_DRAG:
2114       event_data.action = SCROLLBAR_DRAG;
2115       break;
2116     case XmCR_VALUE_CHANGED:
2117       event_data.action = SCROLLBAR_CHANGE;
2118       break;
2119     default:
2120       event_data.action = SCROLLBAR_CHANGE;
2121       break;
2122     }
2123
2124   if (instance->info->pre_activate_cb)
2125     instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2126 }
2127 #endif /* LWLIB_SCROLLBARS_MOTIF */
2128
2129 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2130 static void
2131 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2132                               XtPointer call_data)
2133 {
2134   destroyed_instance* instance = (destroyed_instance*)closure;
2135   instance->widget = NULL;
2136 }
2137
2138 static void
2139 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2140 {
2141   /* This callback is only called when a dialog box is dismissed with the wm's
2142      destroy button (WM_DELETE_WINDOW.)  We want the dialog box to be destroyed
2143      in that case, not just unmapped, so that it releases its keyboard grabs.
2144      But there are problems with running our callbacks while the widget is in
2145      the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2146      instead of XmDESTROY and then destroy it ourself after having run the
2147      callback.
2148    */
2149   do_call (widget, closure, no_selection);
2150   XtDestroyWidget (widget);
2151 }
2152 #endif
2153
2154 \f
2155 /* set the keyboard focus */
2156 void
2157 xm_set_keyboard_focus (Widget parent, Widget w)
2158 {
2159   XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2160   /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2161      instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2162      Presumably the problem was elsewhere, and is now gone...
2163    */
2164 }