d1a546e33e8273837fa7a39e391c00959af9e972
[chise/xemacs-chise.git.1] / src / gui.c
1 /* Generic GUI code. (menubars, scrollbars, toolbars, dialogs)
2    Copyright (C) 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1995, 1996 Ben Wing.
4    Copyright (C) 1995 Sun Microsystems, Inc.
5    Copyright (C) 1998 Free Software Foundation, Inc.
6
7 This file is part of XEmacs.
8
9 XEmacs is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation; either version 2, or (at your option) any
12 later version.
13
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with XEmacs; see the file COPYING.  If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.  */
23
24 /* Synched up with: Not in FSF. */
25
26 #include <config.h>
27 #include "lisp.h"
28 #include "gui.h"
29 #include "elhash.h"
30 #include "buffer.h"
31 #include "bytecode.h"
32
33 Lisp_Object Q_active, Q_suffix, Q_keys, Q_style, Q_selected;
34 Lisp_Object Q_filter, Q_config, Q_included, Q_key_sequence;
35 Lisp_Object Q_accelerator, Q_label, Q_callback;
36 Lisp_Object Qtoggle, Qradio;
37
38 static Lisp_Object parse_gui_item_tree_list (Lisp_Object list);
39
40 #ifdef HAVE_POPUPS
41
42 /* count of menus/dboxes currently up */
43 int popup_up_p;
44
45 DEFUN ("popup-up-p", Fpopup_up_p, 0, 0, 0, /*
46 Return t if a popup menu or dialog box is up, nil otherwise.
47 See `popup-menu' and `popup-dialog-box'.
48 */
49        ())
50 {
51   return popup_up_p ? Qt : Qnil;
52 }
53 #endif /* HAVE_POPUPS */
54
55 int
56 separator_string_p (const char *s)
57 {
58   const char *p;
59   char first;
60
61   if (!s || s[0] == '\0')
62     return 0;
63   first = s[0];
64   if (first != '-' && first != '=')
65     return 0;
66   for (p = s; *p == first; p++)
67     ;
68
69   return (*p == '!' || *p == ':' || *p == '\0');
70 }
71
72 /* Massage DATA to find the correct function and argument.  Used by
73    popup_selection_callback() and the msw code. */
74 void
75 get_gui_callback (Lisp_Object data, Lisp_Object *fn, Lisp_Object *arg)
76 {
77   if (SYMBOLP (data)
78       || (COMPILED_FUNCTIONP (data)
79           && XCOMPILED_FUNCTION (data)->flags.interactivep)
80       || (CONSP (data) && (EQ (XCAR (data), Qlambda))
81           && !NILP (Fassq (Qinteractive, Fcdr (Fcdr (data))))))
82     {
83       *fn = Qcall_interactively;
84       *arg = data;
85     }
86   else if (CONSP (data))
87     {
88       *fn = Qeval;
89       *arg = data;
90     }
91   else
92     {
93       *fn = Qeval;
94       *arg = list3 (Qsignal,
95                     list2 (Qquote, Qerror),
96                     list2 (Qquote, list2 (build_translated_string
97                                           ("illegal callback"),
98                                           data)));
99     }
100 }
101
102 /*
103  * Add a value VAL associated with keyword KEY into PGUI_ITEM
104  * structure. If KEY is not a keyword, or is an unknown keyword, then
105  * error is signaled.
106  */
107 void
108 gui_item_add_keyval_pair (Lisp_Object gui_item,
109                           Lisp_Object key, Lisp_Object val,
110                           Error_behavior errb)
111 {
112   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
113
114   if (!KEYWORDP (key))
115     signal_simple_error_2 ("Non-keyword in gui item", key, pgui_item->name);
116
117   if      (EQ (key, Q_suffix))   pgui_item->suffix   = val;
118   else if (EQ (key, Q_active))   pgui_item->active   = val;
119   else if (EQ (key, Q_included)) pgui_item->included = val;
120   else if (EQ (key, Q_config))   pgui_item->config   = val;
121   else if (EQ (key, Q_filter))   pgui_item->filter   = val;
122   else if (EQ (key, Q_style))    pgui_item->style    = val;
123   else if (EQ (key, Q_selected)) pgui_item->selected = val;
124   else if (EQ (key, Q_keys))     pgui_item->keys     = val;
125   else if (EQ (key, Q_callback))         pgui_item->callback     = val;
126   else if (EQ (key, Q_key_sequence)) ;   /* ignored for FSF compatibility */
127   else if (EQ (key, Q_label)) ;   /* ignored for 21.0 implement in 21.2  */
128   else if (EQ (key, Q_accelerator))
129     {
130       if (SYMBOLP (val) || CHARP (val))
131         pgui_item->accelerator = val;
132       else if (ERRB_EQ (errb, ERROR_ME))
133         signal_simple_error ("Bad keyboard accelerator", val);
134     }
135   else if (ERRB_EQ (errb, ERROR_ME))
136     signal_simple_error_2 ("Unknown keyword in gui item", key,
137                            pgui_item->name);
138 }
139
140 void
141 gui_item_init (Lisp_Object gui_item)
142 {
143   Lisp_Gui_Item *lp = XGUI_ITEM (gui_item);
144
145   lp->name     = Qnil;
146   lp->callback = Qnil;
147   lp->suffix   = Qnil;
148   lp->active   = Qt;
149   lp->included = Qt;
150   lp->config   = Qnil;
151   lp->filter   = Qnil;
152   lp->style    = Qnil;
153   lp->selected = Qnil;
154   lp->keys     = Qnil;
155   lp->accelerator     = Qnil;
156 }
157
158 Lisp_Object
159 allocate_gui_item (void)
160 {
161   Lisp_Gui_Item *lp = alloc_lcrecord_type (Lisp_Gui_Item, &lrecord_gui_item);
162   Lisp_Object val;
163
164   zero_lcrecord (lp);
165   XSETGUI_ITEM (val, lp);
166
167   gui_item_init (val);
168
169   return val;
170 }
171
172 /*
173  * ITEM is a lisp vector, describing a menu item or a button. The
174  * function extracts the description of the item into the PGUI_ITEM
175  * structure.
176  */
177 static Lisp_Object
178 make_gui_item_from_keywords_internal (Lisp_Object item,
179                                       Error_behavior errb)
180 {
181   int length, plist_p, start;
182   Lisp_Object *contents;
183   Lisp_Object gui_item = allocate_gui_item ();
184   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
185
186   CHECK_VECTOR (item);
187   length = XVECTOR_LENGTH (item);
188   contents = XVECTOR_DATA (item);
189
190   if (length < 1)
191     signal_simple_error ("GUI item descriptors must be at least 1 elts long", item);
192
193   /* length 1:                  [ "name" ]
194      length 2:          [ "name" callback ]
195      length 3:          [ "name" callback active-p ]
196                    or   [ "name" keyword  value  ]
197      length 4:          [ "name" callback active-p suffix ]
198                    or   [ "name" callback keyword  value  ]
199      length 5+:         [ "name" callback [ keyword value ]+ ]
200                    or   [ "name" [ keyword value ]+ ]
201   */
202   plist_p = (length > 2 && (KEYWORDP (contents [1])
203                             || KEYWORDP (contents [2])));
204
205   pgui_item->name = contents [0];
206   if (length > 1 && !KEYWORDP (contents [1]))
207     {
208       pgui_item->callback = contents [1];
209       start = 2;
210     }
211   else
212     start =1;
213
214   if (!plist_p && length > 2)
215     /* the old way */
216     {
217       pgui_item->active = contents [2];
218       if (length == 4)
219         pgui_item->suffix = contents [3];
220     }
221   else
222     /* the new way */
223     {
224       int i;
225       if ((length - start) & 1)
226         signal_simple_error (
227                 "GUI item descriptor has an odd number of keywords and values",
228                              item);
229
230       for (i = start; i < length;)
231         {
232           Lisp_Object key = contents [i++];
233           Lisp_Object val = contents [i++];
234           gui_item_add_keyval_pair (gui_item, key, val, errb);
235         }
236     }
237   return gui_item;
238 }
239
240 Lisp_Object
241 gui_parse_item_keywords (Lisp_Object item)
242 {
243   return make_gui_item_from_keywords_internal (item, ERROR_ME);
244 }
245
246 Lisp_Object
247 gui_parse_item_keywords_no_errors (Lisp_Object item)
248 {
249   return make_gui_item_from_keywords_internal (item, ERROR_ME_NOT);
250 }
251
252 /* convert a gui item into plist properties */
253 void
254 gui_add_item_keywords_to_plist (Lisp_Object plist, Lisp_Object gui_item)
255 {
256   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
257
258   if (!NILP (pgui_item->callback))
259     Fplist_put (plist, Q_callback, pgui_item->callback);
260   if (!NILP (pgui_item->suffix))
261     Fplist_put (plist, Q_suffix, pgui_item->suffix);
262   if (!NILP (pgui_item->active))
263     Fplist_put (plist, Q_active, pgui_item->active);
264   if (!NILP (pgui_item->included))
265     Fplist_put (plist, Q_included, pgui_item->included);
266   if (!NILP (pgui_item->config))
267     Fplist_put (plist, Q_config, pgui_item->config);
268   if (!NILP (pgui_item->filter))
269     Fplist_put (plist, Q_filter, pgui_item->filter);
270   if (!NILP (pgui_item->style))
271     Fplist_put (plist, Q_style, pgui_item->style);
272   if (!NILP (pgui_item->selected))
273     Fplist_put (plist, Q_selected, pgui_item->selected);
274   if (!NILP (pgui_item->keys))
275     Fplist_put (plist, Q_keys, pgui_item->keys);
276   if (!NILP (pgui_item->accelerator))
277     Fplist_put (plist, Q_accelerator, pgui_item->accelerator);
278 }
279
280 /*
281  * Decide whether a GUI item is active by evaluating its :active form
282  * if any
283  */
284 int
285 gui_item_active_p (Lisp_Object gui_item)
286 {
287   /* This function can call lisp */
288
289   /* Shortcut to avoid evaluating Qt each time */
290   return (EQ (XGUI_ITEM (gui_item)->active, Qt)
291           || !NILP (Feval (XGUI_ITEM (gui_item)->active)));
292 }
293
294 /* set menu accelerator key to first underlined character in menu name */
295 Lisp_Object
296 gui_item_accelerator (Lisp_Object gui_item)
297 {
298   Lisp_Gui_Item *pgui = XGUI_ITEM (gui_item);
299
300   if (!NILP (pgui->accelerator))
301     return pgui->accelerator;
302
303   else
304     return gui_name_accelerator (pgui->name);
305 }
306
307 Lisp_Object
308 gui_name_accelerator (Lisp_Object nm)
309 {
310   Bufbyte *name = XSTRING_DATA (nm);
311
312   while (*name)
313     {
314       if (*name == '%')
315         {
316           ++name;
317           if (!(*name))
318             return Qnil;
319           if (*name == '_' && *(name + 1))
320             {
321               Emchar accelerator = charptr_emchar (name + 1);
322               /* #### bogus current_buffer dependency */
323               return make_char (DOWNCASE (current_buffer, accelerator));
324             }
325         }
326         INC_CHARPTR (name);
327     }
328   return make_char (DOWNCASE (current_buffer,
329                               charptr_emchar (XSTRING_DATA (nm))));
330 }
331
332 /*
333  * Decide whether a GUI item is selected by evaluating its :selected form
334  * if any
335  */
336 int
337 gui_item_selected_p (Lisp_Object gui_item)
338 {
339   /* This function can call lisp */
340
341   /* Shortcut to avoid evaluating Qt each time */
342   return (EQ (XGUI_ITEM (gui_item)->selected, Qt)
343           || !NILP (Feval (XGUI_ITEM (gui_item)->selected)));
344 }
345
346 /*
347  * Decide whether a GUI item is included by evaluating its :included
348  * form if given, and testing its :config form against supplied CONFLIST
349  * configuration variable
350  */
351 int
352 gui_item_included_p (Lisp_Object gui_item, Lisp_Object conflist)
353 {
354   /* This function can call lisp */
355   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
356
357   /* Evaluate :included first. Shortcut to avoid evaluating Qt each time */
358   if (!EQ (pgui_item->included, Qt)
359       && NILP (Feval (pgui_item->included)))
360     return 0;
361
362   /* Do :config if conflist is given */
363   if (!NILP (conflist) && !NILP (pgui_item->config)
364       && NILP (Fmemq (pgui_item->config, conflist)))
365     return 0;
366
367   return 1;
368 }
369
370 static DOESNT_RETURN
371 signal_too_long_error (Lisp_Object name)
372 {
373   signal_simple_error ("GUI item produces too long displayable string", name);
374 }
375
376 #ifdef HAVE_WINDOW_SYSTEM
377 /*
378  * Format "left flush" display portion of an item into BUF, guarded by
379  * maximum buffer size BUF_LEN. BUF_LEN does not count for terminating
380  * null character, so actual maximum size of buffer consumed is
381  * BUF_LEN + 1 bytes. If buffer is not big enough, then error is
382  * signaled.
383  * Return value is the offset to the terminating null character into the
384  * buffer.
385  */
386 unsigned int
387 gui_item_display_flush_left (Lisp_Object gui_item,
388                              char *buf, Bytecount buf_len)
389 {
390   /* This function can call lisp */
391   char *p = buf;
392   Bytecount len;
393   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
394
395   /* Copy item name first */
396   CHECK_STRING (pgui_item->name);
397   len = XSTRING_LENGTH (pgui_item->name);
398   if (len > buf_len)
399     signal_too_long_error (pgui_item->name);
400   memcpy (p, XSTRING_DATA (pgui_item->name), len);
401   p += len;
402
403   /* Add space and suffix, if there is a suffix.
404    * If suffix is not string evaluate it */
405   if (!NILP (pgui_item->suffix))
406     {
407       Lisp_Object suffix = pgui_item->suffix;
408       /* Shortcut to avoid evaluating suffix each time */
409       if (!STRINGP (suffix))
410         {
411           suffix = Feval (suffix);
412           CHECK_STRING (suffix);
413         }
414
415       len = XSTRING_LENGTH (suffix);
416       if (p + len + 1 > buf + buf_len)
417         signal_too_long_error (pgui_item->name);
418       *(p++) = ' ';
419       memcpy (p, XSTRING_DATA (suffix), len);
420       p += len;
421     }
422   *p = '\0';
423   return p - buf;
424 }
425
426 /*
427  * Format "right flush" display portion of an item into BUF, guarded by
428  * maximum buffer size BUF_LEN. BUF_LEN does not count for terminating
429  * null character, so actual maximum size of buffer consumed is
430  * BUF_LEN + 1 bytes. If buffer is not big enough, then error is
431  * signaled.
432  * Return value is the offset to the terminating null character into the
433  * buffer.
434  */
435 unsigned int
436 gui_item_display_flush_right (Lisp_Object gui_item,
437                               char *buf, Bytecount buf_len)
438 {
439   Lisp_Gui_Item *pgui_item = XGUI_ITEM (gui_item);
440   *buf = 0;
441
442 #ifdef HAVE_MENUBARS
443   /* Have keys? */
444   if (!menubar_show_keybindings)
445     return 0;
446 #endif
447
448   /* Try :keys first */
449   if (!NILP (pgui_item->keys))
450     {
451       CHECK_STRING (pgui_item->keys);
452       if (XSTRING_LENGTH (pgui_item->keys) + 1 > buf_len)
453         signal_too_long_error (pgui_item->name);
454       memcpy (buf, XSTRING_DATA (pgui_item->keys),
455               XSTRING_LENGTH (pgui_item->keys) + 1);
456       return XSTRING_LENGTH (pgui_item->keys);
457     }
458
459   /* See if we can derive keys out of callback symbol */
460   if (SYMBOLP (pgui_item->callback))
461     {
462       char buf2[1024]; /* #### */
463       Bytecount len;
464
465       where_is_to_char (pgui_item->callback, buf2);
466       len = strlen (buf2);
467       if (len > buf_len)
468         signal_too_long_error (pgui_item->name);
469       strcpy (buf, buf2);
470       return len;
471     }
472
473   /* No keys - no right flush display */
474   return 0;
475 }
476 #endif /* HAVE_WINDOW_SYSTEM */
477
478 static Lisp_Object
479 mark_gui_item (Lisp_Object obj)
480 {
481   Lisp_Gui_Item *p = XGUI_ITEM (obj);
482
483   mark_object (p->name);
484   mark_object (p->callback);
485   mark_object (p->config);
486   mark_object (p->suffix);
487   mark_object (p->active);
488   mark_object (p->included);
489   mark_object (p->config);
490   mark_object (p->filter);
491   mark_object (p->style);
492   mark_object (p->selected);
493   mark_object (p->keys);
494   mark_object (p->accelerator);
495
496   return Qnil;
497 }
498
499 static unsigned long
500 gui_item_hash_internal (Lisp_Object obj, int depth)
501 {
502   Lisp_Gui_Item *p = XGUI_ITEM (obj);
503
504   return HASH2 (HASH5 (internal_hash (p->name, depth + 1),
505                        internal_hash (p->callback, depth + 1),
506                        internal_hash (p->suffix, depth + 1),
507                        internal_hash (p->active, depth + 1),
508                        internal_hash (p->included, depth + 1)),
509                 HASH5 (internal_hash (p->config, depth + 1),
510                        internal_hash (p->filter, depth + 1),
511                        internal_hash (p->style, depth + 1),
512                        internal_hash (p->selected, depth + 1),
513                        internal_hash (p->keys, depth + 1)));
514 }
515
516 static unsigned long
517 gui_item_hash (Lisp_Object obj, int depth)
518 {
519   Lisp_Gui_Item *p = XGUI_ITEM (obj);
520
521   /* Note that this evaluates the active and selected slots so that
522      the hash changes when the result of these changes. */
523   return HASH2 (HASH5 (internal_hash (p->name, depth + 1),
524                        internal_hash (p->callback, depth + 1),
525                        internal_hash (p->suffix, depth + 1),
526                        gui_item_active_p (obj),
527                        internal_hash (p->included, depth + 1)),
528                 HASH5 (internal_hash (p->config, depth + 1),
529                        internal_hash (p->filter, depth + 1),
530                        internal_hash (p->style, depth + 1),
531                        gui_item_selected_p (obj),
532                        internal_hash (p->keys, depth + 1)));
533 }
534
535 int
536 gui_item_id_hash (Lisp_Object hashtable, Lisp_Object gitem, int slot)
537 {
538   int hashid = gui_item_hash_internal (gitem, 0);
539   int id = GUI_ITEM_ID_BITS (hashid, slot);
540   while (!NILP (Fgethash (make_int (id),
541                           hashtable, Qnil)))
542     {
543       id = GUI_ITEM_ID_BITS (id + 1, slot);
544     }
545   return id;
546 }
547
548 static int
549 gui_item_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
550 {
551   Lisp_Gui_Item *p1 = XGUI_ITEM (obj1);
552   Lisp_Gui_Item *p2 = XGUI_ITEM (obj2);
553
554   if (!(internal_equal (p1->name, p2->name, depth + 1)
555         &&
556         internal_equal (p1->callback, p2->callback, depth + 1)
557         &&
558         EQ (p1->suffix, p2->suffix)
559         &&
560         EQ (p1->active, p2->active)
561         &&
562         EQ (p1->included, p2->included)
563         &&
564         EQ (p1->config, p2->config)
565         &&
566         EQ (p1->filter, p2->filter)
567         &&
568         EQ (p1->style, p2->style)
569         &&
570         EQ (p1->selected, p2->selected)
571         &&
572         EQ (p1->accelerator, p2->accelerator)
573         &&
574         EQ (p1->keys, p2->keys)))
575     return 0;
576   return 1;
577 }
578
579 static void
580 print_gui_item (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
581 {
582   Lisp_Gui_Item *g = XGUI_ITEM (obj);
583   char buf[20];
584
585   if (print_readably)
586     error ("printing unreadable object #<gui-item 0x%x>", g->header.uid);
587
588   write_c_string ("#<gui-item ", printcharfun);
589   sprintf (buf, "0x%x>", g->header.uid);
590   write_c_string (buf, printcharfun);
591 }
592
593 /* parse a glyph descriptor into a tree of gui items.
594
595    The gui_item slot of an image instance can be a single item or an
596    arbitrarily nested hierarchy of item lists. */
597
598 static Lisp_Object
599 parse_gui_item_tree_item (Lisp_Object entry)
600 {
601   Lisp_Object ret = entry;
602   struct gcpro gcpro1;
603
604   GCPRO1 (ret);
605
606   if (VECTORP (entry))
607     {
608       ret = gui_parse_item_keywords_no_errors (entry);
609     }
610   else if (STRINGP (entry))
611     {
612       CHECK_STRING (entry);
613     }
614   else
615     signal_simple_error ("item must be a vector or a string", entry);
616
617   RETURN_UNGCPRO (ret);
618 }
619
620 Lisp_Object
621 parse_gui_item_tree_children (Lisp_Object list)
622 {
623   Lisp_Object rest, ret = Qnil, sub = Qnil;
624   struct gcpro gcpro1, gcpro2;
625
626   GCPRO2 (ret, sub);
627   CHECK_CONS (list);
628   /* recursively add items to the tree view */
629   LIST_LOOP (rest, list)
630     {
631       if (CONSP (XCAR (rest)))
632         sub = parse_gui_item_tree_list (XCAR (rest));
633       else
634         sub = parse_gui_item_tree_item (XCAR (rest));
635
636       ret = Fcons (sub, ret);
637     }
638   /* make the order the same as the items we have parsed */
639   RETURN_UNGCPRO (Fnreverse (ret));
640 }
641
642 static Lisp_Object
643 parse_gui_item_tree_list (Lisp_Object list)
644 {
645   Lisp_Object ret;
646   struct gcpro gcpro1;
647   CHECK_CONS (list);
648   /* first one can never be a list */
649   ret = parse_gui_item_tree_item (XCAR (list));
650   GCPRO1 (ret);
651   ret = Fcons (ret, parse_gui_item_tree_children (XCDR (list)));
652   RETURN_UNGCPRO (ret);
653 }
654
655 static void
656 finalize_gui_item (void* header, int for_disksave)
657 {
658 }
659
660 DEFINE_LRECORD_IMPLEMENTATION ("gui-item", gui_item,
661                                mark_gui_item, print_gui_item,
662                                finalize_gui_item, gui_item_equal,
663                                gui_item_hash,
664                                0,
665                                Lisp_Gui_Item);
666
667 void
668 syms_of_gui (void)
669 {
670   INIT_LRECORD_IMPLEMENTATION (gui_item);
671
672   defkeyword (&Q_active,   ":active");
673   defkeyword (&Q_suffix,   ":suffix");
674   defkeyword (&Q_keys,     ":keys");
675   defkeyword (&Q_key_sequence,":key-sequence");
676   defkeyword (&Q_style,    ":style");
677   defkeyword (&Q_selected, ":selected");
678   defkeyword (&Q_filter,   ":filter");
679   defkeyword (&Q_config,   ":config");
680   defkeyword (&Q_included, ":included");
681   defkeyword (&Q_accelerator, ":accelerator");
682   defkeyword (&Q_label, ":label");
683   defkeyword (&Q_callback, ":callback");
684
685   defsymbol (&Qtoggle, "toggle");
686   defsymbol (&Qradio, "radio");
687
688 #ifdef HAVE_POPUPS
689   DEFSUBR (Fpopup_up_p);
690 #endif
691 }
692
693 void
694 vars_of_gui (void)
695 {
696 }