Add prompt stack for recursive minibuffer
[sxemacs] / src / ui / lwlib / lwlib.c
1 /* A general interface to the widgets of different toolkits.
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 #ifdef NeXT
21 #undef __STRICT_BSD__           /* ick */
22 #endif
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <X11/StringDefs.h>
33 #include "lwlib-internal.h"
34 #include "lwlib-utils.h"
35
36 #ifdef NEED_LUCID
37 #include "lwlib-Xlw.h"
38 #endif
39 #ifdef NEED_MOTIF
40 #include "lwlib-Xm.h"
41 #ifdef LWLIB_WIDGETS_MOTIF
42 #include <Xm/Xm.h>
43 #endif
44 #endif
45 #ifdef NEED_ATHENA
46 #include "lwlib-Xaw.h"
47 #endif
48
49 #if defined HAVE_BDWGC && defined EF_USE_BDWGC
50 # if defined HAVE_GC_GC_H
51 #  include "gc/gc.h"
52 # elif defined HAVE_GC_H
53 #  include "gc.h"
54 # endif
55
56 # define xmalloc        GC_MALLOC
57 # define xmalloc_atomic GC_MALLOC_ATOMIC
58 # define xrealloc       GC_REALLOC
59 # define xfree(s)
60 # define xstrdup        GC_STRDUP
61 #else  /* !HAVE_BDWGC */
62 # define xmalloc        malloc
63 # define xmalloc_atomic malloc
64 # define xrealloc       realloc
65 # define xfree          free
66 # define xstrdup        strdup
67 #endif  /* HAVE_BDWGC */
68
69
70 /* #### Does a check need to be put back in here to make sure we have
71    sufficient defines to function properly or are the checks in the
72    makefile sufficient? */
73
74 /* List of all widgets managed by the library.  Note that each "widget"
75    listed here may actually be a tree of widgets; for example, a
76    single entry here might represent a single menubar or popup menu,
77    each of which might be implemented with a tree of widgets.
78    */
79 static widget_info *all_widget_info = NULL;
80
81 /* boolean flag indicating that the menubar is active */
82 int lw_menu_active = 0;
83
84 /* X11 menubar widget */
85 Widget lw_menubar_widget = NULL;
86
87 /* whether the last menu operation was a keyboard accelerator */
88 int lw_menu_accelerate = False;
89 \f
90 /* Forward declarations */
91 static void instantiate_widget_instance(widget_instance * instance);
92 static void free_widget_value_args(widget_value * wv);
93 \f
94 /* utility functions for widget_instance and widget_info */
95 static char *safe_strdup(const char *s)
96 {
97         char *result;
98         if (!s)
99                 return 0;
100         ssize_t len = strlen(s)+1;
101         result = (char *)xmalloc(len);
102         if (!result)
103                 return 0;
104         strncpy(result, s, len);
105         return result;
106 }
107
108 static inline void safe_free_str(char *s)
109 {
110         if (s) {
111                 xfree(s);
112         }
113 }
114
115 static widget_value *widget_value_free_list = 0;
116
117 widget_value *malloc_widget_value(void)
118 {
119         widget_value *wv;
120         if (widget_value_free_list) {
121                 wv = widget_value_free_list;
122                 widget_value_free_list = wv->free_list;
123                 wv->free_list = 0;
124         } else {
125                 wv = (widget_value *) xmalloc(sizeof(widget_value));
126         }
127         if (wv) {
128                 memset(wv, '\0', sizeof(widget_value));
129         }
130         return wv;
131 }
132
133 /* this is analogous to free().  It frees only what was allocated
134    by malloc_widget_value(), and no substructures.
135  */
136 void free_widget_value(widget_value * wv)
137 {
138         if (wv->free_list)
139                 abort();
140         wv->free_list = widget_value_free_list;
141         widget_value_free_list = wv;
142 }
143
144 static void free_widget_value_contents(widget_value * wv)
145 {
146         if (wv->name)
147                 xfree(wv->name);
148         if (wv->value)
149                 xfree(wv->value);
150         if (wv->key)
151                 xfree(wv->key);
152
153         /* #### - all of this 0xDEADBEEF stuff should be unnecessary
154            in production code...  it should be conditionalized. */
155         wv->name = wv->value = wv->key = (char *)0xDEADBEEF;
156
157         if (wv->toolkit_data && wv->free_toolkit_data) {
158                 XtFree((char *)wv->toolkit_data);
159                 wv->toolkit_data = (void *)0xDEADBEEF;
160         }
161 #ifdef NEED_SCROLLBARS
162         if (wv->scrollbar_data) {
163                 xfree(wv->scrollbar_data);
164                 wv->scrollbar_data = NULL;
165         }
166 #endif
167         if (wv->contents && (wv->contents != (widget_value *) 1)) {
168                 free_widget_value_tree(wv->contents);
169                 wv->contents = (widget_value *) 0xDEADBEEF;
170         }
171
172         free_widget_value_args(wv);
173
174         if (wv->next) {
175                 free_widget_value_tree(wv->next);
176                 wv->next = (widget_value *) 0xDEADBEEF;
177         }
178 }
179
180 void free_widget_value_tree(widget_value * wv)
181 {
182         if (!wv)
183                 return;
184
185         free_widget_value_contents(wv);
186         free_widget_value(wv);
187 }
188
189 #ifdef NEED_SCROLLBARS
190
191 static void copy_scrollbar_values(widget_value * val, widget_value * copy)
192 {
193         if (!copy->scrollbar_data)
194                 copy->scrollbar_data =
195                     (scrollbar_values *) xmalloc(sizeof(scrollbar_values));
196
197         if (val->scrollbar_data)
198                 *copy->scrollbar_data = *val->scrollbar_data;
199         else
200                 memset(copy->scrollbar_data, '\0', sizeof(scrollbar_values));
201 }
202
203 /*
204  * Return true if old->scrollbar_data were not equivalent
205  * to new->scrollbar_data.
206  */
207 static Boolean merge_scrollbar_values(widget_value * old, widget_value * new)
208 {
209         Boolean changed = False;
210
211         if (new->scrollbar_data && !old->scrollbar_data) {
212                 copy_scrollbar_values(new, old);
213                 changed = True;
214         } else if (!new->scrollbar_data && old->scrollbar_data) {
215                 xfree(old->scrollbar_data);
216                 old->scrollbar_data = NULL;
217         } else if (new->scrollbar_data && old->scrollbar_data) {
218                 scrollbar_values *old_sb = old->scrollbar_data;
219                 scrollbar_values *new_sb = new->scrollbar_data;
220
221                 if ((old_sb->line_increment != new_sb->line_increment) ||
222                     (old_sb->page_increment != new_sb->page_increment) ||
223                     (old_sb->minimum != new_sb->minimum) ||
224                     (old_sb->maximum != new_sb->maximum) ||
225                     (old_sb->slider_size != new_sb->slider_size) ||
226                     (old_sb->slider_position != new_sb->slider_position) ||
227                     (old_sb->scrollbar_width != new_sb->scrollbar_width) ||
228                     (old_sb->scrollbar_height != new_sb->scrollbar_height) ||
229                     (old_sb->scrollbar_x != new_sb->scrollbar_x) ||
230                     (old_sb->scrollbar_y != new_sb->scrollbar_y))
231                         changed = True;
232
233                 *old_sb = *new_sb;
234         }
235
236         return changed;
237 }
238
239 #endif                          /* NEED_SCROLLBARS */
240
241 #ifdef HAVE_WIDGETS
242 /*
243  * Return true if old->args was not equivalent
244  * to new->args.
245  */
246 static Boolean merge_widget_value_args(widget_value * old, widget_value * new)
247 {
248         Boolean changed = False;
249
250         if (new->args && !old->args) {
251                 lw_copy_widget_value_args(new, old);
252                 changed = True;
253         }
254         /* Generally we don't want to lose values that are already in the
255            widget. */
256         else if (!new->args && old->args) {
257                 lw_copy_widget_value_args(old, new);
258                 changed = True;
259         } else if (new->args && old->args && new->args != old->args) {
260                 /* #### Do something more sensible here than just copying the
261                    new values (like actually merging the values). */
262                 lw_copy_widget_value_args(new, old);
263                 changed = True;
264         } else if (new->args && new->args == old->args
265                    && new->args->args_changed == True) {
266                 changed = True;
267         }
268
269         return changed;
270 }
271 #endif                          /* HAVE_WIDGETS */
272
273 /* Make a complete copy of a widget_value tree.  Store CHANGE into
274    the widget_value tree's `change' field. */
275
276 widget_value *copy_widget_value_tree(widget_value * val, change_type change)
277 {
278         widget_value *copy;
279
280         if (!val)
281                 return NULL;
282         if (val == (widget_value *) 1)
283                 return val;
284
285         copy = malloc_widget_value();
286         if (copy) {
287                 /* #### - don't seg fault *here* if out of memory.  Menus will be
288                    truncated inexplicably. */
289                 copy->type = val->type;
290                 copy->name = safe_strdup(val->name);
291                 copy->value = safe_strdup(val->value);
292                 copy->key = safe_strdup(val->key);
293                 copy->accel = val->accel;
294                 copy->enabled = val->enabled;
295                 copy->selected = val->selected;
296                 copy->edited = False;
297                 copy->change = change;
298                 copy->contents = copy_widget_value_tree(val->contents, change);
299                 copy->call_data = val->call_data;
300                 copy->next = copy_widget_value_tree(val->next, change);
301                 copy->toolkit_data = NULL;
302                 copy->free_toolkit_data = False;
303
304                 lw_copy_widget_value_args(val, copy);
305 #ifdef NEED_SCROLLBARS
306                 copy_scrollbar_values(val, copy);
307 #endif
308         }
309         return copy;
310 }
311
312 /* This function is used to implement incremental menu construction. */
313
314 widget_value *replace_widget_value_tree(widget_value * node,
315                                         widget_value * newtree)
316 {
317         widget_value *copy;
318
319         if (!node || !newtree)
320                 abort();
321
322         copy = copy_widget_value_tree(newtree, STRUCTURAL_CHANGE);
323
324         free_widget_value_contents(node);
325         *node = *copy;
326         free_widget_value(copy);        /* free the node, but not its contents. */
327         return node;
328 }
329
330 static widget_info *allocate_widget_info(const char *type, const char *name,
331                                          LWLIB_ID id, widget_value * val,
332                                          lw_callback pre_activate_cb,
333                                          lw_callback selection_cb,
334                                          lw_callback post_activate_cb)
335 {
336         widget_info *info = (widget_info *) xmalloc(sizeof(widget_info));
337         info->type = safe_strdup(type);
338         info->name = safe_strdup(name);
339         info->id = id;
340         info->val = copy_widget_value_tree(val, STRUCTURAL_CHANGE);
341         info->busy = False;
342         info->pre_activate_cb = pre_activate_cb;
343         info->selection_cb = selection_cb;
344         info->post_activate_cb = post_activate_cb;
345         info->instances = NULL;
346
347         info->next = all_widget_info;
348         all_widget_info = info;
349
350         return info;
351 }
352
353 static void free_widget_info(widget_info * info)
354 {
355         safe_free_str(info->type);
356         safe_free_str(info->name);
357         free_widget_value_tree(info->val);
358         memset(info, '\0', sizeof(widget_info));
359         xfree(info);
360 }
361
362 static void
363 mark_widget_destroyed(Widget widget, XtPointer closure, XtPointer call_data)
364 {
365         widget_instance *instance = (widget_instance *) closure;
366
367         /* be very conservative */
368         if (instance->widget == widget)
369                 instance->widget = NULL;
370 }
371
372 static widget_instance *allocate_widget_instance(widget_info * info,
373                                                  Widget parent,
374                                                  Boolean pop_up_p)
375 {
376         widget_instance *instance =
377             (widget_instance *) xmalloc(sizeof(widget_instance));
378         instance->parent = parent;
379         instance->pop_up_p = pop_up_p;
380         instance->info = info;
381         instance->next = info->instances;
382         info->instances = instance;
383
384         instantiate_widget_instance(instance);
385
386         XtAddCallback(instance->widget, XtNdestroyCallback,
387                       mark_widget_destroyed, (XtPointer) instance);
388         return instance;
389 }
390
391 static void free_widget_instance(widget_instance * instance)
392 {
393         memset(instance, '\0', sizeof(widget_instance));
394         xfree(instance);
395 }
396
397 static widget_info *get_widget_info(LWLIB_ID id, Boolean remove_p)
398 {
399         widget_info *info;
400         widget_info *prev;
401         for (prev = NULL, info = all_widget_info;
402              info; prev = info, info = info->next)
403                 if (info->id == id) {
404                         if (remove_p) {
405                                 if (prev)
406                                         prev->next = info->next;
407                                 else
408                                         all_widget_info = info->next;
409                         }
410                         return info;
411                 }
412         return NULL;
413 }
414
415 /* Internal function used by the library dependent implementation to get the
416    widget_value for a given widget in an instance */
417 widget_info *lw_get_widget_info(LWLIB_ID id)
418 {
419         return get_widget_info(id, 0);
420 }
421
422 static int
423 map_widget_values(widget_value * value, int (*mapfunc) (widget_value * value,
424                                                         void *closure),
425                   void *closure)
426 {
427         int retval = 0;
428
429         if (value->contents)
430                 retval = map_widget_values(value->contents, mapfunc, closure);
431         if (retval)
432                 return retval;
433
434         if (value->next)
435                 retval = map_widget_values(value->next, mapfunc, closure);
436         if (retval)
437                 return retval;
438
439         return (mapfunc) (value, closure);
440 }
441
442 int
443 lw_map_widget_values(LWLIB_ID id, int (*mapfunc) (widget_value * value,
444                                                   void *closure), void *closure)
445 {
446         widget_info *info = get_widget_info(id, 0);
447
448         if (!info)
449                 abort();
450
451         if (info->val)
452                 return map_widget_values(info->val, mapfunc, closure);
453         return 0;
454 }
455
456 static widget_instance *get_widget_instance(Widget widget, Boolean remove_p)
457 {
458         widget_info *info;
459         widget_instance *instance;
460         widget_instance *prev;
461         for (info = all_widget_info; info; info = info->next)
462                 for (prev = NULL, instance = info->instances;
463                      instance; prev = instance, instance = instance->next)
464                         if (instance->widget == widget) {
465                                 if (remove_p) {
466                                         if (prev)
467                                                 prev->next = instance->next;
468                                         else
469                                                 info->instances =
470                                                     instance->next;
471                                 }
472                                 return instance;
473                         }
474         return (widget_instance *) 0;
475 }
476
477 static widget_instance *find_instance(LWLIB_ID id, Widget parent,
478                                       Boolean pop_up_p)
479 {
480         widget_info *info = get_widget_info(id, False);
481         widget_instance *instance;
482
483         if (info)
484                 for (instance = info->instances; instance;
485                      instance = instance->next)
486                         if (instance->parent == parent
487                             && instance->pop_up_p == pop_up_p)
488                                 return instance;
489
490         return NULL;
491 }
492 \f
493 /* utility function for widget_value */
494 static Boolean safe_strcmp(const char *s1, const char *s2)
495 {
496         if (!!s1 ^ !!s2)
497                 return True;
498         return (s1 && s2) ? strcmp(s1, s2) : s1 ? False : !!s2;
499 }
500
501 static change_type max(change_type i1, change_type i2)
502 {
503         return (int)i1 > (int)i2 ? i1 : i2;
504 }
505
506 #if 0
507 # define EXPLAIN(name, oc, nc, desc, a1, a2)                            \
508    printf ("Change: \"%s\"\tmax(%s=%d,%s=%d)\t%s %d %d\n",              \
509            name,                                                        \
510            (oc == NO_CHANGE ? "none" :                                  \
511             (oc == INVISIBLE_CHANGE ? "invisible" :                     \
512              (oc == VISIBLE_CHANGE ? "visible" :                        \
513               (oc == STRUCTURAL_CHANGE ? "structural" : "???")))),      \
514            oc,                                                          \
515            (nc == NO_CHANGE ? "none" :                                  \
516             (nc == INVISIBLE_CHANGE ? "invisible" :                     \
517              (nc == VISIBLE_CHANGE ? "visible" :                        \
518               (nc == STRUCTURAL_CHANGE ? "structural" : "???")))),      \
519            nc, desc, a1, a2)
520 #else
521 # define EXPLAIN(name, oc, nc, desc, a1, a2)
522 #endif
523
524 static widget_value *merge_widget_value(widget_value * val1,
525                                         widget_value * val2, int level)
526 {
527         change_type change;
528         widget_value *merged_next;
529         widget_value *merged_contents;
530
531         if (!val1) {
532                 if (val2)
533                         return copy_widget_value_tree(val2, STRUCTURAL_CHANGE);
534                 else
535                         return NULL;
536         }
537         if (!val2) {
538                 free_widget_value_tree(val1);
539                 return NULL;
540         }
541
542         change = NO_CHANGE;
543
544         if (val1->type != val2->type) {
545                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE, "type change",
546                         val1->type, val2->type);
547                 change = max(change, STRUCTURAL_CHANGE);
548                 val1->type = val2->type;
549         }
550         if (safe_strcmp(val1->name, val2->name)) {
551                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE, "name change",
552                         val1->name, val2->name);
553                 change = max(change, STRUCTURAL_CHANGE);
554                 safe_free_str(val1->name);
555                 val1->name = safe_strdup(val2->name);
556         }
557         if (safe_strcmp(val1->value, val2->value)) {
558                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "value change",
559                         val1->value, val2->value);
560                 change = max(change, VISIBLE_CHANGE);
561                 safe_free_str(val1->value);
562                 val1->value = safe_strdup(val2->value);
563         }
564         if (safe_strcmp(val1->key, val2->key)) {
565                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "key change",
566                         val1->key, val2->key);
567                 change = max(change, VISIBLE_CHANGE);
568                 safe_free_str(val1->key);
569                 val1->key = safe_strdup(val2->key);
570         }
571         if (val1->accel != val2->accel) {
572                 EXPLAIN(val1->name, change, VISIBLE_CHANGE,
573                         "accelerator change", val1->accel, val2->accel);
574                 change = max(change, VISIBLE_CHANGE);
575                 val1->accel = val2->accel;
576         }
577         if (val1->enabled != val2->enabled) {
578                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "enablement change",
579                         val1->enabled, val2->enabled);
580                 change = max(change, VISIBLE_CHANGE);
581                 val1->enabled = val2->enabled;
582         }
583         if (val1->selected != val2->selected) {
584                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "selection change",
585                         val1->selected, val2->selected);
586                 change = max(change, VISIBLE_CHANGE);
587                 val1->selected = val2->selected;
588         }
589         if (val1->call_data != val2->call_data) {
590                 EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
591                         "call-data change", val1->call_data, val2->call_data);
592                 change = max(change, INVISIBLE_CHANGE);
593                 val1->call_data = val2->call_data;
594         }
595 #ifdef HAVE_WIDGETS
596         if (merge_widget_value_args(val1, val2)) {
597                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "widget change", 0,
598                         0);
599                 change = max(change, VISIBLE_CHANGE);
600         }
601 #endif
602
603 #ifdef NEED_SCROLLBARS
604         if (merge_scrollbar_values(val1, val2)) {
605                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "scrollbar change",
606                         0, 0);
607                 change = max(change, VISIBLE_CHANGE);
608         }
609 #endif
610
611         if (level > 0) {
612                 merged_contents =
613                     merge_widget_value(val1->contents, val2->contents,
614                                        level - 1);
615
616                 if (val1->contents && !merged_contents) {
617                         EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
618                                 "(contents gone)", 0, 0);
619                         change = max(change, INVISIBLE_CHANGE);
620                 } else if (merged_contents
621                            && merged_contents->change != NO_CHANGE) {
622                         EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
623                                 "(contents change)", 0, 0);
624                         change = max(change, INVISIBLE_CHANGE);
625                 }
626
627                 val1->contents = merged_contents;
628         }
629
630         merged_next = merge_widget_value(val1->next, val2->next, level);
631
632         if (val1->next && !merged_next) {
633                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE,
634                         "(following gone)", 0, 0);
635                 change = max(change, STRUCTURAL_CHANGE);
636         } else if (merged_next) {
637                 if (merged_next->change) {
638                         EXPLAIN(val1->name, change, merged_next->change,
639                                 "(following change)", 0, 0);
640                 }
641                 change = max(change, merged_next->change);
642         }
643
644         val1->next = merged_next;
645
646         val1->change = change;
647
648         if (change > NO_CHANGE && val1->toolkit_data) {
649                 if (val1->free_toolkit_data)
650                         XtFree((char *)val1->toolkit_data);
651                 val1->toolkit_data = NULL;
652         }
653
654         return val1;
655 }
656 \f
657 /* modifying the widgets */
658 static Widget name_to_widget(widget_instance * instance, const char *name)
659 {
660         Widget widget = NULL;
661
662         if (!instance->widget)
663                 return NULL;
664
665         if (!strcmp(XtName(instance->widget), name))
666                 widget = instance->widget;
667         else {
668                 int length = strlen(name) + 2;
669                 char *real_name = (char *)alloca(length);
670                 real_name[0] = '*';
671                 strcpy(real_name + 1, name);
672
673                 widget = XtNameToWidget(instance->widget, real_name);
674         }
675         return widget;
676 }
677
678 static void
679 set_one_value(widget_instance * instance, widget_value * val, Boolean deep_p)
680 {
681         Widget widget = name_to_widget(instance, val->name);
682
683         if (widget) {
684 #ifdef NEED_LUCID
685                 if (lw_lucid_widget_p(instance->widget))
686                         xlw_update_one_widget(instance, widget, val, deep_p);
687 #endif
688 #ifdef NEED_MOTIF
689                 if (lw_motif_widget_p(instance->widget))
690                         xm_update_one_widget(instance, widget, val, deep_p);
691 #endif
692 #ifdef NEED_ATHENA
693                 if (lw_xaw_widget_p(instance->widget))
694                         xaw_update_one_widget(instance, widget, val, deep_p);
695 #endif
696         }
697 }
698
699 static void
700 update_one_widget_instance(widget_instance * instance, Boolean deep_p)
701 {
702         widget_value *val;
703
704         if (!instance->widget)
705                 /* the widget was destroyed */
706                 return;
707
708         for (val = instance->info->val; val; val = val->next)
709                 if (val->change != NO_CHANGE)
710                         set_one_value(instance, val, deep_p);
711 }
712
713 static void update_all_widget_values(widget_info * info, Boolean deep_p)
714 {
715         widget_instance *instance;
716         widget_value *val;
717
718         for (instance = info->instances; instance; instance = instance->next)
719                 update_one_widget_instance(instance, deep_p);
720
721         for (val = info->val; val; val = val->next) {
722                 val->change = NO_CHANGE;
723                 if (val->args)
724                         val->args->args_changed = False;
725         }
726 }
727
728 void lw_modify_all_widgets(LWLIB_ID id, widget_value * val, Boolean deep_p)
729 {
730         widget_info *info = get_widget_info(id, False);
731         widget_value *new_val;
732         widget_value *next_new_val;
733         widget_value *cur;
734         widget_value *prev;
735         widget_value *next;
736         int found;
737
738         if (!info)
739                 return;
740
741         for (new_val = val; new_val; new_val = new_val->next) {
742                 next_new_val = new_val->next;
743                 new_val->next = NULL;
744                 found = False;
745                 for (prev = NULL, cur = info->val; cur;
746                      prev = cur, cur = cur->next)
747                         if (!strcmp(cur->name, new_val->name)) {
748                                 found = True;
749                                 next = cur->next;
750                                 cur->next = NULL;
751                                 cur =
752                                     merge_widget_value(cur, new_val,
753                                                        deep_p ? 1000 : 1);
754                                 if (prev)
755                                         prev->next = cur ? cur : next;
756                                 else
757                                         info->val = cur ? cur : next;
758                                 if (cur)
759                                         cur->next = next;
760                                 break;
761                         }
762                 if (!found) {
763                         /* Could not find it, add it */
764                         if (prev)
765                                 prev->next =
766                                     copy_widget_value_tree(new_val,
767                                                            STRUCTURAL_CHANGE);
768                         else
769                                 info->val =
770                                     copy_widget_value_tree(new_val,
771                                                            STRUCTURAL_CHANGE);
772                 }
773                 new_val->next = next_new_val;
774         }
775
776         update_all_widget_values(info, deep_p);
777 }
778 \f
779 /* creating the widgets */
780
781 static void initialize_widget_instance(widget_instance * instance)
782 {
783         widget_value *val;
784
785         for (val = instance->info->val; val; val = val->next)
786                 val->change = STRUCTURAL_CHANGE;
787
788         update_one_widget_instance(instance, True);
789
790         for (val = instance->info->val; val; val = val->next) {
791                 val->change = NO_CHANGE;
792                 if (val->args)
793                         val->args->args_changed = False;
794         }
795 }
796
797 /* strcasecmp() is not sufficiently portable or standard,
798    and it's easier just to write our own. */
799 static int ascii_strcasecmp(const char *s1, const char *s2)
800 {
801         while (1) {
802                 char c1 = *s1++;
803                 char c2 = *s2++;
804                 if (c1 >= 'A' && c1 <= 'Z')
805                         c1 += 'a' - 'A';
806                 if (c2 >= 'A' && c2 <= 'Z')
807                         c2 += 'a' - 'A';
808                 if (c1 != c2)
809                         return c1 - c2;
810                 if (c1 == '\0')
811                         return 0;
812         }
813 }
814
815 static widget_creation_function
816 find_in_table(const char *type, const widget_creation_entry table[])
817 {
818         const widget_creation_entry *cur;
819         for (cur = table; cur->type; cur++)
820                 if (!ascii_strcasecmp(type, cur->type))
821                         return cur->function;
822         return NULL;
823 }
824
825 static Boolean dialog_spec_p(const char *name)
826 {
827         /* return True if name matches [EILPQeilpq][1-9][Bb] or
828            [EILPQeilpq][1-9][Bb][Rr][1-9] */
829         if (!name)
830                 return False;
831
832         switch (name[0]) {
833         case 'E':
834         case 'I':
835         case 'L':
836         case 'P':
837         case 'Q':
838         case 'e':
839         case 'i':
840         case 'l':
841         case 'p':
842         case 'q':
843                 if (name[1] >= '0' && name[1] <= '9') {
844                         if (name[2] != 'B' && name[2] != 'b')
845                                 return False;
846                         if (!name[3])
847                                 return True;
848                         if ((name[3] == 'T' || name[3] == 't') && !name[4])
849                                 return True;
850                         if ((name[3] == 'R' || name[3] == 'r')
851                             && name[4] >= '0' && name[4] <= '9' && !name[5])
852                                 return True;
853                         return False;
854                 } else
855                         return False;
856
857         default:
858                 return False;
859         }
860 }
861
862 static void instantiate_widget_instance(widget_instance * instance)
863 {
864         widget_creation_function function = NULL;
865
866 #ifdef NEED_LUCID
867         if (!function)
868                 function =
869                     find_in_table(instance->info->type, xlw_creation_table);
870 #endif
871 #ifdef NEED_MOTIF
872         if (!function)
873                 function =
874                     find_in_table(instance->info->type, xm_creation_table);
875 #endif
876 #ifdef NEED_ATHENA
877         if (!function)
878                 function =
879                     find_in_table(instance->info->type, xaw_creation_table);
880 #endif
881
882         if (!function) {
883                 if (dialog_spec_p(instance->info->type)) {
884 #ifdef LWLIB_DIALOGS_MOTIF
885                         if (!function)
886                                 function = xm_create_dialog;
887 #endif
888 #ifdef LWLIB_DIALOGS_ATHENA
889                         if (!function)
890                                 function = xaw_create_dialog;
891 #endif
892                 }
893         }
894
895         if (!function) {
896                 fprintf(stderr, "No creation function for widget type %s\n",
897                         instance->info->type);
898                 abort();
899         }
900
901         instance->widget = (*function) (instance);
902
903         if (!instance->widget)
904                 abort();
905
906         /*   XtRealizeWidget (instance->widget); */
907 }
908
909 void
910 lw_register_widget(const char *type, const char *name,
911                    LWLIB_ID id, widget_value * val,
912                    lw_callback pre_activate_cb, lw_callback selection_cb,
913                    lw_callback post_activate_cb)
914 {
915         if (!get_widget_info(id, False))
916                 allocate_widget_info(type, name, id, val, pre_activate_cb,
917                                      selection_cb, post_activate_cb);
918 }
919
920 Widget lw_get_widget(LWLIB_ID id, Widget parent, Boolean pop_up_p)
921 {
922         widget_instance *instance = find_instance(id, parent, pop_up_p);
923         return instance ? instance->widget : NULL;
924 }
925
926 Widget lw_make_widget(LWLIB_ID id, Widget parent, Boolean pop_up_p)
927 {
928         widget_instance *instance = find_instance(id, parent, pop_up_p);
929
930         if (!instance) {
931                 widget_info *info = get_widget_info(id, False);
932                 if (!info)
933                         return NULL;
934                 instance = allocate_widget_instance(info, parent, pop_up_p);
935                 initialize_widget_instance(instance);
936         }
937         if (!instance->widget)
938                 abort();
939         return instance->widget;
940 }
941
942 Widget
943 lw_create_widget(const char *type, const char *name,
944                  LWLIB_ID id, widget_value * val,
945                  Widget parent, Boolean pop_up_p, lw_callback pre_activate_cb,
946                  lw_callback selection_cb, lw_callback post_activate_cb)
947 {
948         lw_register_widget(type, name, id, val, pre_activate_cb, selection_cb,
949                            post_activate_cb);
950         return lw_make_widget(id, parent, pop_up_p);
951 }
952 \f
953 /* destroying the widgets */
954 static void destroy_one_instance(widget_instance * instance)
955 {
956         /* Remove the destroy callback on the widget; that callback will try to
957            dereference the instance object (to set its widget slot to 0, since the
958            widget is dead.)  Since the instance is now dead, we don't have to worry
959            about the fact that its widget is dead too.
960
961            This happens in the Phase2Destroy of the widget, so this callback would
962            not have been run until arbitrarily long after the instance was freed.
963          */
964         if (instance->widget)
965                 XtRemoveCallback(instance->widget, XtNdestroyCallback,
966                                  mark_widget_destroyed, (XtPointer) instance);
967
968         if (instance->widget) {
969                 /* The else are pretty tricky here, including the empty statement
970                    at the end because it would be very bad to destroy a widget
971                    twice. */
972 #ifdef NEED_LUCID
973                 if (lw_lucid_widget_p(instance->widget))
974                         xlw_destroy_instance(instance);
975                 else
976 #endif
977 #ifdef NEED_MOTIF
978                 if (lw_motif_widget_p(instance->widget))
979                         xm_destroy_instance(instance);
980                 else
981 #endif
982 #ifdef NEED_ATHENA
983                 if (lw_xaw_widget_p(instance->widget))
984                         xaw_destroy_instance(instance);
985                 else
986 #endif
987                 {
988                         /* do not remove the empty statement */
989                         ;
990                 }
991         }
992
993         free_widget_instance(instance);
994 }
995
996 void lw_destroy_widget(Widget w)
997 {
998         widget_instance *instance = get_widget_instance(w, True);
999
1000         if (instance) {
1001                 widget_info *info = instance->info;
1002                 /* instance has already been removed from the list; free it */
1003                 destroy_one_instance(instance);
1004                 /* if there are no instances left, free the info too */
1005                 if (!info->instances)
1006                         lw_destroy_all_widgets(info->id);
1007         }
1008 }
1009
1010 void lw_destroy_all_widgets(LWLIB_ID id)
1011 {
1012         widget_info *info = get_widget_info(id, True);
1013         widget_instance *instance;
1014         widget_instance *next;
1015
1016         if (info) {
1017                 for (instance = info->instances; instance;) {
1018                         next = instance->next;
1019                         destroy_one_instance(instance);
1020                         instance = next;
1021                 }
1022                 free_widget_info(info);
1023         }
1024 }
1025
1026 void lw_destroy_everything(void)
1027 {
1028         while (all_widget_info)
1029                 lw_destroy_all_widgets(all_widget_info->id);
1030 }
1031
1032 void lw_destroy_all_pop_ups(void)
1033 {
1034         widget_info *info;
1035         widget_info *next;
1036         widget_instance *instance;
1037
1038         for (info = all_widget_info; info; info = next) {
1039                 next = info->next;
1040                 instance = info->instances;
1041                 if (instance && instance->pop_up_p)
1042                         lw_destroy_all_widgets(info->id);
1043         }
1044 }
1045
1046 Widget lw_raise_all_pop_up_widgets(void)
1047 {
1048         widget_info *info;
1049         widget_instance *instance;
1050         Widget result = NULL;
1051
1052         for (info = all_widget_info; info; info = info->next)
1053                 for (instance = info->instances; instance;
1054                      instance = instance->next)
1055                         if (instance->pop_up_p) {
1056                                 Widget widget = instance->widget;
1057                                 if (widget) {
1058                                         if (XtIsManaged(widget)
1059 #ifdef NEED_MOTIF
1060                                             /* What a complete load of crap!!!!
1061                                                When a dialogShell is on the screen, it is not managed!
1062                                              */
1063                                             ||
1064                                             (lw_motif_widget_p(instance->widget)
1065                                              &&
1066                                              XtIsManaged(first_child(widget)))
1067 #endif
1068                                             ) {
1069                                                 if (!result)
1070                                                         result = widget;
1071                                                 XMapRaised(XtDisplay(widget),
1072                                                            XtWindow(widget));
1073                                         }
1074                                 }
1075                         }
1076         return result;
1077 }
1078
1079 static void lw_pop_all_widgets(LWLIB_ID id, Boolean up)
1080 {
1081         widget_info *info = get_widget_info(id, False);
1082         widget_instance *instance;
1083
1084         if (info)
1085                 for (instance = info->instances; instance;
1086                      instance = instance->next)
1087                         if (instance->pop_up_p && instance->widget) {
1088 #ifdef NEED_LUCID
1089                                 if (lw_lucid_widget_p(instance->widget)) {
1090                                         XtRealizeWidget(instance->widget);
1091                                         xlw_pop_instance(instance, up);
1092                                 }
1093 #endif
1094 #ifdef NEED_MOTIF
1095                                 if (lw_motif_widget_p(instance->widget)) {
1096                                         XtRealizeWidget(instance->widget);
1097                                         xm_pop_instance(instance, up);
1098                                 }
1099 #endif
1100 #ifdef NEED_ATHENA
1101                                 if (lw_xaw_widget_p(instance->widget)) {
1102                                         XtRealizeWidget(XtParent
1103                                                         (instance->widget));
1104                                         XtRealizeWidget(instance->widget);
1105                                         xaw_pop_instance(instance, up);
1106                                 }
1107 #endif
1108                         }
1109 }
1110
1111 void lw_pop_up_all_widgets(LWLIB_ID id)
1112 {
1113         lw_pop_all_widgets(id, True);
1114 }
1115
1116 void lw_pop_down_all_widgets(LWLIB_ID id)
1117 {
1118         lw_pop_all_widgets(id, False);
1119 }
1120
1121 void lw_popup_menu(Widget widget, XEvent * event)
1122 {
1123 #ifdef LWLIB_MENUBARS_LUCID
1124         if (lw_lucid_widget_p(widget))
1125                 xlw_popup_menu(widget, event);
1126 #endif
1127 #ifdef LWLIB_MENUBARS_MOTIF
1128         if (lw_motif_widget_p(widget))
1129                 xm_popup_menu(widget, event);
1130 #endif
1131 #ifdef LWLIB_MENUBARS_ATHENA
1132         if (lw_xaw_widget_p(widget))
1133                 xaw_popup_menu(widget, event);  /* not implemented */
1134 #endif
1135 }
1136 \f
1137 /* get the values back */
1138 static Boolean get_one_value(widget_instance * instance, widget_value * val)
1139 {
1140         Widget widget = name_to_widget(instance, val->name);
1141
1142         if (widget) {
1143 #ifdef NEED_LUCID
1144                 if (lw_lucid_widget_p(instance->widget))
1145                         xlw_update_one_value(instance, widget, val);
1146 #endif
1147 #ifdef NEED_MOTIF
1148                 if (lw_motif_widget_p(instance->widget))
1149                         xm_update_one_value(instance, widget, val);
1150 #endif
1151 #ifdef NEED_ATHENA
1152                 if (lw_xaw_widget_p(instance->widget))
1153                         xaw_update_one_value(instance, widget, val);
1154 #endif
1155                 return True;
1156         } else
1157                 return False;
1158 }
1159
1160 Boolean lw_get_some_values(LWLIB_ID id, widget_value * val_out)
1161 {
1162         widget_info *info = get_widget_info(id, False);
1163         widget_instance *instance;
1164         widget_value *val;
1165         Boolean result = False;
1166
1167         if (!info)
1168                 return False;
1169
1170         instance = info->instances;
1171         if (!instance)
1172                 return False;
1173
1174         for (val = val_out; val; val = val->next)
1175                 if (get_one_value(instance, val))
1176                         result = True;
1177
1178         return result;
1179 }
1180
1181 widget_value *lw_get_all_values(LWLIB_ID id)
1182 {
1183         widget_info *info = get_widget_info(id, False);
1184         widget_value *val = info->val;
1185         if (lw_get_some_values(id, val))
1186                 return val;
1187         else
1188                 return NULL;
1189 }
1190
1191 /* internal function used by the library dependent implementation to get the
1192    widget_value for a given widget in an instance */
1193 widget_value *lw_get_widget_value_for_widget(widget_instance * instance,
1194                                              Widget w)
1195 {
1196         char *name = XtName(w);
1197         widget_value *cur;
1198         for (cur = instance->info->val; cur; cur = cur->next)
1199                 if (!strcmp(cur->name, name))
1200                         return cur;
1201         return NULL;
1202 }
1203 \f
1204 /* update other instances value when one thing changed */
1205 /* This function can be used as a an XtCallback for the widgets that get
1206   modified to update other instances of the widgets.  Closure should be the
1207   widget_instance. */
1208 void
1209 lw_internal_update_other_instances(Widget widget, XtPointer closure,
1210                                    XtPointer call_data)
1211 {
1212         /* To forbid recursive calls */
1213         static Boolean updating;
1214
1215         widget_instance *instance = (widget_instance *) closure;
1216         char *name = XtName(widget);
1217         widget_info *info;
1218         widget_instance *cur;
1219         widget_value *val;
1220
1221         /* never recurse as this could cause infinite recursions. */
1222         if (updating)
1223                 return;
1224
1225         /* protect against the widget being destroyed */
1226         if (XtWidgetBeingDestroyedP(widget))
1227                 return;
1228
1229         /* Return immediately if there are no other instances */
1230         info = instance->info;
1231         if (!info->instances->next)
1232                 return;
1233
1234         updating = True;
1235
1236         for (val = info->val; val && strcmp(val->name, name); val = val->next) ;
1237
1238         if (val && get_one_value(instance, val))
1239                 for (cur = info->instances; cur; cur = cur->next)
1240                         if (cur != instance)
1241                                 set_one_value(cur, val, True);
1242
1243         updating = False;
1244 }
1245 \f
1246 /* get the id */
1247
1248 LWLIB_ID lw_get_widget_id(Widget w)
1249 {
1250         widget_instance *instance = get_widget_instance(w, False);
1251
1252         return instance ? instance->info->id : 0;
1253 }
1254 \f
1255 /* set the keyboard focus */
1256 void lw_set_keyboard_focus(Widget parent, Widget w)
1257 {
1258 #if defined(NEED_MOTIF) && !defined(LESSTIF_VERSION)
1259         /* This loses with Lesstif v0.75a */
1260         xm_set_keyboard_focus(parent, w);
1261 #else
1262         XtSetKeyboardFocus(parent, w);
1263 #endif
1264 }
1265 \f
1266 /* Show busy */
1267 static void show_one_widget_busy(Widget w, Boolean flag)
1268 {
1269         Pixel foreground = 0;
1270         Pixel background = 1;
1271         Widget widget_to_invert = XtNameToWidget(w, "*sheet");
1272         Arg al[2];
1273
1274         if (!widget_to_invert)
1275                 widget_to_invert = w;
1276
1277         XtSetArg(al[0], XtNforeground, &foreground);
1278         XtSetArg(al[1], XtNbackground, &background);
1279         XtGetValues(widget_to_invert, al, 2);
1280
1281         XtSetArg(al[0], XtNforeground, background);
1282         XtSetArg(al[1], XtNbackground, foreground);
1283         XtSetValues(widget_to_invert, al, 2);
1284 }
1285
1286 void lw_show_busy(Widget w, Boolean busy)
1287 {
1288         widget_instance *instance = get_widget_instance(w, False);
1289         widget_info *info;
1290         widget_instance *next;
1291
1292         if (instance) {
1293                 info = instance->info;
1294                 if (info->busy != busy) {
1295                         for (next = info->instances; next; next = next->next)
1296                                 if (next->widget)
1297                                         show_one_widget_busy(next->widget,
1298                                                              busy);
1299                         info->busy = busy;
1300                 }
1301         }
1302 }
1303
1304 void lw_add_value_args_to_args(widget_value * wv, ArgList addto, int *offset)
1305 {
1306         int i;
1307         if (wv->args && wv->args->nargs) {
1308                 for (i = 0; i < wv->args->nargs; i++) {
1309                         addto[i + *offset] = wv->args->args[i];
1310                 }
1311                 *offset += wv->args->nargs;
1312         }
1313 }
1314
1315 XtArgVal lw_get_value_arg(widget_value * wv, String name)
1316 {
1317         int i;
1318         if (wv->args) {
1319                 for (i = 0; i < wv->args->nargs; i++) {
1320                         if (!strcmp(wv->args->args[i].name, name)) {
1321                                 return wv->args->args[i].value;
1322                         }
1323                 }
1324         }
1325         return (XtArgVal) 0;
1326 }
1327
1328 void lw_add_widget_value_arg(widget_value * wv, String name, XtArgVal value)
1329 {
1330         int i = 0;
1331         if (!wv->args) {
1332                 wv->args = (widget_args *) xmalloc(sizeof(widget_args));
1333                 memset(wv->args, '\0', sizeof(widget_args));
1334                 wv->args->ref_count = 1;
1335                 wv->args->nargs = 0;
1336                 wv->args->args = (ArgList) xmalloc(sizeof(Arg) * 10);
1337                 memset(wv->args->args, '\0', sizeof(Arg) * 10);
1338         }
1339
1340         if (wv->args->nargs > 10)
1341                 return;
1342
1343         /* Register the change. */
1344         wv->args->args_changed = True;
1345         /* If the arg is already there then we must replace it. */
1346         for (i = 0; i < wv->args->nargs; i++) {
1347                 if (!strcmp(wv->args->args[i].name, name)) {
1348                         XtSetArg(wv->args->args[i], name, value);
1349                         break;
1350                 }
1351         }
1352         if (i >= wv->args->nargs) {
1353                 XtSetArg(wv->args->args[wv->args->nargs], name, value);
1354                 wv->args->nargs++;
1355         }
1356 }
1357
1358 static void free_widget_value_args(widget_value * wv)
1359 {
1360         if (wv->args) {
1361                 if (--wv->args->ref_count <= 0) {
1362 #ifdef LWLIB_WIDGETS_MOTIF
1363                         int i;
1364                         for (i = 0; i < wv->args->nargs; i++) {
1365                                 if (!strcmp
1366                                     (wv->args->args[i].name, XmNfontList))
1367                                         XmFontListFree((XmFontList) wv->args->
1368                                                        args[i].value);
1369                         }
1370 #endif
1371                         xfree(wv->args->args);
1372                         xfree(wv->args);
1373                         wv->args = 0;
1374                 }
1375         }
1376 }
1377
1378 void lw_copy_widget_value_args(widget_value * val, widget_value * copy)
1379 {
1380         if (val == copy || val->args == copy->args)
1381                 return;
1382
1383         if (copy->args) {
1384                 free_widget_value_args(copy);
1385         }
1386
1387         if (val->args) {
1388                 copy->args = val->args;
1389                 copy->args->ref_count++;
1390         }
1391 }
1392
1393 /* Remove %_ and convert %% to %.  We can do this in-place because we
1394    are always shortening, never lengthening, the string. */
1395 void lw_remove_accelerator_spec(char *val)
1396 {
1397         char *foo = val, *bar = val;
1398
1399         while (*bar) {
1400                 if (*bar == '%' && *(bar + 1) == '_')
1401                         bar += 2;
1402                 else if (*bar == '%' && *(bar + 1) == '%') {
1403                         *foo++ = *bar++;
1404                         bar++;
1405                 } else
1406                         *foo++ = *bar++;
1407         }
1408         *foo = '\0';
1409 }