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