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