XEmacs 21.2.36 "Notos"
[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   XtSetValues (widget, al, ac);
795
796 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
797   /* Common to all label like widgets */
798   if (XtIsSubclass (widget, xmLabelWidgetClass))
799     xm_update_label (instance, widget, val);
800 #endif
801   class = XtClass (widget);
802   /* Class specific things */
803   if (class == xmPushButtonWidgetClass ||
804       class == xmArrowButtonWidgetClass)
805     {
806       xm_update_pushbutton (instance, widget, val);
807     }
808 #ifdef LWLIB_MENUBARS_MOTIF
809   else if (class == xmCascadeButtonWidgetClass)
810     {
811       xm_update_cascadebutton (instance, widget, val);
812     }
813 #endif
814   else if (class == xmToggleButtonWidgetClass
815            || class == xmToggleButtonGadgetClass)
816     {
817       xm_update_toggle (instance, widget, val);
818     }
819   else if (class == xmRowColumnWidgetClass)
820     {
821       Boolean radiobox = 0;
822
823       XtSetArg (al [0], XmNradioBehavior, &radiobox);
824       XtGetValues (widget, al, 1);
825
826       if (radiobox)
827         xm_update_radiobox (instance, widget, val);
828 #ifdef LWLIB_MENUBARS_MOTIF
829       else
830         xm_update_menu (instance, widget, val, deep_p);
831 #endif
832     }
833   else if (class == xmTextWidgetClass)
834     {
835       xm_update_text (instance, widget, val);
836     }
837   else if (class == xmTextFieldWidgetClass)
838     {
839       xm_update_text_field (instance, widget, val);
840     }
841   else if (class == xmListWidgetClass)
842     {
843       xm_update_list (instance, widget, val);
844     }
845 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
846   else if (class == xmComboBoxWidgetClass)
847     {
848       xm_update_combo_box (instance, widget, val);
849     }
850 #endif
851 #ifdef LWLIB_SCROLLBARS_MOTIF
852   else if (class == xmScrollBarWidgetClass)
853     {
854       xm_update_scrollbar (instance, widget, val);
855     }
856 #endif
857   /* Lastly update our global arg values. */
858   if (val->args && val->args->nargs)
859     XtSetValues (widget, val->args->args, val->args->nargs);
860 }
861
862 \f/* getting the value back */
863 void
864 xm_update_one_value (widget_instance* instance, Widget widget,
865                      widget_value* val)
866 {
867   WidgetClass class = XtClass (widget);
868   widget_value *old_wv;
869
870   /* copy the call_data slot into the "return" widget_value */
871   for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
872     if (!strcmp (val->name, old_wv->name))
873       {
874         val->call_data = old_wv->call_data;
875         break;
876       }
877
878   if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
879     {
880       Arg al [1];
881       XtSetArg (al [0], XmNset, &val->selected);
882       XtGetValues (widget, al, 1);
883       val->edited = True;
884     }
885   else if (class == xmTextWidgetClass)
886     {
887       if (val->value)
888         free (val->value);
889       val->value = XmTextGetString (widget);
890       val->edited = True;
891     }
892   else if (class == xmTextFieldWidgetClass)
893     {
894       if (val->value)
895         free (val->value);
896       val->value = XmTextFieldGetString (widget);
897       val->edited = True;
898     }
899   else if (class == xmRowColumnWidgetClass)
900     {
901       Boolean radiobox = 0;
902       {
903         Arg al [1];
904         XtSetArg (al [0], XmNradioBehavior, &radiobox);
905         XtGetValues (widget, al, 1);
906       }
907
908       if (radiobox)
909         {
910           CompositeWidget radio = (CompositeWidget)widget;
911           int i;
912           for (i = 0; i < radio->composite.num_children; i++)
913             {
914               int set = False;
915               Widget toggle = radio->composite.children [i];
916               Arg al [1];
917
918               XtSetArg (al [0], XmNset, &set);
919               XtGetValues (toggle, al, 1);
920               if (set)
921                 {
922                   if (val->value)
923                     free (val->value);
924                   val->value = safe_strdup (XtName (toggle));
925                 }
926             }
927           val->edited = True;
928         }
929     }
930   else if (class == xmListWidgetClass
931 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
932            || class == xmComboBoxWidgetClass
933 #endif
934            )
935     {
936       int pos_cnt;
937       int* pos_list;
938       Widget list = widget;
939 #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
940       if (class == xmComboBoxWidgetClass)
941         list = CB_List (widget);
942 #endif
943       if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
944         {
945           int i;
946           widget_value* cur;
947           for (cur = val->contents, i = 0; cur; cur = cur->next)
948             if (cur->value)
949               {
950                 int j;
951                 cur->selected = False;
952                 i += 1;
953                 for (j = 0; j < pos_cnt; j++)
954                   if (pos_list [j] == i)
955                     {
956                       cur->selected = True;
957                       val->value = safe_strdup (cur->name);
958                     }
959               }
960           val->edited = 1;
961           XtFree ((char *) pos_list);
962         }
963     }
964 #ifdef LWLIB_SCROLLBARS_MOTIF
965   else if (class == xmScrollBarWidgetClass)
966     {
967       /* This function is not used by the scrollbar. */
968       return;
969     }
970 #endif
971 }
972
973 \f
974 /* This function is for activating a button from a program.  It's wrong because
975    we pass a NULL argument in the call_data which is not Motif compatible.
976    This is used from the XmNdefaultAction callback of the List widgets to
977    have a double-click put down a dialog box like the button would do.
978    I could not find a way to do that with accelerators.
979  */
980 static void
981 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
982 {
983   Widget button = (Widget)closure;
984   XtCallCallbacks (button, XmNactivateCallback, NULL);
985 }
986
987 /* creation functions */
988
989 #ifdef LWLIB_DIALOGS_MOTIF
990
991 /* dialogs */
992
993 #if (XmVersion >= 1002)
994 # define ARMANDACTIVATE_KLUDGE
995 # define DND_KLUDGE
996 #endif
997
998 #ifdef ARMANDACTIVATE_KLUDGE
999  /* We want typing Return at a dialog box to select the default button; but
1000     we're satisfied with having it select the leftmost button instead.
1001
1002     In Motif 1.1.5 we could do this by putting this resource in the
1003     app-defaults file:
1004
1005         *dialog*button1.accelerators:#override\
1006         <KeyPress>Return: ArmAndActivate()\n\
1007         <KeyPress>KP_Enter: ArmAndActivate()\n\
1008         Ctrl<KeyPress>m: ArmAndActivate()\n
1009
1010     but that doesn't work with 1.2.1 and I don't understand why. However,
1011     doing the equivalent C code does work, with the notable disadvantage that
1012     the user can't override it.  So that's what we do until we figure out
1013     something better....
1014   */
1015 static char button_trans[] = "\
1016 <KeyPress>Return: ArmAndActivate()\n\
1017 <KeyPress>KP_Enter: ArmAndActivate()\n\
1018 Ctrl<KeyPress>m: ArmAndActivate()\n";
1019
1020 #endif /* ARMANDACTIVATE_KLUDGE */
1021
1022
1023 #ifdef DND_KLUDGE
1024  /* This is a kludge to disable drag-and-drop in dialog boxes.  The symptom
1025     was a segv down in libXm somewhere if you used the middle button on a
1026     dialog box to begin a drag; when you released the button to make a drop
1027     things would lose if you were not over the button where you started the
1028     drag (canceling the operation).  This was probably due to the fact that
1029     the dialog boxes were not set up to handle a drag but were trying to do
1030     so anyway for some reason.
1031
1032     So we disable drag-and-drop in dialog boxes by turning off the binding for
1033     Btn2Down which, by default, initiates a drag.  Clearly this is a shitty
1034     solution as it only works in default configurations, but...
1035   */
1036 static char disable_dnd_trans[] = "<Btn2Down>: ";
1037 #endif /* DND_KLUDGE */
1038
1039
1040 static Widget
1041 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1042              const char* shell_title, const char* icon_name,
1043              Boolean text_input_slot, Boolean radio_box, Boolean list,
1044              int left_buttons, int right_buttons)
1045 {
1046   Widget result;
1047   Widget form;
1048   Widget row;
1049   Widget icon;
1050   Widget icon_separator;
1051   Widget message;
1052   Widget value = 0;
1053   Widget separator;
1054   Widget button = 0;
1055   Widget children [16];         /* for the final XtManageChildren */
1056   int   n_children;
1057   Arg   al[64];                 /* Arg List */
1058   int   ac;                     /* Arg Count */
1059   int   i;
1060
1061 #ifdef DND_KLUDGE
1062   XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1063 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1064 #else  /* ! DND_KLUDGE */
1065 # define DO_DND_KLUDGE(widget)
1066 #endif /* ! DND_KLUDGE */
1067
1068   if (pop_up_p)
1069     {
1070       ac = 0;
1071       XtSetArg(al[ac], XmNtitle, shell_title);          ac++;
1072       XtSetArg(al[ac], XtNallowShellResize, True);      ac++;
1073       XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP);     ac++;
1074       result = XmCreateDialogShell (parent, "dialog", al, ac);
1075
1076       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1077 /*      XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1078       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1079       form = XmCreateForm (result, (char *) shell_title, al, ac);
1080     }
1081   else
1082     {
1083       ac = 0;
1084       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1085       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1086       form = XmCreateForm (parent, (char *) shell_title, al, ac);
1087       result = form;
1088     }
1089
1090   ac = 0;
1091   XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN);          ac++;
1092   XtSetArg(al[ac], XmNorientation, XmVERTICAL);         ac++;
1093   XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1094   XtSetArg(al[ac], XmNmarginWidth, 0);                  ac++;
1095   XtSetArg(al[ac], XmNmarginHeight, 0);                 ac++;
1096   XtSetArg(al[ac], XmNspacing, 13);                     ac++;
1097   XtSetArg(al[ac], XmNadjustLast, False);               ac++;
1098   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);   ac++;
1099   XtSetArg(al[ac], XmNisAligned, True);                 ac++;
1100   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);    ac++;
1101   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1102   XtSetArg(al[ac], XmNbottomOffset, 13);                ac++;
1103   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);   ac++;
1104   XtSetArg(al[ac], XmNleftOffset, 13);                  ac++;
1105   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);  ac++;
1106   XtSetArg(al[ac], XmNrightOffset, 13);                 ac++;
1107   row = XmCreateRowColumn (form, "row", al, ac);
1108
1109   n_children = 0;
1110   for (i = 0; i < left_buttons; i++)
1111     {
1112       char button_name [16];
1113       sprintf (button_name, "button%d", i + 1);
1114       ac = 0;
1115       if (i == 0)
1116         {
1117           XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1118           XtSetArg(al[ac], XmNshowAsDefault, TRUE);   ac++;
1119         }
1120       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1121       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1122       DO_DND_KLUDGE (children [n_children]);
1123
1124       if (i == 0)
1125         {
1126           button = children [n_children];
1127           ac = 0;
1128           XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1129           XtSetValues (row, al, ac);
1130
1131 #ifdef ARMANDACTIVATE_KLUDGE    /* See comment above */
1132           {
1133             XtTranslations losers = XtParseTranslationTable (button_trans);
1134             XtOverrideTranslations (button, losers);
1135             XtFree ((char *) losers);
1136           }
1137 #endif /* ARMANDACTIVATE_KLUDGE */
1138         }
1139
1140       n_children++;
1141     }
1142
1143   /* invisible separator button */
1144   ac = 0;
1145   XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1146   children [n_children] = XmCreateLabel (row, "separator_button",
1147                                          al, ac);
1148   DO_DND_KLUDGE (children [n_children]);
1149   n_children++;
1150
1151   for (i = 0; i < right_buttons; i++)
1152     {
1153       char button_name [16];
1154       sprintf (button_name, "button%d", left_buttons + i + 1);
1155       ac = 0;
1156       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1157       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1158       DO_DND_KLUDGE (children [n_children]);
1159       if (! button) button = children [n_children];
1160       n_children++;
1161     }
1162
1163   XtManageChildren (children, n_children);
1164
1165   ac = 0;
1166   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);            ac++;
1167   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1168   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1169   XtSetArg(al[ac], XmNbottomWidget, row);                       ac++;
1170   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1171   XtSetArg(al[ac], XmNleftOffset, 0);                           ac++;
1172   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1173   XtSetArg(al[ac], XmNrightOffset, 0);                          ac++;
1174   separator = XmCreateSeparator (form, "", al, ac);
1175
1176   ac = 0;
1177   XtSetArg(al[ac], XmNlabelType, XmPIXMAP);                     ac++;
1178   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1179   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1180   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);         ac++;
1181   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1182   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1183   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1184   icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1185   DO_DND_KLUDGE (icon);
1186
1187   ac = 0;
1188   XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);                ac++;
1189   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);          ac++;
1190   XtSetArg(al[ac], XmNtopOffset, 6);                            ac++;
1191   XtSetArg(al[ac], XmNtopWidget, icon);                         ac++;
1192   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1193   XtSetArg(al[ac], XmNbottomOffset, 6);                         ac++;
1194   XtSetArg(al[ac], XmNbottomWidget, separator);                 ac++;
1195   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);           ac++;
1196   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1197   icon_separator = XmCreateLabel (form, "", al, ac);
1198   DO_DND_KLUDGE (icon_separator);
1199
1200   if (text_input_slot)
1201     {
1202       ac = 0;
1203       XtSetArg(al[ac], XmNcolumns, 50);                         ac++;
1204       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1205       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1206       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1207       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1208       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1209       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1210       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1211       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1212       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1213       value = XmCreateTextField (form, "value", al, ac);
1214       DO_DND_KLUDGE (value);
1215     }
1216   else if (radio_box)
1217     {
1218       Widget radio_butt;
1219       ac = 0;
1220       XtSetArg(al[ac], XmNmarginWidth, 0);                      ac++;
1221       XtSetArg(al[ac], XmNmarginHeight, 0);                     ac++;
1222       XtSetArg(al[ac], XmNspacing, 13);                         ac++;
1223       XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);       ac++;
1224       XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);           ac++;
1225       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1226       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1227       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1228       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1229       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1230       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1231       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1232       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1233       value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1234       ac = 0;
1235       i = 0;
1236       radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1237       children [i++] = radio_butt;
1238       radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1239       children [i++] = radio_butt;
1240       radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1241       children [i++] = radio_butt;
1242       XtManageChildren (children, i);
1243     }
1244   else if (list)
1245     {
1246       ac = 0;
1247       XtSetArg(al[ac], XmNvisibleItemCount, 5);                 ac++;
1248       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1249       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1250       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1251       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1252       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1253       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1254       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1255       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1256       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1257       value = XmCreateScrolledList (form, "list", al, ac);
1258
1259       /* this is the easiest way I found to have the double click in the
1260          list activate the default button */
1261       XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1262     }
1263
1264   ac = 0;
1265   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);        ac++;
1266   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1267   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1268   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1269   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1270   XtSetArg(al[ac], XmNbottomWidget,
1271            text_input_slot || radio_box || list ? value : separator); ac++;
1272   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);         ac++;
1273   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1274   XtSetArg(al[ac], XmNleftWidget, icon);                        ac++;
1275   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1276   XtSetArg(al[ac], XmNrightOffset, 13);                         ac++;
1277   message = XmCreateLabel (form, "message", al, ac);
1278   DO_DND_KLUDGE (message);
1279
1280   if (list)
1281     XtManageChild (value);
1282
1283   i = 0;
1284   children [i] = row; i++;
1285   children [i] = separator; i++;
1286   if (text_input_slot || radio_box)
1287     {
1288       children [i] = value; i++;
1289     }
1290   children [i] = message; i++;
1291   children [i] = icon; i++;
1292   children [i] = icon_separator; i++;
1293   XtManageChildren (children, i);
1294
1295   if (text_input_slot || list)
1296     {
1297       XtInstallAccelerators (value, button);
1298       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1299     }
1300   else
1301     {
1302       XtInstallAccelerators (form, button);
1303       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1304     }
1305
1306 #ifdef DND_KLUDGE
1307   XtFree ((char *) dnd_override);
1308 #endif
1309 #undef DO_DND_KLUDGE
1310
1311   return result;
1312 }
1313
1314 static destroyed_instance*
1315 find_matching_instance (widget_instance* instance)
1316 {
1317   destroyed_instance*   cur;
1318   destroyed_instance*   prev;
1319   char* type = instance->info->type;
1320   char* name = instance->info->name;
1321
1322   for (prev = NULL, cur = all_destroyed_instances;
1323        cur;
1324        prev = cur, cur = cur->next)
1325     {
1326       if (!strcmp (cur->name, name)
1327           && !strcmp (cur->type, type)
1328           && cur->parent == instance->parent
1329           && cur->pop_up_p == instance->pop_up_p)
1330         {
1331           if (prev)
1332             prev->next = cur->next;
1333           else
1334             all_destroyed_instances = cur->next;
1335           return cur;
1336         }
1337       /* do some cleanup */
1338       else if (!cur->widget)
1339         {
1340           if (prev)
1341             prev->next = cur->next;
1342           else
1343             all_destroyed_instances = cur->next;
1344           free_destroyed_instance (cur);
1345           cur = prev ? prev : all_destroyed_instances;
1346         }
1347     }
1348   return NULL;
1349 }
1350
1351 static void
1352 recenter_widget (Widget widget)
1353 {
1354   Widget parent = XtParent (widget);
1355   Screen* screen = XtScreen (widget);
1356   Dimension screen_width = WidthOfScreen (screen);
1357   Dimension screen_height = HeightOfScreen (screen);
1358   Dimension parent_width = 0;
1359   Dimension parent_height = 0;
1360   Dimension child_width = 0;
1361   Dimension child_height = 0;
1362   Position x;
1363   Position y;
1364   Arg al [2];
1365
1366   XtSetArg (al [0], XtNwidth,  &child_width);
1367   XtSetArg (al [1], XtNheight, &child_height);
1368   XtGetValues (widget, al, 2);
1369
1370   XtSetArg (al [0], XtNwidth,  &parent_width);
1371   XtSetArg (al [1], XtNheight, &parent_height);
1372   XtGetValues (parent, al, 2);
1373
1374   x = (Position) ((parent_width  - child_width)  / 2);
1375   y = (Position) ((parent_height - child_height) / 2);
1376
1377   XtTranslateCoords (parent, x, y, &x, &y);
1378
1379   if ((Dimension) (x + child_width) > screen_width)
1380     x = screen_width - child_width;
1381   if (x < 0)
1382     x = 0;
1383
1384   if ((Dimension) (y + child_height) > screen_height)
1385     y = screen_height - child_height;
1386   if (y < 0)
1387     y = 0;
1388
1389   XtSetArg (al [0], XtNx, x);
1390   XtSetArg (al [1], XtNy, y);
1391   XtSetValues (widget, al, 2);
1392 }
1393
1394 static Widget
1395 recycle_instance (destroyed_instance* instance)
1396 {
1397   Widget widget = instance->widget;
1398
1399   /* widget is NULL if the parent was destroyed. */
1400   if (widget)
1401     {
1402       Widget focus;
1403       Widget separator;
1404
1405       /* Remove the destroy callback as the instance is not in the list
1406          anymore */
1407       XtRemoveCallback (instance->parent, XtNdestroyCallback,
1408                         mark_dead_instance_destroyed,
1409                         (XtPointer)instance);
1410
1411       /* Give the focus to the initial item */
1412       focus = XtNameToWidget (widget, "*value");
1413       if (!focus)
1414         focus = XtNameToWidget (widget, "*button1");
1415       if (focus)
1416         XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1417
1418       /* shrink the separator label back to their original size */
1419       separator = XtNameToWidget (widget, "*separator_button");
1420       if (separator)
1421         {
1422           Arg al [2];
1423           XtSetArg (al [0], XtNwidth,  5);
1424           XtSetArg (al [1], XtNheight, 5);
1425           XtSetValues (separator, al, 2);
1426         }
1427
1428       /* Center the dialog in its parent */
1429       recenter_widget (widget);
1430     }
1431   free_destroyed_instance (instance);
1432   return widget;
1433 }
1434
1435 Widget
1436 xm_create_dialog (widget_instance* instance)
1437 {
1438   char*         name = instance->info->type;
1439   Widget        parent = instance->parent;
1440   Widget        widget;
1441   Boolean       pop_up_p = instance->pop_up_p;
1442   const char*   shell_name = 0;
1443   const char*   icon_name = 0;
1444   Boolean       text_input_slot = False;
1445   Boolean       radio_box = False;
1446   Boolean       list = False;
1447   int           total_buttons;
1448   int           left_buttons = 0;
1449   int           right_buttons = 1;
1450   destroyed_instance*   dead_one;
1451
1452   /* try to find a widget to recycle */
1453   dead_one = find_matching_instance (instance);
1454   if (dead_one)
1455     {
1456       Widget recycled_widget = recycle_instance (dead_one);
1457       if (recycled_widget)
1458         return recycled_widget;
1459     }
1460
1461   switch (name [0]){
1462   case 'E': case 'e':
1463     icon_name = "dbox-error";
1464     shell_name = "Error";
1465     break;
1466
1467   case 'I': case 'i':
1468     icon_name = "dbox-info";
1469     shell_name = "Information";
1470     break;
1471
1472   case 'L': case 'l':
1473     list = True;
1474     icon_name = "dbox-question";
1475     shell_name = "Prompt";
1476     break;
1477
1478   case 'P': case 'p':
1479     text_input_slot = True;
1480     icon_name = "dbox-question";
1481     shell_name = "Prompt";
1482     break;
1483
1484   case 'Q': case 'q':
1485     icon_name = "dbox-question";
1486     shell_name = "Question";
1487     break;
1488   }
1489
1490   total_buttons = name [1] - '0';
1491
1492   if (name [3] == 'T' || name [3] == 't')
1493     {
1494       text_input_slot = False;
1495       radio_box = True;
1496     }
1497   else if (name [3])
1498     right_buttons = name [4] - '0';
1499
1500   left_buttons = total_buttons - right_buttons;
1501
1502   widget = make_dialog (name, parent, pop_up_p,
1503                         shell_name, icon_name, text_input_slot, radio_box,
1504                         list, left_buttons, right_buttons);
1505
1506   XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1507                  (XtPointer) instance);
1508   return widget;
1509 }
1510
1511 #endif /* LWLIB_DIALOGS_MOTIF */
1512
1513 #ifdef LWLIB_MENUBARS_MOTIF
1514 static Widget
1515 make_menubar (widget_instance* instance)
1516 {
1517   Arg al[10];
1518   int ac = 0;
1519
1520   XtSetArg(al[ac], XmNmarginHeight, 0);    ac++;
1521   XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1522
1523   return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1524 }
1525
1526 static void
1527 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1528 {
1529   Widget menu = (Widget) closure;
1530   XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1531 }
1532
1533 static Widget
1534 make_popup_menu (widget_instance* instance)
1535 {
1536   Widget parent = instance->parent;
1537   Window parent_window = parent->core.window;
1538   Widget result;
1539
1540   /* sets the parent window to 0 to fool Motif into not generating a grab */
1541   parent->core.window = 0;
1542   result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1543   XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1544                  (XtPointer)result);
1545   parent->core.window = parent_window;
1546   return result;
1547 }
1548 #endif /* LWLIB_MENUBARS_MOTIF */
1549
1550 #ifdef LWLIB_SCROLLBARS_MOTIF
1551 static Widget
1552 make_scrollbar (widget_instance *instance, int vertical)
1553 {
1554   Arg al[20];
1555   int ac = 0;
1556   static XtCallbackRec callbacks[2] =
1557   { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1558
1559   callbacks[0].closure  = (XtPointer) instance;
1560
1561   XtSetArg (al[ac], XmNminimum,       1); ac++;
1562   XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1563   XtSetArg (al[ac], XmNincrement,     1); ac++;
1564   XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1565   XtSetArg (al[ac], XmNborderWidth,   0); ac++;
1566   XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1567
1568   XtSetArg (al[ac], XmNdecrementCallback,       callbacks); ac++;
1569   XtSetArg (al[ac], XmNdragCallback,            callbacks); ac++;
1570   XtSetArg (al[ac], XmNincrementCallback,       callbacks); ac++;
1571   XtSetArg (al[ac], XmNpageDecrementCallback,   callbacks); ac++;
1572   XtSetArg (al[ac], XmNpageIncrementCallback,   callbacks); ac++;
1573   XtSetArg (al[ac], XmNtoBottomCallback,        callbacks); ac++;
1574   XtSetArg (al[ac], XmNtoTopCallback,           callbacks); ac++;
1575   XtSetArg (al[ac], XmNvalueChangedCallback,    callbacks); ac++;
1576
1577   return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1578 }
1579
1580 static Widget
1581 make_vertical_scrollbar (widget_instance *instance)
1582 {
1583   return make_scrollbar (instance, 1);
1584 }
1585
1586 static Widget
1587 make_horizontal_scrollbar (widget_instance *instance)
1588 {
1589   return make_scrollbar (instance, 0);
1590 }
1591
1592 #endif /* LWLIB_SCROLLBARS_MOTIF */
1593
1594 #ifdef LWLIB_WIDGETS_MOTIF
1595 /* glyph widgets */
1596 static Widget
1597 xm_create_button (widget_instance *instance)
1598 {
1599   Arg al[20];
1600   int ac = 0;
1601   Widget button = 0;
1602   widget_value* val = instance->info->val;
1603
1604   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1605   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1606   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1607   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1608   /* The highlight doesn't appear to be dynamically set which makes it
1609      look ugly.  I think this may be a LessTif bug but for now we just
1610      get rid of it. */
1611   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1612
1613   /* add any args the user supplied for creation time */
1614   lw_add_value_args_to_args (val, al, &ac);
1615
1616   if (!val->call_data)
1617     button = XmCreateLabel (instance->parent, val->name, al, ac);
1618
1619   else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1620     {
1621       XtSetArg (al [ac], XmNset, val->selected);        ac++;
1622       XtSetArg (al [ac], XmNindicatorType,
1623                 (val->type == TOGGLE_TYPE ?
1624                  XmN_OF_MANY : XmONE_OF_MANY));    ac++;
1625       XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1626       button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1627       XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1628       XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1629                      (XtPointer)instance);
1630     }
1631   else
1632     {
1633       button = XmCreatePushButton (instance->parent, val->name, al, ac);
1634       XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1635                      (XtPointer)instance);
1636     }
1637
1638   XtManageChild (button);
1639
1640   return button;
1641 }
1642
1643 static Widget
1644 xm_create_progress (widget_instance *instance)
1645 {
1646   Arg al[20];
1647   int ac = 0;
1648   Widget scale = 0;
1649   widget_value* val = instance->info->val;
1650 #if 0           /* This looks too awful, although more correct. */
1651   if (!val->call_data)
1652     {
1653       XtSetArg (al [ac], XmNsensitive, False);          ac++;
1654     }
1655   else
1656     {
1657       XtSetArg (al [ac], XmNsensitive, val->enabled);           ac++;
1658     }
1659 #else
1660   XtSetArg (al [ac], XmNsensitive, True);               ac++;
1661 #endif
1662   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1663   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1664   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1665   XtSetArg (al [ac], XmNorientation, XmHORIZONTAL);     ac++;
1666   /* The highlight doesn't appear to be dynamically set which makes it
1667      look ugly.  I think this may be a LessTif bug but for now we just
1668      get rid of it. */
1669   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1670   /* add any args the user supplied for creation time */
1671   lw_add_value_args_to_args (val, al, &ac);
1672
1673   scale = XmCreateScale (instance->parent, val->name, al, ac);
1674   if (val->call_data)
1675     XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1676                    (XtPointer)instance);
1677
1678   XtManageChild (scale);
1679
1680   return scale;
1681 }
1682
1683 static Widget
1684 xm_create_text_field (widget_instance *instance)
1685 {
1686   Arg al[20];
1687   int ac = 0;
1688   Widget text = 0;
1689   widget_value* val = instance->info->val;
1690
1691   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1692   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1693   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1694   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1695   /* The highlight doesn't appear to be dynamically set which makes it
1696      look ugly.  I think this may be a LessTif bug but for now we just
1697      get rid of it. */
1698   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1699
1700   /* add any args the user supplied for creation time */
1701   lw_add_value_args_to_args (val, al, &ac);
1702
1703   text = XmCreateTextField (instance->parent, val->name, al, ac);
1704   if (val->call_data)
1705     XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1706                    (XtPointer)instance);
1707
1708   XtManageChild (text);
1709
1710   return text;
1711 }
1712
1713 static Widget
1714 xm_create_label_field (widget_instance *instance)
1715 {
1716   return xm_create_label (instance->parent, instance->info->val);
1717 }
1718
1719 Widget
1720 xm_create_label (Widget parent, widget_value* val)
1721 {
1722   Arg al[20];
1723   int ac = 0;
1724   Widget label = 0;
1725
1726   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1727   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1728   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1729   /* The highlight doesn't appear to be dynamically set which makes it
1730      look ugly.  I think this may be a LessTif bug but for now we just
1731      get rid of it. */
1732   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1733
1734   /* add any args the user supplied for creation time */
1735   lw_add_value_args_to_args (val, al, &ac);
1736
1737   label = XmCreateLabel (parent, val->name, al, ac);
1738
1739   XtManageChild (label);
1740
1741   /* Do it again for arguments that have no effect until the widget is realized. */
1742   ac = 0;
1743   lw_add_value_args_to_args (val, al, &ac);
1744   XtSetValues (label, al, ac);
1745
1746   return label;
1747 }
1748
1749 #if XmVERSION > 1
1750 static Widget
1751 xm_create_combo_box (widget_instance *instance)
1752 {
1753   Arg al[20];
1754   int ac = 0;
1755   Widget combo = 0;
1756   widget_value* val = instance->info->val;
1757
1758   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1759   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1760   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1761   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1762   /* The highlight doesn't appear to be dynamically set which makes it
1763      look ugly.  I think this may be a LessTif bug but for now we just
1764      get rid of it. */
1765   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1766
1767   /* add any args the user supplied for creation time */
1768   lw_add_value_args_to_args (val, al, &ac);
1769
1770   combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
1771   if (val->call_data)
1772     XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1773                    (XtPointer)instance);
1774
1775   XtManageChild (combo);
1776
1777   return combo;
1778 }
1779 #endif
1780 #endif /* LWLIB_WIDGETS_MOTIF */
1781
1782 \f
1783 /* Table of functions to create widgets */
1784
1785 widget_creation_entry
1786 xm_creation_table [] =
1787 {
1788 #ifdef LWLIB_MENUBARS_MOTIF
1789   {"menubar",                   make_menubar},
1790   {"popup",                     make_popup_menu},
1791 #endif
1792 #ifdef LWLIB_SCROLLBARS_MOTIF
1793   {"vertical-scrollbar",        make_vertical_scrollbar},
1794   {"horizontal-scrollbar",      make_horizontal_scrollbar},
1795 #endif
1796 #ifdef LWLIB_WIDGETS_MOTIF
1797   {"button",            xm_create_button},
1798   {"progress",          xm_create_progress},
1799   {"text-field",                xm_create_text_field},
1800   {"label",             xm_create_label_field},
1801 #if XmVERSION > 1
1802   {"combo-box",         xm_create_combo_box},
1803 #endif
1804 #endif
1805   {NULL, NULL}
1806 };
1807
1808 \f/* Destruction of instances */
1809 void
1810 xm_destroy_instance (widget_instance* instance)
1811 {
1812 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
1813   /* It appears that this is used only for dialog boxes. */
1814   Widget widget = instance->widget;
1815   /* recycle the dialog boxes */
1816   /* Disable the recycling until we can find a way to have the dialog box
1817      get reasonable layout after we modify its contents. */
1818   if (0
1819       && XtClass (widget) == xmDialogShellWidgetClass)
1820     {
1821       destroyed_instance* dead_instance =
1822         make_destroyed_instance (instance->info->name,
1823                                  instance->info->type,
1824                                  instance->widget,
1825                                  instance->parent,
1826                                  instance->pop_up_p);
1827       dead_instance->next = all_destroyed_instances;
1828       all_destroyed_instances = dead_instance;
1829       XtUnmanageChild (first_child (instance->widget));
1830       XFlush (XtDisplay (instance->widget));
1831       XtAddCallback (instance->parent, XtNdestroyCallback,
1832                      mark_dead_instance_destroyed, (XtPointer)dead_instance);
1833     }
1834   else
1835     {
1836       /* This might not be necessary now that the nosel is attached to
1837          popdown instead of destroy, but it can't hurt. */
1838       XtRemoveCallback (instance->widget, XtNdestroyCallback,
1839                         xm_nosel_callback, (XtPointer)instance);
1840
1841       XtDestroyWidget (instance->widget);
1842     }
1843 #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
1844 }
1845
1846 \f/* popup utility */
1847 #ifdef LWLIB_MENUBARS_MOTIF
1848
1849 void
1850 xm_popup_menu (Widget widget, XEvent *event)
1851 {
1852   if (event->type == ButtonPress || event->type == ButtonRelease)
1853     {
1854       /* This is so totally ridiculous: there's NO WAY to tell Motif
1855          that *any* button can select a menu item.  Only one button
1856          can have that honor.
1857        */
1858       char *trans = 0;
1859       if      (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1860       else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1861       else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1862       else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1863       else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1864       if (trans)
1865         {
1866           Arg al [1];
1867           XtSetArg (al [0], XmNmenuPost, trans);
1868           XtSetValues (widget, al, 1);
1869         }
1870       XmMenuPosition (widget, (XButtonPressedEvent *) event);
1871     }
1872   XtManageChild (widget);
1873 }
1874
1875 #endif
1876
1877 #ifdef LWLIB_DIALOGS_MOTIF
1878
1879 static void
1880 set_min_dialog_size (Widget w)
1881 {
1882   short width;
1883   short height;
1884   Arg al [2];
1885
1886   XtSetArg (al [0], XmNwidth,  &width);
1887   XtSetArg (al [1], XmNheight, &height);
1888   XtGetValues (w, al, 2);
1889
1890   XtSetArg (al [0], XmNminWidth,  width);
1891   XtSetArg (al [1], XmNminHeight, height);
1892   XtSetValues (w, al, 2);
1893 }
1894
1895 #endif
1896
1897 void
1898 xm_pop_instance (widget_instance* instance, Boolean up)
1899 {
1900   Widget widget = instance->widget;
1901
1902 #ifdef LWLIB_DIALOGS_MOTIF
1903   if (XtClass (widget) == xmDialogShellWidgetClass)
1904     {
1905       Widget widget_to_manage = first_child (widget);
1906       if (up)
1907         {
1908           XtManageChild (widget_to_manage);
1909           set_min_dialog_size (widget);
1910           XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1911         }
1912       else
1913         XtUnmanageChild (widget_to_manage);
1914     }
1915   else
1916 #endif
1917     {
1918       if (up)
1919         XtManageChild (widget);
1920       else
1921         XtUnmanageChild (widget);
1922     }
1923 }
1924
1925 \f
1926 /* motif callback */
1927
1928 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1929
1930 static void
1931 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1932 {
1933   XtPointer user_data;
1934   widget_instance* instance = (widget_instance*)closure;
1935   Widget instance_widget;
1936   LWLIB_ID id;
1937   Arg al [1];
1938
1939   if (!instance)
1940     return;
1941   if (widget->core.being_destroyed)
1942     return;
1943
1944   instance_widget = instance->widget;
1945   if (!instance_widget)
1946     return;
1947
1948   id = instance->info->id;
1949   user_data = NULL;
1950   XtSetArg(al [0], XmNuserData, &user_data);
1951   XtGetValues (widget, al, 1);
1952   switch (type)
1953     {
1954     case pre_activate:
1955       if (instance->info->pre_activate_cb)
1956         instance->info->pre_activate_cb (widget, id, user_data);
1957       break;
1958     case selection:
1959       if (instance->info->selection_cb)
1960         instance->info->selection_cb (widget, id, user_data);
1961       break;
1962     case no_selection:
1963       if (instance->info->selection_cb)
1964         instance->info->selection_cb (widget, id, (XtPointer) -1);
1965       break;
1966     case post_activate:
1967       if (instance->info->post_activate_cb)
1968         instance->info->post_activate_cb (widget, id, user_data);
1969       break;
1970     default:
1971       abort ();
1972     }
1973 }
1974
1975 /* Like lw_internal_update_other_instances except that it does not do
1976    anything if its shell parent is not managed.  This is to protect
1977    lw_internal_update_other_instances to dereference freed memory
1978    if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1979    list */
1980 static void
1981 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1982                                     XtPointer call_data)
1983 {
1984   Widget parent;
1985   for (parent = widget; parent; parent = XtParent (parent))
1986     if (XtIsShell (parent))
1987       break;
1988     else if (!XtIsManaged (parent))
1989       return;
1990    lw_internal_update_other_instances (widget, closure, call_data);
1991 }
1992
1993 static void
1994 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1995 {
1996 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
1997   /* We want the selected status to change only when we decide it
1998      should change.  Yuck but correct. */
1999   if (XtClass (widget) == xmToggleButtonWidgetClass
2000       || XtClass (widget) == xmToggleButtonGadgetClass)
2001     {
2002       Boolean check;
2003       Arg al [1];
2004
2005       XtSetArg (al [0], XmNset, &check);
2006       XtGetValues (widget, al, 1);
2007
2008       XtSetArg (al [0], XmNset, !check);
2009       XtSetValues (widget, al, 1);
2010     }
2011 #endif
2012   lw_internal_update_other_instances (widget, closure, call_data);
2013   do_call (widget, closure, selection);
2014 }
2015
2016 static void
2017 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2018 {
2019   do_call (widget, closure, post_activate);
2020 }
2021
2022 #ifdef LWLIB_MENUBARS_MOTIF
2023
2024 static void
2025 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
2026 {
2027 #if 0
2028   if (call_data)
2029     {
2030       /* new behavior for incremental menu construction */
2031
2032     }
2033   else
2034 #endif
2035     do_call (widget, closure, pre_activate);
2036 }
2037
2038 #endif /* LWLIB_MENUBARS_MOTIF */
2039
2040 #ifdef LWLIB_SCROLLBARS_MOTIF
2041 static void
2042 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
2043 {
2044   widget_instance *instance = (widget_instance *) closure;
2045   LWLIB_ID id;
2046   XmScrollBarCallbackStruct *data =
2047     (XmScrollBarCallbackStruct *) call_data;
2048   scroll_event event_data;
2049   scrollbar_values *val =
2050     (scrollbar_values *) instance->info->val->scrollbar_data;
2051   double percent;
2052
2053   if (!instance || widget->core.being_destroyed)
2054     return;
2055
2056   id = instance->info->id;
2057
2058   percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2059   event_data.slider_value =
2060     (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2061
2062   if (event_data.slider_value > (val->maximum - val->slider_size))
2063     event_data.slider_value = val->maximum - val->slider_size;
2064   else if (event_data.slider_value < 1)
2065     event_data.slider_value = 1;
2066
2067   if (data->event)
2068     {
2069       switch (data->event->xany.type)
2070         {
2071         case KeyPress:
2072         case KeyRelease:
2073           event_data.time = data->event->xkey.time;
2074           break;
2075         case ButtonPress:
2076         case ButtonRelease:
2077           event_data.time = data->event->xbutton.time;
2078           break;
2079         case MotionNotify:
2080           event_data.time = data->event->xmotion.time;
2081           break;
2082         case EnterNotify:
2083         case LeaveNotify:
2084           event_data.time = data->event->xcrossing.time;
2085           break;
2086         default:
2087           event_data.time = 0;
2088           break;
2089         }
2090     }
2091   else
2092     event_data.time = 0;
2093
2094   switch (data->reason)
2095     {
2096     case XmCR_DECREMENT:
2097       event_data.action = SCROLLBAR_LINE_UP;
2098       break;
2099     case XmCR_INCREMENT:
2100       event_data.action = SCROLLBAR_LINE_DOWN;
2101       break;
2102     case XmCR_PAGE_DECREMENT:
2103       event_data.action = SCROLLBAR_PAGE_UP;
2104       break;
2105     case XmCR_PAGE_INCREMENT:
2106       event_data.action = SCROLLBAR_PAGE_DOWN;
2107       break;
2108     case XmCR_TO_TOP:
2109       event_data.action = SCROLLBAR_TOP;
2110       break;
2111     case XmCR_TO_BOTTOM:
2112       event_data.action = SCROLLBAR_BOTTOM;
2113       break;
2114     case XmCR_DRAG:
2115       event_data.action = SCROLLBAR_DRAG;
2116       break;
2117     case XmCR_VALUE_CHANGED:
2118       event_data.action = SCROLLBAR_CHANGE;
2119       break;
2120     default:
2121       event_data.action = SCROLLBAR_CHANGE;
2122       break;
2123     }
2124
2125   if (instance->info->pre_activate_cb)
2126     instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2127 }
2128 #endif /* LWLIB_SCROLLBARS_MOTIF */
2129
2130 #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
2131 static void
2132 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
2133                               XtPointer call_data)
2134 {
2135   destroyed_instance* instance = (destroyed_instance*)closure;
2136   instance->widget = NULL;
2137 }
2138
2139 static void
2140 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
2141 {
2142   /* This callback is only called when a dialog box is dismissed with the wm's
2143      destroy button (WM_DELETE_WINDOW.)  We want the dialog box to be destroyed
2144      in that case, not just unmapped, so that it releases its keyboard grabs.
2145      But there are problems with running our callbacks while the widget is in
2146      the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
2147      instead of XmDESTROY and then destroy it ourself after having run the
2148      callback.
2149    */
2150   do_call (widget, closure, no_selection);
2151   XtDestroyWidget (widget);
2152 }
2153 #endif
2154
2155 \f
2156 /* set the keyboard focus */
2157 void
2158 xm_set_keyboard_focus (Widget parent, Widget w)
2159 {
2160   XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2161   /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2162      instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2163      Presumably the problem was elsewhere, and is now gone...
2164    */
2165 }