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