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