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