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