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