Use proper max in lwlib
[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         else {
322
323                 copy = copy_widget_value_tree(newtree, STRUCTURAL_CHANGE);
324
325                 free_widget_value_contents(node);
326                 *node = *copy;
327                 /* free the node, but not its contents. */
328                 free_widget_value(copy);
329         }
330         return node;
331 }
332
333 static widget_info *allocate_widget_info(const char *type, const char *name,
334                                          LWLIB_ID id, widget_value * val,
335                                          lw_callback pre_activate_cb,
336                                          lw_callback selection_cb,
337                                          lw_callback post_activate_cb)
338 {
339         widget_info *info = (widget_info *) xmalloc(sizeof(widget_info));
340         info->type = safe_strdup(type);
341         info->name = safe_strdup(name);
342         info->id = id;
343         info->val = copy_widget_value_tree(val, STRUCTURAL_CHANGE);
344         info->busy = False;
345         info->pre_activate_cb = pre_activate_cb;
346         info->selection_cb = selection_cb;
347         info->post_activate_cb = post_activate_cb;
348         info->instances = NULL;
349
350         info->next = all_widget_info;
351         all_widget_info = info;
352
353         return info;
354 }
355
356 static void free_widget_info(widget_info * info)
357 {
358         safe_free_str(info->type);
359         safe_free_str(info->name);
360         free_widget_value_tree(info->val);
361         memset(info, '\0', sizeof(widget_info));
362         xfree(info);
363 }
364
365 static void
366 mark_widget_destroyed(Widget widget, XtPointer closure, XtPointer call_data)
367 {
368         widget_instance *instance = (widget_instance *) closure;
369
370         /* be very conservative */
371         if (instance->widget == widget)
372                 instance->widget = NULL;
373 }
374
375 static widget_instance *allocate_widget_instance(widget_info * info,
376                                                  Widget parent,
377                                                  Boolean pop_up_p)
378 {
379         widget_instance *instance =
380             (widget_instance *) xmalloc(sizeof(widget_instance));
381         instance->parent = parent;
382         instance->pop_up_p = pop_up_p;
383         instance->info = info;
384         instance->next = info->instances;
385         info->instances = instance;
386
387         instantiate_widget_instance(instance);
388
389         XtAddCallback(instance->widget, XtNdestroyCallback,
390                       mark_widget_destroyed, (XtPointer) instance);
391         return instance;
392 }
393
394 static void free_widget_instance(widget_instance * instance)
395 {
396         memset(instance, '\0', sizeof(widget_instance));
397         xfree(instance);
398 }
399
400 static widget_info *get_widget_info(LWLIB_ID id, Boolean remove_p)
401 {
402         widget_info *info;
403         widget_info *prev;
404         for (prev = NULL, info = all_widget_info;
405              info; prev = info, info = info->next)
406                 if (info->id == id) {
407                         if (remove_p) {
408                                 if (prev)
409                                         prev->next = info->next;
410                                 else
411                                         all_widget_info = info->next;
412                         }
413                         return info;
414                 }
415         return NULL;
416 }
417
418 /* Internal function used by the library dependent implementation to get the
419    widget_value for a given widget in an instance */
420 widget_info *lw_get_widget_info(LWLIB_ID id)
421 {
422         return get_widget_info(id, 0);
423 }
424
425 static int
426 map_widget_values(widget_value * value, int (*mapfunc) (widget_value * value,
427                                                         void *closure),
428                   void *closure)
429 {
430         int retval = 0;
431
432         if (value->contents)
433                 retval = map_widget_values(value->contents, mapfunc, closure);
434         if (retval)
435                 return retval;
436
437         if (value->next)
438                 retval = map_widget_values(value->next, mapfunc, closure);
439         if (retval)
440                 return retval;
441
442         return (mapfunc) (value, closure);
443 }
444
445 int
446 lw_map_widget_values(LWLIB_ID id, int (*mapfunc) (widget_value * value,
447                                                   void *closure), void *closure)
448 {
449         widget_info *info = get_widget_info(id, 0);
450
451         if (!info)
452                 abort();
453         else if (info->val)
454                 return map_widget_values(info->val, mapfunc, closure);
455         return 0;
456 }
457
458 static widget_instance *get_widget_instance(Widget widget, Boolean remove_p)
459 {
460         widget_info *info;
461         widget_instance *instance;
462         widget_instance *prev;
463         for (info = all_widget_info; info; info = info->next)
464                 for (prev = NULL, instance = info->instances;
465                      instance; prev = instance, instance = instance->next)
466                         if (instance->widget == widget) {
467                                 if (remove_p) {
468                                         if (prev)
469                                                 prev->next = instance->next;
470                                         else
471                                                 info->instances =
472                                                     instance->next;
473                                 }
474                                 return instance;
475                         }
476         return (widget_instance *) 0;
477 }
478
479 static widget_instance *find_instance(LWLIB_ID id, Widget parent,
480                                       Boolean pop_up_p)
481 {
482         widget_info *info = get_widget_info(id, False);
483         widget_instance *instance;
484
485         if (info)
486                 for (instance = info->instances; instance;
487                      instance = instance->next)
488                         if (instance->parent == parent
489                             && instance->pop_up_p == pop_up_p)
490                                 return instance;
491
492         return NULL;
493 }
494 \f
495 /* utility function for widget_value */
496 static Boolean safe_strcmp(const char *s1, const char *s2)
497 {
498         if (!!s1 ^ !!s2)
499                 return True;
500         return (s1 && s2) ? strcmp(s1, s2) : s1 ? False : !!s2;
501 }
502
503
504 /* We only expect to use the following max function */
505 #ifdef max
506 #undef max
507 #endif
508
509 static change_type max(change_type i1, change_type i2)
510 {
511         return (int)i1 > (int)i2 ? i1 : i2;
512 }
513
514 #if 0
515 # define EXPLAIN(name, oc, nc, desc, a1, a2)                            \
516    printf ("Change: \"%s\"\tmax(%s=%d,%s=%d)\t%s %d %d\n",              \
517            name,                                                        \
518            (oc == NO_CHANGE ? "none" :                                  \
519             (oc == INVISIBLE_CHANGE ? "invisible" :                     \
520              (oc == VISIBLE_CHANGE ? "visible" :                        \
521               (oc == STRUCTURAL_CHANGE ? "structural" : "???")))),      \
522            oc,                                                          \
523            (nc == NO_CHANGE ? "none" :                                  \
524             (nc == INVISIBLE_CHANGE ? "invisible" :                     \
525              (nc == VISIBLE_CHANGE ? "visible" :                        \
526               (nc == STRUCTURAL_CHANGE ? "structural" : "???")))),      \
527            nc, desc, a1, a2)
528 #else
529 # define EXPLAIN(name, oc, nc, desc, a1, a2)
530 #endif
531
532 static widget_value *merge_widget_value(widget_value * val1,
533                                         widget_value * val2, int level)
534 {
535         change_type change;
536         widget_value *merged_next;
537         widget_value *merged_contents;
538
539         if (!val1) {
540                 if (val2)
541                         return copy_widget_value_tree(val2, STRUCTURAL_CHANGE);
542                 else
543                         return NULL;
544         }
545         if (!val2) {
546                 free_widget_value_tree(val1);
547                 return NULL;
548         }
549
550         change = NO_CHANGE;
551
552         if (val1->type != val2->type) {
553                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE, "type change",
554                         val1->type, val2->type);
555                 change = max(change, STRUCTURAL_CHANGE);
556                 val1->type = val2->type;
557         }
558         if (safe_strcmp(val1->name, val2->name)) {
559                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE, "name change",
560                         val1->name, val2->name);
561                 change = max(change, STRUCTURAL_CHANGE);
562                 safe_free_str(val1->name);
563                 val1->name = safe_strdup(val2->name);
564         }
565         if (safe_strcmp(val1->value, val2->value)) {
566                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "value change",
567                         val1->value, val2->value);
568                 change = max(change, VISIBLE_CHANGE);
569                 safe_free_str(val1->value);
570                 val1->value = safe_strdup(val2->value);
571         }
572         if (safe_strcmp(val1->key, val2->key)) {
573                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "key change",
574                         val1->key, val2->key);
575                 change = max(change, VISIBLE_CHANGE);
576                 safe_free_str(val1->key);
577                 val1->key = safe_strdup(val2->key);
578         }
579         if (val1->accel != val2->accel) {
580                 EXPLAIN(val1->name, change, VISIBLE_CHANGE,
581                         "accelerator change", val1->accel, val2->accel);
582                 change = max(change, VISIBLE_CHANGE);
583                 val1->accel = val2->accel;
584         }
585         if (val1->enabled != val2->enabled) {
586                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "enablement change",
587                         val1->enabled, val2->enabled);
588                 change = max(change, VISIBLE_CHANGE);
589                 val1->enabled = val2->enabled;
590         }
591         if (val1->selected != val2->selected) {
592                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "selection change",
593                         val1->selected, val2->selected);
594                 change = max(change, VISIBLE_CHANGE);
595                 val1->selected = val2->selected;
596         }
597         if (val1->call_data != val2->call_data) {
598                 EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
599                         "call-data change", val1->call_data, val2->call_data);
600                 change = max(change, INVISIBLE_CHANGE);
601                 val1->call_data = val2->call_data;
602         }
603 #ifdef HAVE_WIDGETS
604         if (merge_widget_value_args(val1, val2)) {
605                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "widget change", 0,
606                         0);
607                 change = max(change, VISIBLE_CHANGE);
608         }
609 #endif
610
611 #ifdef NEED_SCROLLBARS
612         if (merge_scrollbar_values(val1, val2)) {
613                 EXPLAIN(val1->name, change, VISIBLE_CHANGE, "scrollbar change",
614                         0, 0);
615                 change = max(change, VISIBLE_CHANGE);
616         }
617 #endif
618
619         if (level > 0) {
620                 merged_contents =
621                     merge_widget_value(val1->contents, val2->contents,
622                                        level - 1);
623
624                 if (val1->contents && !merged_contents) {
625                         EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
626                                 "(contents gone)", 0, 0);
627                         change = max(change, INVISIBLE_CHANGE);
628                 } else if (merged_contents
629                            && merged_contents->change != NO_CHANGE) {
630                         EXPLAIN(val1->name, change, INVISIBLE_CHANGE,
631                                 "(contents change)", 0, 0);
632                         change = max(change, INVISIBLE_CHANGE);
633                 }
634
635                 val1->contents = merged_contents;
636         }
637
638         merged_next = merge_widget_value(val1->next, val2->next, level);
639
640         if (val1->next && !merged_next) {
641                 EXPLAIN(val1->name, change, STRUCTURAL_CHANGE,
642                         "(following gone)", 0, 0);
643                 change = max(change, STRUCTURAL_CHANGE);
644         } else if (merged_next) {
645                 if (merged_next->change) {
646                         EXPLAIN(val1->name, change, merged_next->change,
647                                 "(following change)", 0, 0);
648                 }
649                 change = max(change, merged_next->change);
650         }
651
652         val1->next = merged_next;
653
654         val1->change = change;
655
656         if (change > NO_CHANGE && val1->toolkit_data) {
657                 if (val1->free_toolkit_data)
658                         XtFree((char *)val1->toolkit_data);
659                 val1->toolkit_data = NULL;
660         }
661
662         return val1;
663 }
664 \f
665 /* modifying the widgets */
666 static Widget name_to_widget(widget_instance * instance, const char *name)
667 {
668         Widget widget = NULL;
669
670         if (!instance->widget)
671                 return NULL;
672
673         if (!strcmp(XtName(instance->widget), name))
674                 widget = instance->widget;
675         else {
676                 int length = strlen(name) + 2;
677                 char *real_name = (char *)alloca(length);
678                 real_name[0] = '*';
679                 strcpy(real_name + 1, name);
680
681                 widget = XtNameToWidget(instance->widget, real_name);
682         }
683         return widget;
684 }
685
686 static void
687 set_one_value(widget_instance * instance, widget_value * val, Boolean deep_p)
688 {
689         Widget widget = name_to_widget(instance, val->name);
690
691         if (widget) {
692 #ifdef NEED_LUCID
693                 if (lw_lucid_widget_p(instance->widget))
694                         xlw_update_one_widget(instance, widget, val, deep_p);
695 #endif
696 #ifdef NEED_MOTIF
697                 if (lw_motif_widget_p(instance->widget))
698                         xm_update_one_widget(instance, widget, val, deep_p);
699 #endif
700 #ifdef NEED_ATHENA
701                 if (lw_xaw_widget_p(instance->widget))
702                         xaw_update_one_widget(instance, widget, val, deep_p);
703 #endif
704         }
705 }
706
707 static void
708 update_one_widget_instance(widget_instance * instance, Boolean deep_p)
709 {
710         widget_value *val;
711
712         if (!instance->widget)
713                 /* the widget was destroyed */
714                 return;
715
716         for (val = instance->info->val; val; val = val->next)
717                 if (val->change != NO_CHANGE)
718                         set_one_value(instance, val, deep_p);
719 }
720
721 static void update_all_widget_values(widget_info * info, Boolean deep_p)
722 {
723         widget_instance *instance;
724         widget_value *val;
725
726         for (instance = info->instances; instance; instance = instance->next)
727                 update_one_widget_instance(instance, deep_p);
728
729         for (val = info->val; val; val = val->next) {
730                 val->change = NO_CHANGE;
731                 if (val->args)
732                         val->args->args_changed = False;
733         }
734 }
735
736 void lw_modify_all_widgets(LWLIB_ID id, widget_value * val, Boolean deep_p)
737 {
738         widget_info *info = get_widget_info(id, False);
739         widget_value *new_val;
740         widget_value *next_new_val;
741         widget_value *cur;
742         widget_value *prev;
743         widget_value *next;
744         int found;
745
746         if (!info)
747                 return;
748
749         for (new_val = val; new_val; new_val = new_val->next) {
750                 next_new_val = new_val->next;
751                 new_val->next = NULL;
752                 found = False;
753                 for (prev = NULL, cur = info->val; cur;
754                      prev = cur, cur = cur->next)
755                         if (!strcmp(cur->name, new_val->name)) {
756                                 found = True;
757                                 next = cur->next;
758                                 cur->next = NULL;
759                                 cur =
760                                     merge_widget_value(cur, new_val,
761                                                        deep_p ? 1000 : 1);
762                                 if (prev)
763                                         prev->next = cur ? cur : next;
764                                 else
765                                         info->val = cur ? cur : next;
766                                 if (cur)
767                                         cur->next = next;
768                                 break;
769                         }
770                 if (!found) {
771                         /* Could not find it, add it */
772                         if (prev)
773                                 prev->next =
774                                     copy_widget_value_tree(new_val,
775                                                            STRUCTURAL_CHANGE);
776                         else
777                                 info->val =
778                                     copy_widget_value_tree(new_val,
779                                                            STRUCTURAL_CHANGE);
780                 }
781                 new_val->next = next_new_val;
782         }
783
784         update_all_widget_values(info, deep_p);
785 }
786 \f
787 /* creating the widgets */
788
789 static void initialize_widget_instance(widget_instance * instance)
790 {
791         widget_value *val;
792
793         for (val = instance->info->val; val; val = val->next)
794                 val->change = STRUCTURAL_CHANGE;
795
796         update_one_widget_instance(instance, True);
797
798         for (val = instance->info->val; val; val = val->next) {
799                 val->change = NO_CHANGE;
800                 if (val->args)
801                         val->args->args_changed = False;
802         }
803 }
804
805 /* strcasecmp() is not sufficiently portable or standard,
806    and it's easier just to write our own. */
807 static int ascii_strcasecmp(const char *s1, const char *s2)
808 {
809         while (1) {
810                 char c1 = *s1++;
811                 char c2 = *s2++;
812                 if (c1 >= 'A' && c1 <= 'Z')
813                         c1 += 'a' - 'A';
814                 if (c2 >= 'A' && c2 <= 'Z')
815                         c2 += 'a' - 'A';
816                 if (c1 != c2)
817                         return c1 - c2;
818                 if (c1 == '\0')
819                         return 0;
820         }
821 }
822
823 static widget_creation_function
824 find_in_table(const char *type, const widget_creation_entry table[])
825 {
826         const widget_creation_entry *cur;
827         for (cur = table; cur->type; cur++)
828                 if (!ascii_strcasecmp(type, cur->type))
829                         return cur->function;
830         return NULL;
831 }
832
833 static Boolean dialog_spec_p(const char *name)
834 {
835         /* return True if name matches [EILPQeilpq][1-9][Bb] or
836            [EILPQeilpq][1-9][Bb][Rr][1-9] */
837         if (!name)
838                 return False;
839
840         switch (name[0]) {
841         case 'E':
842         case 'I':
843         case 'L':
844         case 'P':
845         case 'Q':
846         case 'e':
847         case 'i':
848         case 'l':
849         case 'p':
850         case 'q':
851                 if (name[1] >= '0' && name[1] <= '9') {
852                         if (name[2] != 'B' && name[2] != 'b')
853                                 return False;
854                         if (!name[3])
855                                 return True;
856                         if ((name[3] == 'T' || name[3] == 't') && !name[4])
857                                 return True;
858                         if ((name[3] == 'R' || name[3] == 'r')
859                             && name[4] >= '0' && name[4] <= '9' && !name[5])
860                                 return True;
861                         return False;
862                 } else
863                         return False;
864
865         default:
866                 return False;
867         }
868 }
869
870 static void instantiate_widget_instance(widget_instance * instance)
871 {
872         widget_creation_function function = NULL;
873
874 #ifdef NEED_LUCID
875         if (!function)
876                 function =
877                     find_in_table(instance->info->type, xlw_creation_table);
878 #endif
879 #ifdef NEED_MOTIF
880         if (!function)
881                 function =
882                     find_in_table(instance->info->type, xm_creation_table);
883 #endif
884 #ifdef NEED_ATHENA
885         if (!function)
886                 function =
887                     find_in_table(instance->info->type, xaw_creation_table);
888 #endif
889
890         if (!function) {
891                 if (dialog_spec_p(instance->info->type)) {
892 #ifdef LWLIB_DIALOGS_MOTIF
893                         if (!function)
894                                 function = xm_create_dialog;
895 #endif
896 #ifdef LWLIB_DIALOGS_ATHENA
897                         if (!function)
898                                 function = xaw_create_dialog;
899 #endif
900                 }
901         }
902
903         if (!function) {
904                 fprintf(stderr, "No creation function for widget type %s\n",
905                         instance->info->type);
906                 abort();
907         } else {
908                 instance->widget = (*function) (instance);
909         }
910
911         if (!instance->widget)
912                 abort();
913
914         /*   XtRealizeWidget (instance->widget); */
915 }
916
917 void
918 lw_register_widget(const char *type, const char *name,
919                    LWLIB_ID id, widget_value * val,
920                    lw_callback pre_activate_cb, lw_callback selection_cb,
921                    lw_callback post_activate_cb)
922 {
923         if (!get_widget_info(id, False))
924                 allocate_widget_info(type, name, id, val, pre_activate_cb,
925                                      selection_cb, post_activate_cb);
926 }
927
928 Widget lw_get_widget(LWLIB_ID id, Widget parent, Boolean pop_up_p)
929 {
930         widget_instance *instance = find_instance(id, parent, pop_up_p);
931         return instance ? instance->widget : NULL;
932 }
933
934 Widget lw_make_widget(LWLIB_ID id, Widget parent, Boolean pop_up_p)
935 {
936         widget_instance *instance = find_instance(id, parent, pop_up_p);
937
938         if (!instance) {
939                 widget_info *info = get_widget_info(id, False);
940                 if (!info)
941                         return NULL;
942                 instance = allocate_widget_instance(info, parent, pop_up_p);
943                 initialize_widget_instance(instance);
944         }
945         if (!instance->widget)
946                 abort();
947         return instance->widget;
948 }
949
950 Widget
951 lw_create_widget(const char *type, const char *name,
952                  LWLIB_ID id, widget_value * val,
953                  Widget parent, Boolean pop_up_p, lw_callback pre_activate_cb,
954                  lw_callback selection_cb, lw_callback post_activate_cb)
955 {
956         lw_register_widget(type, name, id, val, pre_activate_cb, selection_cb,
957                            post_activate_cb);
958         return lw_make_widget(id, parent, pop_up_p);
959 }
960 \f
961 /* destroying the widgets */
962 static void destroy_one_instance(widget_instance * instance)
963 {
964         /* Remove the destroy callback on the widget; that callback will try to
965            dereference the instance object (to set its widget slot to 0, since the
966            widget is dead.)  Since the instance is now dead, we don't have to worry
967            about the fact that its widget is dead too.
968
969            This happens in the Phase2Destroy of the widget, so this callback would
970            not have been run until arbitrarily long after the instance was freed.
971          */
972         if (instance->widget)
973                 XtRemoveCallback(instance->widget, XtNdestroyCallback,
974                                  mark_widget_destroyed, (XtPointer) instance);
975
976         if (instance->widget) {
977                 /* The else are pretty tricky here, including the empty statement
978                    at the end because it would be very bad to destroy a widget
979                    twice. */
980 #ifdef NEED_LUCID
981                 if (lw_lucid_widget_p(instance->widget))
982                         xlw_destroy_instance(instance);
983                 else
984 #endif
985 #ifdef NEED_MOTIF
986                 if (lw_motif_widget_p(instance->widget))
987                         xm_destroy_instance(instance);
988                 else
989 #endif
990 #ifdef NEED_ATHENA
991                 if (lw_xaw_widget_p(instance->widget))
992                         xaw_destroy_instance(instance);
993                 else
994 #endif
995                 {
996                         /* do not remove the empty statement */
997                         ;
998                 }
999         }
1000
1001         free_widget_instance(instance);
1002 }
1003
1004 void lw_destroy_widget(Widget w)
1005 {
1006         widget_instance *instance = get_widget_instance(w, True);
1007
1008         if (instance) {
1009                 widget_info *info = instance->info;
1010                 /* instance has already been removed from the list; free it */
1011                 destroy_one_instance(instance);
1012                 /* if there are no instances left, free the info too */
1013                 if (!info->instances)
1014                         lw_destroy_all_widgets(info->id);
1015         }
1016 }
1017
1018 void lw_destroy_all_widgets(LWLIB_ID id)
1019 {
1020         widget_info *info = get_widget_info(id, True);
1021         widget_instance *instance;
1022         widget_instance *next;
1023
1024         if (info) {
1025                 for (instance = info->instances; instance;) {
1026                         next = instance->next;
1027                         destroy_one_instance(instance);
1028                         instance = next;
1029                 }
1030                 free_widget_info(info);
1031         }
1032 }
1033
1034 void lw_destroy_everything(void)
1035 {
1036         while (all_widget_info)
1037                 lw_destroy_all_widgets(all_widget_info->id);
1038 }
1039
1040 void lw_destroy_all_pop_ups(void)
1041 {
1042         widget_info *info;
1043         widget_info *next;
1044         widget_instance *instance;
1045
1046         for (info = all_widget_info; info; info = next) {
1047                 next = info->next;
1048                 instance = info->instances;
1049                 if (instance && instance->pop_up_p)
1050                         lw_destroy_all_widgets(info->id);
1051         }
1052 }
1053
1054 Widget lw_raise_all_pop_up_widgets(void)
1055 {
1056         widget_info *info;
1057         widget_instance *instance;
1058         Widget result = NULL;
1059
1060         for (info = all_widget_info; info; info = info->next)
1061                 for (instance = info->instances; instance;
1062                      instance = instance->next)
1063                         if (instance->pop_up_p) {
1064                                 Widget widget = instance->widget;
1065                                 if (widget) {
1066                                         if (XtIsManaged(widget)
1067 #ifdef NEED_MOTIF
1068                                             /* What a complete load of crap!!!!
1069                                                When a dialogShell is on the screen, it is not managed!
1070                                              */
1071                                             ||
1072                                             (lw_motif_widget_p(instance->widget)
1073                                              &&
1074                                              XtIsManaged(first_child(widget)))
1075 #endif
1076                                             ) {
1077                                                 if (!result)
1078                                                         result = widget;
1079                                                 XMapRaised(XtDisplay(widget),
1080                                                            XtWindow(widget));
1081                                         }
1082                                 }
1083                         }
1084         return result;
1085 }
1086
1087 static void lw_pop_all_widgets(LWLIB_ID id, Boolean up)
1088 {
1089         widget_info *info = get_widget_info(id, False);
1090         widget_instance *instance;
1091
1092         if (info)
1093                 for (instance = info->instances; instance;
1094                      instance = instance->next)
1095                         if (instance->pop_up_p && instance->widget) {
1096 #ifdef NEED_LUCID
1097                                 if (lw_lucid_widget_p(instance->widget)) {
1098                                         XtRealizeWidget(instance->widget);
1099                                         xlw_pop_instance(instance, up);
1100                                 }
1101 #endif
1102 #ifdef NEED_MOTIF
1103                                 if (lw_motif_widget_p(instance->widget)) {
1104                                         XtRealizeWidget(instance->widget);
1105                                         xm_pop_instance(instance, up);
1106                                 }
1107 #endif
1108 #ifdef NEED_ATHENA
1109                                 if (lw_xaw_widget_p(instance->widget)) {
1110                                         XtRealizeWidget(XtParent
1111                                                         (instance->widget));
1112                                         XtRealizeWidget(instance->widget);
1113                                         xaw_pop_instance(instance, up);
1114                                 }
1115 #endif
1116                         }
1117 }
1118
1119 void lw_pop_up_all_widgets(LWLIB_ID id)
1120 {
1121         lw_pop_all_widgets(id, True);
1122 }
1123
1124 void lw_pop_down_all_widgets(LWLIB_ID id)
1125 {
1126         lw_pop_all_widgets(id, False);
1127 }
1128
1129 void lw_popup_menu(Widget widget, XEvent * event)
1130 {
1131 #ifdef LWLIB_MENUBARS_LUCID
1132         if (lw_lucid_widget_p(widget))
1133                 xlw_popup_menu(widget, event);
1134 #endif
1135 #ifdef LWLIB_MENUBARS_MOTIF
1136         if (lw_motif_widget_p(widget))
1137                 xm_popup_menu(widget, event);
1138 #endif
1139 #ifdef LWLIB_MENUBARS_ATHENA
1140         if (lw_xaw_widget_p(widget))
1141                 xaw_popup_menu(widget, event);  /* not implemented */
1142 #endif
1143 }
1144 \f
1145 /* get the values back */
1146 static Boolean get_one_value(widget_instance * instance, widget_value * val)
1147 {
1148         Widget widget = name_to_widget(instance, val->name);
1149
1150         if (widget) {
1151 #ifdef NEED_LUCID
1152                 if (lw_lucid_widget_p(instance->widget))
1153                         xlw_update_one_value(instance, widget, val);
1154 #endif
1155 #ifdef NEED_MOTIF
1156                 if (lw_motif_widget_p(instance->widget))
1157                         xm_update_one_value(instance, widget, val);
1158 #endif
1159 #ifdef NEED_ATHENA
1160                 if (lw_xaw_widget_p(instance->widget))
1161                         xaw_update_one_value(instance, widget, val);
1162 #endif
1163                 return True;
1164         } else
1165                 return False;
1166 }
1167
1168 Boolean lw_get_some_values(LWLIB_ID id, widget_value * val_out)
1169 {
1170         widget_info *info = get_widget_info(id, False);
1171         widget_instance *instance;
1172         widget_value *val;
1173         Boolean result = False;
1174
1175         if (!info)
1176                 return False;
1177
1178         instance = info->instances;
1179         if (!instance)
1180                 return False;
1181
1182         for (val = val_out; val; val = val->next)
1183                 if (get_one_value(instance, val))
1184                         result = True;
1185
1186         return result;
1187 }
1188
1189 widget_value *lw_get_all_values(LWLIB_ID id)
1190 {
1191         widget_info *info = get_widget_info(id, False);
1192         widget_value *val = info->val;
1193         if (lw_get_some_values(id, val))
1194                 return val;
1195         else
1196                 return NULL;
1197 }
1198
1199 /* internal function used by the library dependent implementation to get the
1200    widget_value for a given widget in an instance */
1201 widget_value *lw_get_widget_value_for_widget(widget_instance * instance,
1202                                              Widget w)
1203 {
1204         char *name = XtName(w);
1205         widget_value *cur;
1206         for (cur = instance->info->val; cur; cur = cur->next)
1207                 if (!strcmp(cur->name, name))
1208                         return cur;
1209         return NULL;
1210 }
1211 \f
1212 /* update other instances value when one thing changed */
1213 /* This function can be used as a an XtCallback for the widgets that get
1214   modified to update other instances of the widgets.  Closure should be the
1215   widget_instance. */
1216 void
1217 lw_internal_update_other_instances(Widget widget, XtPointer closure,
1218                                    XtPointer call_data)
1219 {
1220         /* To forbid recursive calls */
1221         static Boolean updating;
1222
1223         widget_instance *instance = (widget_instance *) closure;
1224         char *name = XtName(widget);
1225         widget_info *info;
1226         widget_instance *cur;
1227         widget_value *val;
1228
1229         /* never recurse as this could cause infinite recursions. */
1230         if (updating)
1231                 return;
1232
1233         /* protect against the widget being destroyed */
1234         if (XtWidgetBeingDestroyedP(widget))
1235                 return;
1236
1237         /* Return immediately if there are no other instances */
1238         info = instance->info;
1239         if (!info->instances->next)
1240                 return;
1241
1242         updating = True;
1243
1244         for (val = info->val; val && strcmp(val->name, name); val = val->next) ;
1245
1246         if (val && get_one_value(instance, val))
1247                 for (cur = info->instances; cur; cur = cur->next)
1248                         if (cur != instance)
1249                                 set_one_value(cur, val, True);
1250
1251         updating = False;
1252 }
1253 \f
1254 /* get the id */
1255
1256 LWLIB_ID lw_get_widget_id(Widget w)
1257 {
1258         widget_instance *instance = get_widget_instance(w, False);
1259
1260         return instance ? instance->info->id : 0;
1261 }
1262 \f
1263 /* set the keyboard focus */
1264 void lw_set_keyboard_focus(Widget parent, Widget w)
1265 {
1266 #if defined(NEED_MOTIF) && !defined(LESSTIF_VERSION)
1267         /* This loses with Lesstif v0.75a */
1268         xm_set_keyboard_focus(parent, w);
1269 #else
1270         XtSetKeyboardFocus(parent, w);
1271 #endif
1272 }
1273 \f
1274 /* Show busy */
1275 static void show_one_widget_busy(Widget w, Boolean flag)
1276 {
1277         Pixel foreground = 0;
1278         Pixel background = 1;
1279         Widget widget_to_invert = XtNameToWidget(w, "*sheet");
1280         Arg al[2];
1281
1282         if (!widget_to_invert)
1283                 widget_to_invert = w;
1284
1285         XtSetArg(al[0], XtNforeground, &foreground);
1286         XtSetArg(al[1], XtNbackground, &background);
1287         XtGetValues(widget_to_invert, al, 2);
1288
1289         XtSetArg(al[0], XtNforeground, background);
1290         XtSetArg(al[1], XtNbackground, foreground);
1291         XtSetValues(widget_to_invert, al, 2);
1292 }
1293
1294 void lw_show_busy(Widget w, Boolean busy)
1295 {
1296         widget_instance *instance = get_widget_instance(w, False);
1297         widget_info *info;
1298         widget_instance *next;
1299
1300         if (instance) {
1301                 info = instance->info;
1302                 if (info->busy != busy) {
1303                         for (next = info->instances; next; next = next->next)
1304                                 if (next->widget)
1305                                         show_one_widget_busy(next->widget,
1306                                                              busy);
1307                         info->busy = busy;
1308                 }
1309         }
1310 }
1311
1312 void lw_add_value_args_to_args(widget_value * wv, ArgList addto, int *offset)
1313 {
1314         int i;
1315         if (wv->args && wv->args->nargs) {
1316                 for (i = 0; i < wv->args->nargs; i++) {
1317                         addto[i + *offset] = wv->args->args[i];
1318                 }
1319                 *offset += wv->args->nargs;
1320         }
1321 }
1322
1323 XtArgVal lw_get_value_arg(widget_value * wv, String name)
1324 {
1325         int i;
1326         if (wv->args) {
1327                 for (i = 0; i < wv->args->nargs; i++) {
1328                         if (!strcmp(wv->args->args[i].name, name)) {
1329                                 return wv->args->args[i].value;
1330                         }
1331                 }
1332         }
1333         return (XtArgVal) 0;
1334 }
1335
1336 void lw_add_widget_value_arg(widget_value * wv, String name, XtArgVal value)
1337 {
1338         int i = 0;
1339         if (!wv->args) {
1340                 wv->args = (widget_args *) xmalloc(sizeof(widget_args));
1341                 memset(wv->args, '\0', sizeof(widget_args));
1342                 wv->args->ref_count = 1;
1343                 wv->args->nargs = 0;
1344                 wv->args->args = (ArgList) xmalloc(sizeof(Arg) * 10);
1345                 memset(wv->args->args, '\0', sizeof(Arg) * 10);
1346         }
1347
1348         if (wv->args->nargs > 10)
1349                 return;
1350
1351         /* Register the change. */
1352         wv->args->args_changed = True;
1353         /* If the arg is already there then we must replace it. */
1354         for (i = 0; i < wv->args->nargs; i++) {
1355                 if (!strcmp(wv->args->args[i].name, name)) {
1356                         XtSetArg(wv->args->args[i], name, value);
1357                         break;
1358                 }
1359         }
1360         if (i >= wv->args->nargs) {
1361                 XtSetArg(wv->args->args[wv->args->nargs], name, value);
1362                 wv->args->nargs++;
1363         }
1364 }
1365
1366 static void free_widget_value_args(widget_value * wv)
1367 {
1368         if (wv->args) {
1369                 if (--wv->args->ref_count <= 0) {
1370 #ifdef LWLIB_WIDGETS_MOTIF
1371                         int i;
1372                         for (i = 0; i < wv->args->nargs; i++) {
1373                                 if (!strcmp
1374                                     (wv->args->args[i].name, XmNfontList))
1375                                         XmFontListFree((XmFontList) wv->args->
1376                                                        args[i].value);
1377                         }
1378 #endif
1379                         xfree(wv->args->args);
1380                         xfree(wv->args);
1381                         wv->args = 0;
1382                 }
1383         }
1384 }
1385
1386 void lw_copy_widget_value_args(widget_value * val, widget_value * copy)
1387 {
1388         if (val == copy || val->args == copy->args)
1389                 return;
1390
1391         if (copy->args) {
1392                 free_widget_value_args(copy);
1393         }
1394
1395         if (val->args) {
1396                 copy->args = val->args;
1397                 copy->args->ref_count++;
1398         }
1399 }
1400
1401 /* Remove %_ and convert %% to %.  We can do this in-place because we
1402    are always shortening, never lengthening, the string. */
1403 void lw_remove_accelerator_spec(char *val)
1404 {
1405         char *foo = val, *bar = val;
1406
1407         while (*bar) {
1408                 if (*bar == '%' && *(bar + 1) == '_')
1409                         bar += 2;
1410                 else if (*bar == '%' && *(bar + 1) == '%') {
1411                         *foo++ = *bar++;
1412                         bar++;
1413                 } else
1414                         *foo++ = *bar++;
1415         }
1416         *foo = '\0';
1417 }