Merge from mainline.
[bpt/emacs.git] / lwlib / lwlib-Xm.c
1 /* The lwlib interface to Motif widgets.
2 Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
4 Free Software Foundation, Inc.
5 Copyright (C) 1992 Lucid, Inc.
6
7 This file is part of the Lucid Widget Library.
8
9 The Lucid Widget Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 1, or (at your option)
12 any later version.
13
14 The Lucid Widget Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with GNU Emacs; see the file COPYING. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA. */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <setjmp.h>
31
32 #include <X11/StringDefs.h>
33 #include <X11/IntrinsicP.h>
34 #include <X11/ObjectP.h>
35 #include <X11/CoreP.h>
36 #include <X11/CompositeP.h>
37
38 #include "../src/lisp.h"
39
40 #include "lwlib-Xm.h"
41 #include "lwlib-utils.h"
42
43 #include <Xm/BulletinB.h>
44 #include <Xm/CascadeB.h>
45 #include <Xm/CascadeBG.h>
46 #include <Xm/DrawingA.h>
47 #include <Xm/FileSB.h>
48 #include <Xm/Label.h>
49 #include <Xm/List.h>
50 #include <Xm/MainW.h>
51 #include <Xm/MenuShell.h>
52 #include <Xm/MessageB.h>
53 #include <Xm/PanedW.h>
54 #include <Xm/PushB.h>
55 #include <Xm/PushBG.h>
56 #include <Xm/ArrowB.h>
57 #include <Xm/SelectioB.h>
58 #include <Xm/Text.h>
59 #include <Xm/TextF.h>
60 #include <Xm/ToggleB.h>
61 #include <Xm/ToggleBG.h>
62 #include <Xm/RowColumn.h>
63 #include <Xm/ScrolledW.h>
64 #include <Xm/Separator.h>
65 #include <Xm/DialogS.h>
66 #include <Xm/Form.h>
67
68 enum do_call_type { pre_activate, selection, no_selection, post_activate };
69
70
71 \f/* Structures to keep destroyed instances */
72 typedef struct _destroyed_instance
73 {
74 char* name;
75 char* type;
76 Widget widget;
77 Widget parent;
78 Boolean pop_up_p;
79 struct _destroyed_instance* next;
80 } destroyed_instance;
81
82 static destroyed_instance *make_destroyed_instance (char *, char *,
83 Widget, Widget,
84 Boolean);
85 static void free_destroyed_instance (destroyed_instance*);
86 Widget first_child (Widget);
87 Boolean lw_motif_widget_p (Widget);
88 static XmString resource_motif_string (Widget, char *);
89 static void destroy_all_children (Widget, int);
90 static void xm_update_label (widget_instance *, Widget, widget_value *);
91 static void xm_update_list (widget_instance *, Widget, widget_value *);
92 static void xm_update_pushbutton (widget_instance *, Widget,
93 widget_value *);
94 static void xm_update_cascadebutton (widget_instance *, Widget,
95 widget_value *);
96 static void xm_update_toggle (widget_instance *, Widget, widget_value *);
97 static void xm_update_radiobox (widget_instance *, Widget, widget_value *);
98 static void make_menu_in_widget (widget_instance *, Widget,
99 widget_value *, int);
100 static void update_one_menu_entry (widget_instance *, Widget,
101 widget_value *, Boolean);
102 static void xm_update_menu (widget_instance *, Widget, widget_value *,
103 Boolean);
104 static void xm_update_text (widget_instance *, Widget, widget_value *);
105 static void xm_update_text_field (widget_instance *, Widget,
106 widget_value *);
107 void xm_update_one_value (widget_instance *, Widget, widget_value *);
108 static void activate_button (Widget, XtPointer, XtPointer);
109 static Widget make_dialog (char *, Widget, Boolean, char *, char *,
110 Boolean, Boolean, Boolean, int, int);
111 static destroyed_instance* find_matching_instance (widget_instance*);
112 static void mark_dead_instance_destroyed (Widget, XtPointer, XtPointer);
113 static void recenter_widget (Widget);
114 static Widget recycle_instance (destroyed_instance*);
115 Widget xm_create_dialog (widget_instance*);
116 static Widget make_menubar (widget_instance*);
117 static void remove_grabs (Widget, XtPointer, XtPointer);
118 static Widget make_popup_menu (widget_instance*);
119 static Widget make_main (widget_instance*);
120 void xm_destroy_instance (widget_instance*);
121 void xm_popup_menu (Widget, XEvent *);
122 static void set_min_dialog_size (Widget);
123 static void do_call (Widget, XtPointer, enum do_call_type);
124 static void xm_generic_callback (Widget, XtPointer, XtPointer);
125 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
126 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
127 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
128 void xm_set_keyboard_focus (Widget, Widget);
129 void xm_set_main_areas (Widget, Widget, Widget);
130 static void xm_internal_update_other_instances (Widget, XtPointer,
131 XtPointer);
132 static void xm_arm_callback (Widget, XtPointer, XtPointer);
133
134 #if 0
135 void xm_update_one_widget (widget_instance *, Widget, widget_value *,
136 Boolean);
137 void xm_pop_instance (widget_instance*, Boolean);
138 void xm_manage_resizing (Widget, Boolean);
139 #endif
140
141
142 #if 0
143
144 /* Print the complete X resource name of widget WIDGET to stderr.
145 This is sometimes handy to have available. */
146
147 void
148 x_print_complete_resource_name (Widget widget)
149 {
150 int i;
151 String names[100];
152
153 for (i = 0; i < 100 && widget != NULL; ++i)
154 {
155 names[i] = XtName (widget);
156 widget = XtParent (widget);
157 }
158
159 for (--i; i >= 1; --i)
160 fprintf (stderr, "%s.", names[i]);
161 fprintf (stderr, "%s\n", names[0]);
162 }
163
164 #endif /* 0 */
165
166
167 static destroyed_instance *all_destroyed_instances = NULL;
168
169 static destroyed_instance*
170 make_destroyed_instance (char* name,
171 char* type,
172 Widget widget,
173 Widget parent,
174 Boolean pop_up_p)
175 {
176 destroyed_instance* instance =
177 (destroyed_instance*)malloc (sizeof (destroyed_instance));
178 instance->name = safe_strdup (name);
179 instance->type = safe_strdup (type);
180 instance->widget = widget;
181 instance->parent = parent;
182 instance->pop_up_p = pop_up_p;
183 instance->next = NULL;
184 return instance;
185 }
186
187 static void
188 free_destroyed_instance (destroyed_instance* instance)
189 {
190 free (instance->name);
191 free (instance->type);
192 free (instance);
193 }
194
195 \f/* motif utility functions */
196 Widget
197 first_child (Widget widget)
198 {
199 return ((CompositeWidget)widget)->composite.children [0];
200 }
201
202 Boolean
203 lw_motif_widget_p (Widget widget)
204 {
205 return
206 XtClass (widget) == xmDialogShellWidgetClass
207 || XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
208 }
209
210 static XmString
211 resource_motif_string (Widget widget,
212 char* name)
213 {
214 XtResource resource;
215 XmString result = 0;
216
217 resource.resource_name = name;
218 resource.resource_class = XmCXmString;
219 resource.resource_type = XmRXmString;
220 resource.resource_size = sizeof (XmString);
221 resource.resource_offset = 0;
222 resource.default_type = XtRImmediate;
223 resource.default_addr = 0;
224
225 XtGetSubresources (widget, (XtPointer)&result, "dialogString",
226 "DialogString", &resource, 1, NULL, 0);
227 return result;
228 }
229
230 /* Destroy all of the children of WIDGET
231 starting with number FIRST_CHILD_TO_DESTROY. */
232
233 static void
234 destroy_all_children (Widget widget,
235 int first_child_to_destroy)
236 {
237 Widget* children;
238 unsigned int number;
239 int i;
240
241 children = XtCompositeChildren (widget, &number);
242 if (children)
243 {
244 XtUnmanageChildren (children + first_child_to_destroy,
245 number - first_child_to_destroy);
246
247 /* Unmanage all children and destroy them. They will only be
248 really destroyed when we get out of DispatchEvent. */
249 for (i = first_child_to_destroy; i < number; i++)
250 {
251 Arg al[2];
252 Widget submenu = 0;
253 /* Cascade buttons have submenus,and these submenus
254 need to be freed. But they are not included in
255 XtCompositeChildren. So get it out of the cascade button
256 and free it. If this child is not a cascade button,
257 then submenu should remain unchanged. */
258 XtSetArg (al[0], XmNsubMenuId, &submenu);
259 XtGetValues (children[i], al, 1);
260 if (submenu)
261 {
262 destroy_all_children (submenu, 0);
263 XtDestroyWidget (submenu);
264 }
265 XtDestroyWidget (children[i]);
266 }
267
268 XtFree ((char *) children);
269 }
270 }
271
272
273 \f
274 /* Callback XmNarmCallback and XmNdisarmCallback for buttons in a
275 menu. CLIENT_DATA contains a pointer to the widget_value
276 corresponding to widget W. CALL_DATA contains a
277 XmPushButtonCallbackStruct containing the reason why the callback
278 is called. */
279
280 static void
281 xm_arm_callback (Widget w, XtPointer client_data, XtPointer call_data)
282 {
283 XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *) call_data;
284 widget_value *wv = (widget_value *) client_data;
285 widget_instance *instance;
286
287 /* Get the id of the menu bar or popup menu this widget is in. */
288 while (w != NULL)
289 {
290 if (XmIsRowColumn (w))
291 {
292 unsigned char type = 0xff;
293
294 XtVaGetValues (w, XmNrowColumnType, &type, NULL);
295 if (type == XmMENU_BAR || type == XmMENU_POPUP)
296 break;
297 }
298
299 w = XtParent (w);
300 }
301
302 if (w != NULL)
303 {
304 instance = lw_get_widget_instance (w);
305 if (instance && instance->info->highlight_cb)
306 {
307 call_data = cbs->reason == XmCR_DISARM ? NULL : wv;
308 instance->info->highlight_cb (w, instance->info->id, call_data);
309 }
310 }
311 }
312
313
314 \f
315 /* Update the label of widget WIDGET. WIDGET must be a Label widget
316 or a subclass of Label. WIDGET_INSTANCE is unused. VAL contains
317 the value to update.
318
319 Menus:
320
321 Emacs fills VAL->name with the text to display in the menu, and
322 sets VAL->value to null. Function make_menu_in_widget creates
323 widgets with VAL->name as resource name. This works because the
324 Label widget uses its resource name for display if no
325 XmNlabelString is set.
326
327 Dialogs:
328
329 VAL->name is again set to the resource name, but VAL->value is
330 not null, and contains the label string to display. */
331
332 static void
333 xm_update_label (widget_instance* instance,
334 Widget widget,
335 widget_value* val)
336 {
337 XmString res_string = 0;
338 XmString built_string = 0;
339 XmString key_string = 0;
340 Arg al [256];
341 int ac;
342
343 ac = 0;
344
345 if (val->value)
346 {
347 /* A label string is specified, i.e. we are in a dialog. First
348 see if it is overridden by something from the resource file. */
349 res_string = resource_motif_string (widget, val->value);
350
351 if (res_string)
352 {
353 XtSetArg (al [ac], XmNlabelString, res_string); ac++;
354 }
355 else
356 {
357 built_string =
358 XmStringCreateLocalized (val->value);
359 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
360 }
361
362 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
363 }
364
365 if (val->key)
366 {
367 key_string = XmStringCreateLocalized (val->key);
368 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
369 }
370
371 if (ac)
372 XtSetValues (widget, al, ac);
373
374 if (built_string)
375 XmStringFree (built_string);
376
377 if (key_string)
378 XmStringFree (key_string);
379 }
380
381 \f/* update of list */
382 static void
383 xm_update_list (widget_instance* instance,
384 Widget widget,
385 widget_value* val)
386 {
387 widget_value* cur;
388 int i;
389 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
390 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
391 instance);
392 for (cur = val->contents, i = 0; cur; cur = cur->next)
393 if (cur->value)
394 {
395 XmString xmstr = XmStringCreateLocalized (cur->value);
396 i += 1;
397 XmListAddItem (widget, xmstr, 0);
398 if (cur->selected)
399 XmListSelectPos (widget, i, False);
400 XmStringFree (xmstr);
401 }
402 }
403
404 \f/* update of buttons */
405 static void
406 xm_update_pushbutton (widget_instance* instance,
407 Widget widget,
408 widget_value* val)
409 {
410 XtVaSetValues (widget, XmNalignment, XmALIGNMENT_CENTER, NULL);
411 XtRemoveAllCallbacks (widget, XmNactivateCallback);
412 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
413 }
414
415 static void
416 xm_update_cascadebutton (widget_instance* instance,
417 Widget widget,
418 widget_value* val)
419 {
420 /* Should also rebuild the menu by calling ...update_menu... */
421 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
422 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
423 instance);
424 }
425
426 \f/* update toggle and radiobox */
427 static void
428 xm_update_toggle (widget_instance* instance,
429 Widget widget,
430 widget_value* val)
431 {
432 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
433 XtAddCallback (widget, XmNvalueChangedCallback,
434 xm_generic_callback, instance);
435 XtVaSetValues (widget, XmNset, val->selected,
436 XmNalignment, XmALIGNMENT_BEGINNING, NULL);
437 }
438
439 static void
440 xm_update_radiobox (widget_instance* instance,
441 Widget widget,
442 widget_value* val)
443
444 {
445 Widget toggle;
446 widget_value* cur;
447
448 /* update the callback */
449 XtRemoveAllCallbacks (widget, XmNentryCallback);
450 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
451
452 /* first update all the toggles */
453 /* Energize kernel interface is currently bad. It sets the selected widget
454 with the selected flag but returns it by its name. So we currently
455 have to support both setting the selection with the selected slot
456 of val contents and setting it with the "value" slot of val. The latter
457 has a higher priority. This to be removed when the kernel is fixed. */
458 for (cur = val->contents; cur; cur = cur->next)
459 {
460 toggle = XtNameToWidget (widget, cur->value);
461 if (toggle)
462 {
463 XtSetSensitive (toggle, cur->enabled);
464 if (!val->value && cur->selected)
465 XtVaSetValues (toggle, XmNset, cur->selected, NULL);
466 if (val->value && strcmp (val->value, cur->value))
467 XtVaSetValues (toggle, XmNset, False, NULL);
468 }
469 }
470
471 /* The selected was specified by the value slot */
472 if (val->value)
473 {
474 toggle = XtNameToWidget (widget, val->value);
475 if (toggle)
476 XtVaSetValues (toggle, XmNset, True, NULL);
477 }
478 }
479
480 \f
481 /* update a popup menu, pulldown menu or a menubar */
482
483 /* KEEP_FIRST_CHILDREN gives the number of initial children to keep. */
484
485 static void
486 make_menu_in_widget (widget_instance* instance,
487 Widget widget,
488 widget_value* val,
489 int keep_first_children)
490 {
491 Widget* children = 0;
492 int num_children;
493 int child_index;
494 widget_value* cur;
495 Widget button = 0;
496 Widget title = 0;
497 Widget menu;
498 Arg al [256];
499 int ac;
500 Boolean menubar_p;
501 unsigned char type;
502
503 Widget* old_children;
504 unsigned int old_num_children;
505
506 /* Disable drag and drop for labels in menu bar. */
507 static char overrideTrans[] = "<Btn2Down>: Noop()";
508 XtTranslations override = XtParseTranslationTable (overrideTrans);
509
510 old_children = XtCompositeChildren (widget, &old_num_children);
511
512 /* Allocate the children array */
513 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next)
514 ;
515 children = (Widget*)XtMalloc (num_children * sizeof (Widget));
516
517 /* WIDGET should be a RowColumn. */
518 if (!XmIsRowColumn (widget))
519 abort ();
520
521 /* Determine whether WIDGET is a menu bar. */
522 type = -1;
523 XtSetArg (al[0], XmNrowColumnType, &type);
524 XtGetValues (widget, al, 1);
525 if (type != XmMENU_BAR && type != XmMENU_PULLDOWN && type != XmMENU_POPUP)
526 abort ();
527 menubar_p = type == XmMENU_BAR;
528
529 /* Add a callback to popups and pulldowns that is called when
530 it is made invisible again. */
531 if (!menubar_p)
532 XtAddCallback (XtParent (widget), XmNpopdownCallback,
533 xm_pop_down_callback, (XtPointer)instance);
534
535 /* Preserve the first KEEP_FIRST_CHILDREN old children. */
536 for (child_index = 0, cur = val; child_index < keep_first_children;
537 child_index++, cur = cur->next)
538 children[child_index] = old_children[child_index];
539
540 /* Check that those are all we have
541 (the caller should have deleted the rest). */
542 if (old_num_children != keep_first_children)
543 abort ();
544
545 /* Create the rest. */
546 for (child_index = keep_first_children; cur; child_index++, cur = cur->next)
547 {
548 enum menu_separator separator;
549
550 ac = 0;
551 XtSetArg (al[ac], XmNsensitive, cur->enabled); ac++;
552 XtSetArg (al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
553 XtSetArg (al[ac], XmNuserData, cur->call_data); ac++;
554
555 if (instance->pop_up_p && !cur->contents && !cur->call_data
556 && !lw_separator_p (cur->name, &separator, 1))
557 {
558 ac = 0;
559 XtSetArg (al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
560 title = button = XmCreateLabel (widget, cur->name, al, ac);
561 }
562 else if (lw_separator_p (cur->name, &separator, 1))
563 {
564 ac = 0;
565 XtSetArg (al[ac], XmNseparatorType, separator); ++ac;
566 button = XmCreateSeparator (widget, cur->name, al, ac);
567 }
568 else if (!cur->contents)
569 {
570 if (menubar_p)
571 button = XmCreateCascadeButton (widget, cur->name, al, ac);
572 else if (!cur->call_data)
573 button = XmCreateLabel (widget, cur->name, al, ac);
574 else if (cur->button_type == BUTTON_TYPE_TOGGLE
575 || cur->button_type == BUTTON_TYPE_RADIO)
576 {
577 XtSetArg (al[ac], XmNset, cur->selected); ++ac;
578 XtSetArg (al[ac], XmNvisibleWhenOff, True); ++ac;
579 XtSetArg (al[ac], XmNindicatorType,
580 (cur->button_type == BUTTON_TYPE_TOGGLE
581 ? XmN_OF_MANY : XmONE_OF_MANY));
582 ++ac;
583 button = XmCreateToggleButton (widget, cur->name, al, ac);
584 XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
585 XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
586 }
587 else
588 {
589 button = XmCreatePushButton (widget, cur->name, al, ac);
590 XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
591 XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
592 }
593
594 xm_update_label (instance, button, cur);
595
596 /* Add a callback that is called when the button is
597 selected. Toggle buttons don't support
598 XmNactivateCallback, we use XmNvalueChangedCallback in
599 that case. Don't add a callback to a simple label. */
600 if (cur->button_type)
601 xm_update_toggle (instance, button, cur);
602 else if (cur->call_data)
603 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
604 (XtPointer)instance);
605 }
606 else
607 {
608 menu = XmCreatePulldownMenu (widget, cur->name, NULL, 0);
609
610 make_menu_in_widget (instance, menu, cur->contents, 0);
611 XtSetArg (al[ac], XmNsubMenuId, menu); ac++;
612 button = XmCreateCascadeButton (widget, cur->name, al, ac);
613
614 xm_update_label (instance, button, cur);
615
616 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
617 (XtPointer)instance);
618 XtOverrideTranslations (button, override);
619
620 }
621
622 children[child_index] = button;
623 }
624
625 /* Last entry is the help button. The original comment read "Has to
626 be done after managing the buttons otherwise the menubar is only
627 4 pixels high." This is no longer true, and to make
628 XmNmenuHelpWidget work, we need to set it before managing the
629 children.. --gerd. */
630 if (button)
631 XtVaSetValues (widget, XmNmenuHelpWidget, button, NULL);
632
633 if (num_children)
634 XtManageChildren (children, num_children);
635
636 XtFree ((char *) children);
637 if (old_children)
638 XtFree ((char *) old_children);
639 }
640
641 static void
642 update_one_menu_entry (widget_instance* instance,
643 Widget widget,
644 widget_value* val,
645 Boolean deep_p)
646 {
647 Arg al [256];
648 int ac;
649 Widget menu;
650 widget_value* contents;
651
652 if (val->this_one_change == NO_CHANGE)
653 return;
654
655 /* update the sensitivity and userdata */
656 /* Common to all widget types */
657 XtSetSensitive (widget, val->enabled);
658 XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
659
660 /* update the menu button as a label. */
661 if (val->this_one_change >= VISIBLE_CHANGE)
662 {
663 xm_update_label (instance, widget, val);
664 if (val->button_type)
665 xm_update_toggle (instance, widget, val);
666 }
667
668 /* update the pulldown/pullaside as needed */
669 ac = 0;
670 menu = NULL;
671 XtSetArg (al [ac], XmNsubMenuId, &menu); ac++;
672 XtGetValues (widget, al, ac);
673
674 contents = val->contents;
675
676 if (!menu)
677 {
678 if (contents)
679 {
680 unsigned int old_num_children, i;
681 Widget parent;
682 Widget *widget_list;
683
684 parent = XtParent (widget);
685 widget_list = XtCompositeChildren (parent, &old_num_children);
686
687 /* Find the widget position within the parent's widget list. */
688 for (i = 0; i < old_num_children; i++)
689 if (strcmp (XtName (widget_list[i]), XtName (widget)) == 0)
690 break;
691 if (i == old_num_children)
692 abort ();
693 if (XmIsCascadeButton (widget_list[i]))
694 {
695 menu = XmCreatePulldownMenu (parent, XtName(widget), NULL, 0);
696 make_menu_in_widget (instance, menu, contents, 0);
697 ac = 0;
698 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
699 XtSetValues (widget, al, ac);
700 }
701 else
702 {
703 Widget button;
704
705 /* The current menuitem is a XmPushButtonGadget, it
706 needs to be replaced by a CascadeButtonGadget */
707 XtDestroyWidget (widget_list[i]);
708 menu = XmCreatePulldownMenu (parent, val->name, NULL, 0);
709 make_menu_in_widget (instance, menu, contents, 0);
710 ac = 0;
711 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
712 /* Non-zero values don't work reliably in
713 conjunction with Emacs' event loop */
714 XtSetArg (al [ac], XmNmappingDelay, 0); ac++;
715 #ifdef XmNpositionIndex /* This is undefined on SCO ODT 2.0. */
716 /* Tell Motif to put it in the right place */
717 XtSetArg (al [ac], XmNpositionIndex , i); ac++;
718 #endif
719 button = XmCreateCascadeButton (parent, val->name, al, ac);
720 xm_update_label (instance, button, val);
721
722 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
723 (XtPointer)instance);
724 XtManageChild (button);
725 }
726
727 if (widget_list)
728 XtFree ((char*) widget_list);
729 }
730 }
731 else if (!contents)
732 {
733 ac = 0;
734 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
735 XtSetValues (widget, al, ac);
736 XtDestroyWidget (menu);
737 }
738 else if (deep_p && contents->change != NO_CHANGE)
739 xm_update_menu (instance, menu, val, 1);
740 }
741
742 static void
743 xm_update_menu (widget_instance* instance,
744 Widget widget,
745 widget_value* val,
746 Boolean deep_p)
747 {
748 Widget* children;
749 unsigned int num_children;
750 int num_children_to_keep = 0;
751 int i;
752 widget_value* cur;
753
754 children = XtCompositeChildren (widget, &num_children);
755
756 /* Widget is a RowColumn widget whose contents have to be updated
757 * to reflect the list of items in val->contents */
758
759 /* See how many buttons we can keep, and how many we
760 must completely replace. */
761 if (val->contents == 0)
762 num_children_to_keep = 0;
763 else if (val->contents->change == STRUCTURAL_CHANGE)
764 {
765 if (children)
766 {
767 for (i = 0, cur = val->contents;
768 (i < num_children
769 && cur); /* how else to ditch unwanted children ?? - mgd */
770 i++, cur = cur->next)
771 {
772 if (cur->this_one_change == STRUCTURAL_CHANGE)
773 break;
774 }
775
776 num_children_to_keep = i;
777 }
778 }
779 else
780 num_children_to_keep = num_children;
781
782 /* Update all the buttons of the RowColumn, in order,
783 except for those we are going to replace entirely. */
784 if (children)
785 {
786 for (i = 0, cur = val->contents; i < num_children_to_keep; i++)
787 {
788 if (!cur)
789 {
790 num_children_to_keep = i;
791 break;
792 }
793 if (children [i]->core.being_destroyed
794 || strcmp (XtName (children [i]), cur->name))
795 continue;
796 update_one_menu_entry (instance, children [i], cur, deep_p);
797 cur = cur->next;
798 }
799 }
800
801 /* Now replace from scratch all the buttons after the last
802 place that the top-level structure changed. */
803 if (val->contents->change == STRUCTURAL_CHANGE)
804 {
805 destroy_all_children (widget, num_children_to_keep);
806 make_menu_in_widget (instance, widget, val->contents,
807 num_children_to_keep);
808 }
809
810 XtFree ((char *) children);
811 }
812
813 \f
814 /* update text widgets */
815
816 static void
817 xm_update_text (widget_instance* instance,
818 Widget widget,
819 widget_value* val)
820 {
821 XmTextSetString (widget, val->value ? val->value : "");
822 XtRemoveAllCallbacks (widget, XmNactivateCallback);
823 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
824 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
825 XtAddCallback (widget, XmNvalueChangedCallback,
826 xm_internal_update_other_instances, instance);
827 }
828
829 static void
830 xm_update_text_field (widget_instance* instance,
831 Widget widget,
832 widget_value* val)
833 {
834 XmTextFieldSetString (widget, val->value ? val->value : "");
835 XtRemoveAllCallbacks (widget, XmNactivateCallback);
836 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
837 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
838 XtAddCallback (widget, XmNvalueChangedCallback,
839 xm_internal_update_other_instances, instance);
840 }
841
842 \f
843 /* update a motif widget */
844
845 void
846 xm_update_one_widget (widget_instance* instance,
847 Widget widget,
848 widget_value* val,
849 Boolean deep_p)
850 {
851 WidgetClass class;
852
853 /* Mark as not edited */
854 val->edited = False;
855
856 /* Common to all widget types */
857 XtSetSensitive (widget, val->enabled);
858 XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
859
860 /* Common to all label like widgets */
861 if (XtIsSubclass (widget, xmLabelWidgetClass))
862 xm_update_label (instance, widget, val);
863
864 class = XtClass (widget);
865 /* Class specific things */
866 if (class == xmPushButtonWidgetClass ||
867 class == xmArrowButtonWidgetClass)
868 {
869 xm_update_pushbutton (instance, widget, val);
870 }
871 else if (class == xmCascadeButtonWidgetClass)
872 {
873 xm_update_cascadebutton (instance, widget, val);
874 }
875 else if (class == xmToggleButtonWidgetClass
876 || class == xmToggleButtonGadgetClass)
877 {
878 xm_update_toggle (instance, widget, val);
879 }
880 else if (class == xmRowColumnWidgetClass)
881 {
882 Boolean radiobox = 0;
883 int ac = 0;
884 Arg al [1];
885
886 XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
887 XtGetValues (widget, al, ac);
888
889 if (radiobox)
890 xm_update_radiobox (instance, widget, val);
891 else
892 xm_update_menu (instance, widget, val, deep_p);
893 }
894 else if (class == xmTextWidgetClass)
895 {
896 xm_update_text (instance, widget, val);
897 }
898 else if (class == xmTextFieldWidgetClass)
899 {
900 xm_update_text_field (instance, widget, val);
901 }
902 else if (class == xmListWidgetClass)
903 {
904 xm_update_list (instance, widget, val);
905 }
906 }
907
908 \f/* getting the value back */
909 void
910 xm_update_one_value (widget_instance* instance,
911 Widget widget,
912 widget_value* val)
913 {
914 WidgetClass class = XtClass (widget);
915 widget_value *old_wv;
916
917 /* copy the call_data slot into the "return" widget_value */
918 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
919 if (!strcmp (val->name, old_wv->name))
920 {
921 val->call_data = old_wv->call_data;
922 break;
923 }
924
925 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
926 {
927 XtVaGetValues (widget, XmNset, &val->selected, NULL);
928 val->edited = True;
929 }
930 else if (class == xmTextWidgetClass)
931 {
932 free (val->value);
933 val->value = XmTextGetString (widget);
934 val->edited = True;
935 }
936 else if (class == xmTextFieldWidgetClass)
937 {
938 free (val->value);
939 val->value = XmTextFieldGetString (widget);
940 val->edited = True;
941 }
942 else if (class == xmRowColumnWidgetClass)
943 {
944 Boolean radiobox = 0;
945 int ac = 0;
946 Arg al [1];
947
948 XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
949 XtGetValues (widget, al, ac);
950
951 if (radiobox)
952 {
953 CompositeWidget radio = (CompositeWidget)widget;
954 int i;
955 for (i = 0; i < radio->composite.num_children; i++)
956 {
957 int set = False;
958 Widget toggle = radio->composite.children [i];
959
960 XtVaGetValues (toggle, XmNset, &set, NULL);
961 if (set)
962 {
963 free (val->value);
964 val->value = safe_strdup (XtName (toggle));
965 }
966 }
967 val->edited = True;
968 }
969 }
970 else if (class == xmListWidgetClass)
971 {
972 int pos_cnt;
973 int* pos_list;
974 if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
975 {
976 int i;
977 widget_value* cur;
978 for (cur = val->contents, i = 0; cur; cur = cur->next)
979 if (cur->value)
980 {
981 int j;
982 cur->selected = False;
983 i += 1;
984 for (j = 0; j < pos_cnt; j++)
985 if (pos_list [j] == i)
986 {
987 cur->selected = True;
988 val->value = safe_strdup (cur->name);
989 }
990 }
991 val->edited = 1;
992 XtFree ((char *) pos_list);
993 }
994 }
995 }
996
997 \f
998 /* This function is for activating a button from a program. It's wrong because
999 we pass a NULL argument in the call_data which is not Motif compatible.
1000 This is used from the XmNdefaultAction callback of the List widgets to
1001 have a double-click put down a dialog box like the button would do.
1002 I could not find a way to do that with accelerators.
1003 */
1004 static void
1005 activate_button (Widget widget,
1006 XtPointer closure,
1007 XtPointer call_data)
1008 {
1009 Widget button = (Widget)closure;
1010 XtCallCallbacks (button, XmNactivateCallback, NULL);
1011 }
1012
1013 /* creation functions */
1014
1015 /* Called for key press in dialogs. Used to pop down dialog on ESC. */
1016 static void
1017 dialog_key_cb (Widget widget,
1018 XtPointer closure,
1019 XEvent *event,
1020 Boolean *continue_to_dispatch)
1021 {
1022 KeySym sym = 0;
1023 Modifiers modif_ret;
1024
1025 XtTranslateKeycode (event->xkey.display, event->xkey.keycode, 0,
1026 &modif_ret, &sym);
1027
1028 if (sym == osfXK_Cancel)
1029 {
1030 Widget w = *((Widget *) closure);
1031
1032 while (w && ! XtIsShell (w))
1033 w = XtParent (w);
1034
1035 if (XtIsShell (w)) XtPopdown (w);
1036 }
1037
1038 *continue_to_dispatch = TRUE;
1039 }
1040
1041 /* dialogs */
1042 static Widget
1043 make_dialog (char* name,
1044 Widget parent,
1045 Boolean pop_up_p,
1046 char* shell_title,
1047 char* icon_name,
1048 Boolean text_input_slot,
1049 Boolean radio_box,
1050 Boolean list,
1051 int left_buttons,
1052 int right_buttons)
1053 {
1054 Widget result;
1055 Widget form;
1056 Widget row;
1057 Widget icon;
1058 Widget icon_separator;
1059 Widget message;
1060 Widget value = 0;
1061 Widget separator;
1062 Widget button = 0;
1063 Widget children [16]; /* for the final XtManageChildren */
1064 int n_children;
1065 Arg al[64]; /* Arg List */
1066 int ac; /* Arg Count */
1067 int i;
1068
1069 if (pop_up_p)
1070 {
1071 ac = 0;
1072 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1073 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1074 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1075 result = XmCreateDialogShell (parent, "dialog", al, ac);
1076 ac = 0;
1077 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1078 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1079 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1080 form = XmCreateForm (result, shell_title, al, ac);
1081 }
1082 else
1083 {
1084 ac = 0;
1085 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1086 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1087 form = XmCreateForm (parent, shell_title, al, ac);
1088 result = form;
1089 }
1090
1091 n_children = left_buttons + right_buttons + 1;
1092 ac = 0;
1093 XtSetArg(al[ac], XmNpacking, n_children == 3?
1094 XmPACK_COLUMN: XmPACK_TIGHT); ac++;
1095 XtSetArg(al[ac], XmNorientation, n_children == 3?
1096 XmVERTICAL: XmHORIZONTAL); ac++;
1097 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1098 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1099 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1100 XtSetArg(al[ac], XmNspacing, 13); ac++;
1101 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1102 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1103 XtSetArg(al[ac], XmNisAligned, True); ac++;
1104 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1105 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1106 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1107 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1108 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1109 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1110 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1111 row = XmCreateRowColumn (form, "row", al, ac);
1112
1113 n_children = 0;
1114 for (i = 0; i < left_buttons; i++)
1115 {
1116 char button_name [16];
1117 sprintf (button_name, "button%d", i + 1);
1118 ac = 0;
1119 if (i == 0)
1120 {
1121 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1122 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1123 }
1124 XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1125 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1126 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1127 XtAddEventHandler (children [n_children],
1128 KeyPressMask, False, dialog_key_cb, result);
1129
1130 if (i == 0)
1131 {
1132 button = children [n_children];
1133 ac = 0;
1134 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1135 XtSetValues (row, al, ac);
1136 }
1137
1138 n_children++;
1139 }
1140
1141 /* invisible separator button */
1142 ac = 0;
1143 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1144 children [n_children] = XmCreateLabel (row, "separator_button", al, ac);
1145 n_children++;
1146
1147 for (i = 0; i < right_buttons; i++)
1148 {
1149 char button_name [16];
1150 sprintf (button_name, "button%d", left_buttons + i + 1);
1151 ac = 0;
1152 XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1153 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1154 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1155 XtAddEventHandler (children [n_children],
1156 KeyPressMask, False, dialog_key_cb, result);
1157
1158 if (! button) button = children [n_children];
1159 n_children++;
1160 }
1161
1162 XtManageChildren (children, n_children);
1163
1164 ac = 0;
1165 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1166 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1167 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1168 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1169 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1170 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1171 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1172 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1173 separator = XmCreateSeparator (form, "", al, ac);
1174
1175 ac = 0;
1176 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1177 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1178 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1179 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1180 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1181 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1182 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1183 icon = XmCreateLabel (form, icon_name, al, ac);
1184
1185 ac = 0;
1186 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1187 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1188 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1189 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1190 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1191 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1192 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1193 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1194 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1195 icon_separator = XmCreateLabel (form, "", al, ac);
1196
1197 if (text_input_slot)
1198 {
1199 ac = 0;
1200 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1201 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1202 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1203 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1204 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1205 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1206 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1207 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1208 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1209 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1210 value = XmCreateTextField (form, "value", al, ac);
1211 }
1212 else if (radio_box)
1213 {
1214 Widget radio_butt;
1215 ac = 0;
1216 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1217 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1218 XtSetArg(al[ac], XmNspacing, 13); ac++;
1219 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1220 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1221 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1222 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1223 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1224 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1225 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1226 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1227 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1228 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1229 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1230 ac = 0;
1231 i = 0;
1232 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1233 children [i++] = radio_butt;
1234 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1235 children [i++] = radio_butt;
1236 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1237 children [i++] = radio_butt;
1238 XtManageChildren (children, i);
1239 }
1240 else if (list)
1241 {
1242 ac = 0;
1243 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1244 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1245 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1246 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1247 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1248 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1249 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1250 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1251 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1252 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1253 value = XmCreateScrolledList (form, "list", al, ac);
1254
1255 /* this is the easiest way I found to have the dble click in the
1256 list activate the default button */
1257 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1258 }
1259
1260 ac = 0;
1261 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1262 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1263 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1264 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1265 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1266 XtSetArg(al[ac], XmNbottomWidget,
1267 text_input_slot || radio_box || list ? value : separator); ac++;
1268 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1269 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1270 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1271 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1272 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1273 message = XmCreateLabel (form, "message", al, ac);
1274
1275 if (list)
1276 XtManageChild (value);
1277
1278 i = 0;
1279 children [i] = row; i++;
1280 children [i] = separator; i++;
1281 if (text_input_slot || radio_box)
1282 {
1283 children [i] = value; i++;
1284 }
1285 children [i] = message; i++;
1286 children [i] = icon; i++;
1287 children [i] = icon_separator; i++;
1288 XtManageChildren (children, i);
1289
1290 if (text_input_slot || list)
1291 {
1292 XtInstallAccelerators (value, button);
1293 XtSetKeyboardFocus (result, value);
1294 }
1295 else
1296 {
1297 XtInstallAccelerators (form, button);
1298 XtSetKeyboardFocus (result, button);
1299 }
1300
1301 return result;
1302 }
1303
1304 static destroyed_instance*
1305 find_matching_instance (widget_instance* instance)
1306 {
1307 destroyed_instance* cur;
1308 destroyed_instance* prev;
1309 char* type = instance->info->type;
1310 char* name = instance->info->name;
1311
1312 for (prev = NULL, cur = all_destroyed_instances;
1313 cur;
1314 prev = cur, cur = cur->next)
1315 {
1316 if (!strcmp (cur->name, name)
1317 && !strcmp (cur->type, type)
1318 && cur->parent == instance->parent
1319 && cur->pop_up_p == instance->pop_up_p)
1320 {
1321 if (prev)
1322 prev->next = cur->next;
1323 else
1324 all_destroyed_instances = cur->next;
1325 return cur;
1326 }
1327 /* do some cleanup */
1328 else if (!cur->widget)
1329 {
1330 if (prev)
1331 prev->next = cur->next;
1332 else
1333 all_destroyed_instances = cur->next;
1334 free_destroyed_instance (cur);
1335 cur = prev ? prev : all_destroyed_instances;
1336 }
1337 }
1338 return NULL;
1339 }
1340
1341 static void
1342 mark_dead_instance_destroyed (Widget widget,
1343 XtPointer closure,
1344 XtPointer call_data)
1345 {
1346 destroyed_instance* instance = (destroyed_instance*)closure;
1347 instance->widget = NULL;
1348 }
1349
1350 static void
1351 recenter_widget (Widget widget)
1352 {
1353 Widget parent = XtParent (widget);
1354 Screen* screen = XtScreen (widget);
1355 Dimension screen_width = WidthOfScreen (screen);
1356 Dimension screen_height = HeightOfScreen (screen);
1357 Dimension parent_width = 0;
1358 Dimension parent_height = 0;
1359 Dimension child_width = 0;
1360 Dimension child_height = 0;
1361 Position x;
1362 Position y;
1363
1364 XtVaGetValues (widget, XtNwidth, &child_width, XtNheight, &child_height, NULL);
1365 XtVaGetValues (parent, XtNwidth, &parent_width, XtNheight, &parent_height,
1366 NULL);
1367
1368 x = (((Position)parent_width) - ((Position)child_width)) / 2;
1369 y = (((Position)parent_height) - ((Position)child_height)) / 2;
1370
1371 XtTranslateCoords (parent, x, y, &x, &y);
1372
1373 if (x + child_width > screen_width)
1374 x = screen_width - child_width;
1375 if (x < 0)
1376 x = 0;
1377
1378 if (y + child_height > screen_height)
1379 y = screen_height - child_height;
1380 if (y < 0)
1381 y = 0;
1382
1383 XtVaSetValues (widget, XtNx, x, XtNy, y, NULL);
1384 }
1385
1386 static Widget
1387 recycle_instance (destroyed_instance* instance)
1388 {
1389 Widget widget = instance->widget;
1390
1391 /* widget is NULL if the parent was destroyed. */
1392 if (widget)
1393 {
1394 Widget focus;
1395 Widget separator;
1396
1397 /* Remove the destroy callback as the instance is not in the list
1398 anymore */
1399 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1400 mark_dead_instance_destroyed,
1401 (XtPointer)instance);
1402
1403 /* Give the focus to the initial item */
1404 focus = XtNameToWidget (widget, "*value");
1405 if (!focus)
1406 focus = XtNameToWidget (widget, "*button1");
1407 if (focus)
1408 XtSetKeyboardFocus (widget, focus);
1409
1410 /* shrink the separator label back to their original size */
1411 separator = XtNameToWidget (widget, "*separator_button");
1412 if (separator)
1413 XtVaSetValues (separator, XtNwidth, 5, XtNheight, 5, NULL);
1414
1415 /* Center the dialog in its parent */
1416 recenter_widget (widget);
1417 }
1418 free_destroyed_instance (instance);
1419 return widget;
1420 }
1421
1422 Widget
1423 xm_create_dialog (widget_instance* instance)
1424 {
1425 char* name = instance->info->type;
1426 Widget parent = instance->parent;
1427 Widget widget;
1428 Boolean pop_up_p = instance->pop_up_p;
1429 char* shell_name = 0;
1430 char* icon_name = 0;
1431 Boolean text_input_slot = False;
1432 Boolean radio_box = False;
1433 Boolean list = False;
1434 int total_buttons;
1435 int left_buttons = 0;
1436 int right_buttons = 1;
1437 destroyed_instance* dead_one;
1438
1439 /* try to find a widget to recycle */
1440 dead_one = find_matching_instance (instance);
1441 if (dead_one)
1442 {
1443 Widget recycled_widget = recycle_instance (dead_one);
1444 if (recycled_widget)
1445 return recycled_widget;
1446 }
1447
1448 switch (name [0]){
1449 case 'E': case 'e':
1450 icon_name = "dbox-error";
1451 shell_name = "Error";
1452 break;
1453
1454 case 'I': case 'i':
1455 icon_name = "dbox-info";
1456 shell_name = "Information";
1457 break;
1458
1459 case 'L': case 'l':
1460 list = True;
1461 icon_name = "dbox-question";
1462 shell_name = "Prompt";
1463 break;
1464
1465 case 'P': case 'p':
1466 text_input_slot = True;
1467 icon_name = "dbox-question";
1468 shell_name = "Prompt";
1469 break;
1470
1471 case 'Q': case 'q':
1472 icon_name = "dbox-question";
1473 shell_name = "Question";
1474 break;
1475 }
1476
1477 total_buttons = name [1] - '0';
1478
1479 if (name [3] == 'T' || name [3] == 't')
1480 {
1481 text_input_slot = False;
1482 radio_box = True;
1483 }
1484 else if (name [3])
1485 right_buttons = name [4] - '0';
1486
1487 left_buttons = total_buttons - right_buttons;
1488
1489 widget = make_dialog (name, parent, pop_up_p,
1490 shell_name, icon_name, text_input_slot, radio_box,
1491 list, left_buttons, right_buttons);
1492
1493 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1494 (XtPointer) instance);
1495
1496 return widget;
1497 }
1498
1499 /* Create a menu bar. We turn off the f10 key
1500 because we have not yet managed to make it work right in Motif. */
1501
1502 static Widget
1503 make_menubar (widget_instance* instance)
1504 {
1505 Arg al[3];
1506 int ac;
1507
1508 ac = 0;
1509 XtSetArg(al[ac], XmNmenuAccelerator, 0); ++ac;
1510 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1511 }
1512
1513 static void
1514 remove_grabs (Widget shell,
1515 XtPointer closure,
1516 XtPointer call_data)
1517 {
1518 Widget menu = (Widget) closure;
1519 XmRemoveFromPostFromList (menu, XtParent (XtParent (menu)));
1520 }
1521
1522 static Widget
1523 make_popup_menu (widget_instance* instance)
1524 {
1525 Widget parent = instance->parent;
1526 Window parent_window = parent->core.window;
1527 Widget result;
1528
1529 /* sets the parent window to 0 to fool Motif into not generating a grab */
1530 parent->core.window = 0;
1531 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1532 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1533 (XtPointer)result);
1534 parent->core.window = parent_window;
1535 return result;
1536 }
1537
1538 static Widget
1539 make_main (widget_instance* instance)
1540 {
1541 Widget parent = instance->parent;
1542 Widget result;
1543 Arg al[2];
1544 int ac;
1545
1546 ac = 0;
1547 XtSetArg (al[ac], XtNborderWidth, 0); ac++;
1548 XtSetArg (al[ac], XmNspacing, 0); ac++;
1549 result = XmCreateMainWindow (parent, instance->info->name, al, ac);
1550 return result;
1551 }
1552
1553 \f/* Table of functions to create widgets */
1554
1555 #ifdef ENERGIZE
1556
1557 /* interface with the XDesigner generated functions */
1558 typedef Widget (*widget_maker) (Widget);
1559 extern Widget create_project_p_sheet (Widget parent);
1560 extern Widget create_debugger_p_sheet (Widget parent);
1561 extern Widget create_breaklist_p_sheet (Widget parent);
1562 extern Widget create_le_browser_p_sheet (Widget parent);
1563 extern Widget create_class_browser_p_sheet (Widget parent);
1564 extern Widget create_call_browser_p_sheet (Widget parent);
1565 extern Widget create_build_dialog (Widget parent);
1566 extern Widget create_editmode_dialog (Widget parent);
1567 extern Widget create_search_dialog (Widget parent);
1568 extern Widget create_project_display_dialog (Widget parent);
1569
1570 static Widget
1571 make_one (widget_instance* instance, widget_maker fn)
1572 {
1573 Widget result;
1574 Arg al [64];
1575 int ac = 0;
1576
1577 if (instance->pop_up_p)
1578 {
1579 XtSetArg (al [ac], XmNallowShellResize, TRUE); ac++;
1580 result = XmCreateDialogShell (instance->parent, "dialog", NULL, 0);
1581 XtAddCallback (result, XmNpopdownCallback, &xm_nosel_callback,
1582 (XtPointer) instance);
1583 (*fn) (result);
1584 }
1585 else
1586 {
1587 result = (*fn) (instance->parent);
1588 XtRealizeWidget (result);
1589 }
1590 return result;
1591 }
1592
1593 static Widget
1594 make_project_p_sheet (widget_instance* instance)
1595 {
1596 return make_one (instance, create_project_p_sheet);
1597 }
1598
1599 static Widget
1600 make_debugger_p_sheet (widget_instance* instance)
1601 {
1602 return make_one (instance, create_debugger_p_sheet);
1603 }
1604
1605 static Widget
1606 make_breaklist_p_sheet (widget_instance* instance)
1607 {
1608 return make_one (instance, create_breaklist_p_sheet);
1609 }
1610
1611 static Widget
1612 make_le_browser_p_sheet (widget_instance* instance)
1613 {
1614 return make_one (instance, create_le_browser_p_sheet);
1615 }
1616
1617 static Widget
1618 make_class_browser_p_sheet (widget_instance* instance)
1619 {
1620 return make_one (instance, create_class_browser_p_sheet);
1621 }
1622
1623 static Widget
1624 make_call_browser_p_sheet (widget_instance* instance)
1625 {
1626 return make_one (instance, create_call_browser_p_sheet);
1627 }
1628
1629 static Widget
1630 make_build_dialog (widget_instance* instance)
1631 {
1632 return make_one (instance, create_build_dialog);
1633 }
1634
1635 static Widget
1636 make_editmode_dialog (widget_instance* instance)
1637 {
1638 return make_one (instance, create_editmode_dialog);
1639 }
1640
1641 static Widget
1642 make_search_dialog (widget_instance* instance)
1643 {
1644 return make_one (instance, create_search_dialog);
1645 }
1646
1647 static Widget
1648 make_project_display_dialog (widget_instance* instance)
1649 {
1650 return make_one (instance, create_project_display_dialog);
1651 }
1652
1653 #endif /* ENERGIZE */
1654
1655 widget_creation_entry
1656 xm_creation_table [] =
1657 {
1658 {"menubar", make_menubar},
1659 {"popup", make_popup_menu},
1660 {"main", make_main},
1661 #ifdef ENERGIZE
1662 {"project_p_sheet", make_project_p_sheet},
1663 {"debugger_p_sheet", make_debugger_p_sheet},
1664 {"breaklist_psheet", make_breaklist_p_sheet},
1665 {"leb_psheet", make_le_browser_p_sheet},
1666 {"class_browser_psheet", make_class_browser_p_sheet},
1667 {"ctree_browser_psheet", make_call_browser_p_sheet},
1668 {"build", make_build_dialog},
1669 {"editmode", make_editmode_dialog},
1670 {"search", make_search_dialog},
1671 {"project_display", make_project_display_dialog},
1672 #endif /* ENERGIZE */
1673 {NULL, NULL}
1674 };
1675
1676 \f/* Destruction of instances */
1677 void
1678 xm_destroy_instance ( widget_instance* instance)
1679 {
1680 Widget widget = instance->widget;
1681 /* recycle the dialog boxes */
1682 /* Disable the recycling until we can find a way to have the dialog box
1683 get reasonable layout after we modify its contents. */
1684 if (0
1685 && XtClass (widget) == xmDialogShellWidgetClass)
1686 {
1687 destroyed_instance* dead_instance =
1688 make_destroyed_instance (instance->info->name,
1689 instance->info->type,
1690 instance->widget,
1691 instance->parent,
1692 instance->pop_up_p);
1693 dead_instance->next = all_destroyed_instances;
1694 all_destroyed_instances = dead_instance;
1695 XtUnmanageChild (first_child (instance->widget));
1696 XFlush (XtDisplay (instance->widget));
1697 XtAddCallback (instance->parent, XtNdestroyCallback,
1698 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1699 }
1700 else
1701 {
1702 /* This might not be necessary now that the nosel is attached to
1703 popdown instead of destroy, but it can't hurt. */
1704 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1705 xm_nosel_callback, (XtPointer)instance);
1706 XtDestroyWidget (instance->widget);
1707 }
1708 }
1709
1710 \f/* popup utility */
1711 void
1712 xm_popup_menu (Widget widget, XEvent *event)
1713 {
1714 XButtonPressedEvent dummy;
1715
1716 if (event == 0)
1717 {
1718 dummy.type = ButtonPress;
1719 dummy.serial = 0;
1720 dummy.send_event = 0;
1721 dummy.display = XtDisplay (widget);
1722 dummy.window = XtWindow (XtParent (widget));
1723 dummy.time = 0;
1724 dummy.button = 0;
1725 XQueryPointer (dummy.display, dummy.window, &dummy.root,
1726 &dummy.subwindow, &dummy.x_root, &dummy.y_root,
1727 &dummy.x, &dummy.y, &dummy.state);
1728 event = (XEvent *) &dummy;
1729 }
1730
1731 if (event->type == ButtonPress || event->type == ButtonRelease)
1732 {
1733 /* Setting the menuPost resource only required by Motif 1.1 and
1734 LessTif 0.84 and earlier. With later versions of LessTif,
1735 setting menuPost is unnecessary and may cause problems, so
1736 don't do it. */
1737 #if XmVersion < 1002 || (defined LESSTIF_VERSION && LESSTIF_VERSION < 84)
1738 {
1739 /* This is so totally ridiculous: there's NO WAY to tell Motif
1740 that *any* button can select a menu item. Only one button
1741 can have that honor. */
1742
1743 char *trans = 0;
1744 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1745 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1746 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1747 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1748 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1749 if (trans) XtVaSetValues (widget, XmNmenuPost, trans, NULL);
1750 }
1751 #endif
1752
1753 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1754 }
1755
1756 XtManageChild (widget);
1757 }
1758
1759 static void
1760 set_min_dialog_size (Widget w)
1761 {
1762 short width;
1763 short height;
1764 XtVaGetValues (w, XmNwidth, &width, XmNheight, &height, NULL);
1765 XtVaSetValues (w, XmNminWidth, width, XmNminHeight, height, NULL);
1766 }
1767
1768 void
1769 xm_pop_instance (widget_instance* instance, Boolean up)
1770 {
1771 Widget widget = instance->widget;
1772
1773 if (XtClass (widget) == xmDialogShellWidgetClass)
1774 {
1775 Widget widget_to_manage = first_child (widget);
1776 if (up)
1777 {
1778 XtManageChild (widget_to_manage);
1779 set_min_dialog_size (widget);
1780 XtSetKeyboardFocus (instance->parent, widget);
1781 }
1782 else
1783 XtUnmanageChild (widget_to_manage);
1784 }
1785 else
1786 {
1787 if (up)
1788 XtManageChild (widget);
1789 else
1790 XtUnmanageChild (widget);
1791 }
1792 }
1793
1794 \f
1795 /* motif callback */
1796
1797 static void
1798 do_call (Widget widget,
1799 XtPointer closure,
1800 enum do_call_type type)
1801 {
1802 Arg al [256];
1803 int ac;
1804 XtPointer user_data;
1805 widget_instance* instance = (widget_instance*)closure;
1806 Widget instance_widget;
1807 LWLIB_ID id;
1808
1809 if (!instance)
1810 return;
1811 if (widget->core.being_destroyed)
1812 return;
1813
1814 instance_widget = instance->widget;
1815 if (!instance_widget)
1816 return;
1817
1818 id = instance->info->id;
1819 ac = 0;
1820 user_data = NULL;
1821 XtSetArg (al [ac], XmNuserData, &user_data); ac++;
1822 XtGetValues (widget, al, ac);
1823
1824 switch (type)
1825 {
1826 case pre_activate:
1827 if (instance->info->pre_activate_cb)
1828 instance->info->pre_activate_cb (widget, id, user_data);
1829 break;
1830
1831 case selection:
1832 if (instance->info->selection_cb)
1833 instance->info->selection_cb (widget, id, user_data);
1834 break;
1835
1836 case no_selection:
1837 if (instance->info->selection_cb)
1838 instance->info->selection_cb (widget, id, (XtPointer) -1);
1839 break;
1840
1841 case post_activate:
1842 if (instance->info->post_activate_cb)
1843 instance->info->post_activate_cb (widget, id, user_data);
1844 break;
1845
1846 default:
1847 abort ();
1848 }
1849 }
1850
1851 /* Like lw_internal_update_other_instances except that it does not do
1852 anything if its shell parent is not managed. This is to protect
1853 lw_internal_update_other_instances to dereference freed memory
1854 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1855 list */
1856 static void
1857 xm_internal_update_other_instances (Widget widget,
1858 XtPointer closure,
1859 XtPointer call_data)
1860 {
1861 Widget parent;
1862 for (parent = widget; parent; parent = XtParent (parent))
1863 if (XtIsShell (parent))
1864 break;
1865 else if (!XtIsManaged (parent))
1866 return;
1867 lw_internal_update_other_instances (widget, closure, call_data);
1868 }
1869
1870 static void
1871 xm_generic_callback (Widget widget,
1872 XtPointer closure,
1873 XtPointer call_data)
1874 {
1875 lw_internal_update_other_instances (widget, closure, call_data);
1876 do_call (widget, closure, selection);
1877 }
1878
1879 static void
1880 xm_nosel_callback (Widget widget,
1881 XtPointer closure,
1882 XtPointer call_data)
1883 {
1884 /* This callback is only called when a dialog box is dismissed with
1885 the wm's destroy button (WM_DELETE_WINDOW.) We want the dialog
1886 box to be destroyed in that case, not just unmapped, so that it
1887 releases its keyboard grabs. But there are problems with running
1888 our callbacks while the widget is in the process of being
1889 destroyed, so we set XmNdeleteResponse to XmUNMAP instead of
1890 XmDESTROY and then destroy it ourself after having run the
1891 callback. */
1892 do_call (widget, closure, no_selection);
1893 XtDestroyWidget (widget);
1894 }
1895
1896 static void
1897 xm_pull_down_callback (Widget widget,
1898 XtPointer closure,
1899 XtPointer call_data)
1900 {
1901 Widget parent = XtParent (widget);
1902
1903 if (XmIsRowColumn (parent))
1904 {
1905 unsigned char type = 0xff;
1906 XtVaGetValues (parent, XmNrowColumnType, &type, NULL);
1907 if (type == XmMENU_BAR)
1908 do_call (widget, closure, pre_activate);
1909 }
1910 }
1911
1912
1913 /* XmNpopdownCallback for MenuShell widgets. WIDGET is the MenuShell,
1914 CLOSURE is a pointer to the widget_instance of the shell,
1915
1916 Note that this callback is called for each cascade button in a
1917 menu, whether or not its submenu is visible. */
1918
1919 static void
1920 xm_pop_down_callback (Widget widget,
1921 XtPointer closure,
1922 XtPointer call_data)
1923 {
1924 widget_instance *instance = (widget_instance *) closure;
1925
1926 if ((!instance->pop_up_p && XtParent (widget) == instance->widget)
1927 || XtParent (widget) == instance->parent)
1928 do_call (widget, closure, post_activate);
1929 }
1930
1931 \f
1932 /* set the keyboard focus */
1933 void
1934 xm_set_keyboard_focus (Widget parent, Widget w)
1935 {
1936 XmProcessTraversal (w, 0);
1937 XtSetKeyboardFocus (parent, w);
1938 }
1939
1940 /* Motif hack to set the main window areas. */
1941 void
1942 xm_set_main_areas (Widget parent,
1943 Widget menubar,
1944 Widget work_area)
1945 {
1946 XmMainWindowSetAreas (parent,
1947 menubar, /* menubar (maybe 0) */
1948 0, /* command area (psheets) */
1949 0, /* horizontal scroll */
1950 0, /* vertical scroll */
1951 work_area); /* work area */
1952 }
1953
1954 /* Motif hack to control resizing on the menubar. */
1955 void
1956 xm_manage_resizing (Widget w, Boolean flag)
1957 {
1958 XtVaSetValues (w, XtNallowShellResize, flag, NULL);
1959 }
1960
1961 /* arch-tag: 73976f64-73b2-4600-aa13-d9ede20ee965
1962 (do not change this comment) */