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