Contents of release-21-2 in 1999-06-17-23.
[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 "bytecode.h"
31
32 Lisp_Object Q_active, Q_suffix, Q_keys, Q_style, Q_selected;
33 Lisp_Object Q_filter, Q_config, Q_included, Q_key_sequence;
34 Lisp_Object Q_accelerator, Q_label, Q_callback;
35 Lisp_Object Qtoggle, Qradio;
36
37 #ifdef HAVE_POPUPS
38
39 /* count of menus/dboxes currently up */
40 int popup_up_p;
41
42 DEFUN ("popup-up-p", Fpopup_up_p, 0, 0, 0, /*
43 Return t if a popup menu or dialog box is up, nil otherwise.
44 See `popup-menu' and `popup-dialog-box'.
45 */
46        ())
47 {
48   return popup_up_p ? Qt : Qnil;
49 }
50 #endif /* HAVE_POPUPS */
51
52 int
53 separator_string_p (CONST char *s)
54 {
55   CONST char *p;
56   char first;
57
58   if (!s || s[0] == '\0')
59     return 0;
60   first = s[0];
61   if (first != '-' && first != '=')
62     return 0;
63   for (p = s; *p == first; p++)
64     ;
65
66   return (*p == '!' || *p == ':' || *p == '\0');
67 }
68
69 /* Massage DATA to find the correct function and argument.  Used by
70    popup_selection_callback() and the msw code. */
71 void
72 get_gui_callback (Lisp_Object data, Lisp_Object *fn, Lisp_Object *arg)
73 {
74   if (SYMBOLP (data)
75       || (COMPILED_FUNCTIONP (data)
76           && XCOMPILED_FUNCTION (data)->flags.interactivep)
77       || (EQ (XCAR (data), Qlambda)
78           && !NILP (Fassq (Qinteractive, Fcdr (Fcdr (data))))))
79     {
80       *fn = Qcall_interactively;
81       *arg = data;
82     }
83   else if (CONSP (data))
84     {
85       *fn = Qeval;
86       *arg = data;
87     }
88   else
89     {
90       *fn = Qeval;
91       *arg = list3 (Qsignal,
92                     list2 (Qquote, Qerror),
93                     list2 (Qquote, list2 (build_translated_string
94                                           ("illegal callback"),
95                                           data)));
96     }
97 }
98
99 /*
100  * Add a value VAL associated with keyword KEY into PGUI_ITEM
101  * structure. If KEY is not a keyword, or is an unknown keyword, then
102  * error is signaled.
103  */
104 void
105 gui_item_add_keyval_pair (Lisp_Object gui_item,
106                           Lisp_Object key, Lisp_Object val, 
107                           Error_behavior errb)
108 {
109   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
110
111   if (!KEYWORDP (key))
112     signal_simple_error_2 ("Non-keyword in gui item", key, pgui_item->name);
113
114   if      (EQ (key, Q_suffix))   pgui_item->suffix   = val;
115   else if (EQ (key, Q_active))   pgui_item->active   = val;
116   else if (EQ (key, Q_included)) pgui_item->included = val;
117   else if (EQ (key, Q_config))   pgui_item->config   = val;
118   else if (EQ (key, Q_filter))   pgui_item->filter   = val;
119   else if (EQ (key, Q_style))    pgui_item->style    = val;
120   else if (EQ (key, Q_selected)) pgui_item->selected = val;
121   else if (EQ (key, Q_keys))     pgui_item->keys     = val;
122   else if (EQ (key, Q_callback))         pgui_item->callback     = val;
123   else if (EQ (key, Q_key_sequence)) ;   /* ignored for FSF compatability */
124   else if (EQ (key, Q_label)) ;   /* ignored for 21.0 implement in 21.2  */
125   else if (ERRB_EQ (errb, ERROR_ME))
126     signal_simple_error_2 ("Unknown keyword in gui item", key, pgui_item->name);
127 }
128
129 void
130 gui_item_init (Lisp_Object gui_item)
131 {
132   struct Lisp_Gui_Item *lp = XGUI_ITEM (gui_item);
133
134   lp->name     = Qnil;
135   lp->callback = Qnil;
136   lp->suffix   = Qnil;
137   lp->active   = Qt;
138   lp->included = Qt;
139   lp->config   = Qnil;
140   lp->filter   = Qnil;
141   lp->style    = Qnil;
142   lp->selected = Qnil;
143   lp->keys     = Qnil;
144 }
145
146 Lisp_Object
147 allocate_gui_item ()
148 {
149   struct Lisp_Gui_Item *lp =
150     alloc_lcrecord_type (struct Lisp_Gui_Item, &lrecord_gui_item);
151   Lisp_Object val;
152
153   zero_lcrecord (lp);
154   XSETGUI_ITEM (val, lp);
155
156   gui_item_init (val);
157
158   return val;
159 }
160
161 /*
162  * ITEM is a lisp vector, describing a menu item or a button. The
163  * function extracts the description of the item into the PGUI_ITEM
164  * structure.
165  */
166 static Lisp_Object
167 make_gui_item_from_keywords_internal (Lisp_Object item,
168                                       Error_behavior errb)
169 {
170   int length, plist_p, start;
171   Lisp_Object *contents;
172   Lisp_Object gui_item = allocate_gui_item ();
173   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
174
175   CHECK_VECTOR (item);
176   length = XVECTOR_LENGTH (item);
177   contents = XVECTOR_DATA (item);
178
179   if (length < 1)
180     signal_simple_error ("GUI item descriptors must be at least 1 elts long", item);
181
182   /* length 1:                  [ "name" ]
183      length 2:          [ "name" callback ]
184      length 3:          [ "name" callback active-p ]
185                    or   [ "name" keyword  value  ]
186      length 4:          [ "name" callback active-p suffix ]
187                    or   [ "name" callback keyword  value  ]
188      length 5+:         [ "name" callback [ keyword value ]+ ]
189                    or   [ "name" [ keyword value ]+ ]
190   */
191   plist_p = (length > 2 && (KEYWORDP (contents [1])
192                             || KEYWORDP (contents [2])));
193
194   pgui_item->name = contents [0];
195   if (length > 1 && !KEYWORDP (contents [1]))
196     {
197       pgui_item->callback = contents [1];
198       start = 2;
199     }
200   else 
201     start =1;
202
203   if (!plist_p && length > 2)
204     /* the old way */
205     {
206       pgui_item->active = contents [2];
207       if (length == 4)
208         pgui_item->suffix = contents [3];
209     }
210   else
211     /* the new way */
212     {
213       int i;
214       if ((length - start) & 1)
215         signal_simple_error (
216                 "GUI item descriptor has an odd number of keywords and values",
217                              item);
218
219       for (i = start; i < length;)
220         {
221           Lisp_Object key = contents [i++];
222           Lisp_Object val = contents [i++];
223           gui_item_add_keyval_pair (gui_item, key, val, errb);
224         }
225     }
226   return gui_item;
227 }
228
229 Lisp_Object
230 gui_parse_item_keywords (Lisp_Object item)
231 {
232   return make_gui_item_from_keywords_internal (item, ERROR_ME);
233 }
234
235 Lisp_Object
236 gui_parse_item_keywords_no_errors (Lisp_Object item)
237 {
238   return make_gui_item_from_keywords_internal (item, ERROR_ME_NOT);
239 }
240
241 /* convert a gui item into plist properties */
242 void
243 gui_add_item_keywords_to_plist (Lisp_Object plist, Lisp_Object gui_item)
244 {
245   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
246   
247   if (!NILP (pgui_item->callback))
248     Fplist_put (plist, Q_callback, pgui_item->callback);
249   if (!NILP (pgui_item->suffix))
250     Fplist_put (plist, Q_suffix, pgui_item->suffix);
251   if (!NILP (pgui_item->active))
252     Fplist_put (plist, Q_active, pgui_item->active);
253   if (!NILP (pgui_item->included))
254     Fplist_put (plist, Q_included, pgui_item->included);
255   if (!NILP (pgui_item->config))
256     Fplist_put (plist, Q_config, pgui_item->config);
257   if (!NILP (pgui_item->filter))
258     Fplist_put (plist, Q_filter, pgui_item->filter);
259   if (!NILP (pgui_item->style))
260     Fplist_put (plist, Q_style, pgui_item->style);
261   if (!NILP (pgui_item->selected))
262     Fplist_put (plist, Q_selected, pgui_item->selected);
263   if (!NILP (pgui_item->keys))
264     Fplist_put (plist, Q_keys, pgui_item->keys);
265 }
266
267 /*
268  * Decide whether a GUI item is active by evaluating its :active form
269  * if any
270  */
271 int
272 gui_item_active_p (Lisp_Object gui_item)
273 {
274   /* This function can call lisp */
275
276   /* Shortcut to avoid evaluating Qt each time */
277   return (EQ (XGUI_ITEM (gui_item)->active, Qt)
278           || !NILP (Feval (XGUI_ITEM (gui_item)->active)));
279 }
280
281 /*
282  * Decide whether a GUI item is selected by evaluating its :selected form
283  * if any
284  */
285 int
286 gui_item_selected_p (Lisp_Object gui_item)
287 {
288   /* This function can call lisp */
289
290   /* Shortcut to avoid evaluating Qt each time */
291   return (EQ (XGUI_ITEM (gui_item)->selected, Qt)
292           || !NILP (Feval (XGUI_ITEM (gui_item)->selected)));
293 }
294
295 /*
296  * Decide whether a GUI item is included by evaluating its :included
297  * form if given, and testing its :config form against supplied CONFLIST
298  * configuration variable
299  */
300 int
301 gui_item_included_p (Lisp_Object gui_item, Lisp_Object conflist)
302 {
303   /* This function can call lisp */
304   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
305
306   /* Evaluate :included first. Shortcut to avoid evaluating Qt each time */
307   if (!EQ (pgui_item->included, Qt)
308       && NILP (Feval (pgui_item->included)))
309     return 0;
310
311   /* Do :config if conflist is given */
312   if (!NILP (conflist) && !NILP (pgui_item->config)
313       && NILP (Fmemq (pgui_item->config, conflist)))
314     return 0;
315
316   return 1;
317 }
318
319 static DOESNT_RETURN
320 signal_too_long_error (Lisp_Object name)
321 {
322   signal_simple_error ("GUI item produces too long displayable string", name);
323 }
324
325 #ifdef HAVE_WINDOW_SYSTEM
326 /*
327  * Format "left flush" display portion of an item into BUF, guarded by
328  * maximum buffer size BUF_LEN. BUF_LEN does not count for terminating
329  * null character, so actual maximum size of buffer consumed is
330  * BUF_LEN + 1 bytes. If buffer is not big enough, then error is
331  * signaled.
332  * Return value is the offset to the terminating null character into the
333  * buffer.
334  */
335 unsigned int
336 gui_item_display_flush_left  (Lisp_Object gui_item,
337                               char* buf, Bytecount buf_len)
338 {
339   char *p = buf;
340   Bytecount len;
341   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
342
343   /* Copy item name first */
344   CHECK_STRING (pgui_item->name);
345   len = XSTRING_LENGTH (pgui_item->name);
346   if (len > buf_len)
347     signal_too_long_error (pgui_item->name);
348   memcpy (p, XSTRING_DATA (pgui_item->name), len);
349   p += len;
350
351   /* Add space and suffix, if there is a suffix.
352    * If suffix is not string evaluate it */
353   if (!NILP (pgui_item->suffix))
354     {
355       Lisp_Object suffix = pgui_item->suffix;
356       /* Shortcut to avoid evaluating suffix each time */
357       if (!STRINGP (suffix))
358         {
359           suffix = Feval (suffix);
360           CHECK_STRING (suffix);
361         }
362
363       len = XSTRING_LENGTH (suffix);
364       if (p + len + 1 > buf + buf_len)
365         signal_too_long_error (pgui_item->name);
366       *(p++) = ' ';
367       memcpy (p, XSTRING_DATA (suffix), len);
368       p += len;
369     }
370   *p = '\0';
371   return p - buf;
372 }
373
374 /*
375  * Format "right flush" display portion of an item into BUF, guarded by
376  * maximum buffer size BUF_LEN. BUF_LEN does not count for terminating
377  * null character, so actual maximum size of buffer consumed is
378  * BUF_LEN + 1 bytes. If buffer is not big enough, then error is
379  * signaled.
380  * Return value is the offset to the terminating null character into the
381  * buffer.
382  */
383 unsigned int
384 gui_item_display_flush_right (Lisp_Object gui_item,
385                               char* buf, Bytecount buf_len)
386 {
387   struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item);
388   *buf = 0;
389
390   /* Have keys? */
391   if (!menubar_show_keybindings)
392     return 0;
393
394   /* Try :keys first */
395   if (!NILP (pgui_item->keys))
396     {
397       CHECK_STRING (pgui_item->keys);
398       if (XSTRING_LENGTH (pgui_item->keys) > buf_len)
399         signal_too_long_error (pgui_item->name);
400       strcpy (buf, (CONST char *) XSTRING_DATA (pgui_item->keys));
401       return XSTRING_LENGTH (pgui_item->keys);
402     }
403
404   /* See if we can derive keys out of callback symbol */
405   if (SYMBOLP (pgui_item->callback))
406     {
407       char buf2 [1024];
408       Bytecount len;
409
410       where_is_to_char (pgui_item->callback, buf2);
411       len = strlen (buf2);
412       if (len > buf_len)
413         signal_too_long_error (pgui_item->name);
414       strcpy (buf, buf2);
415       return len;
416     }
417
418   /* No keys - no right flush display */
419   return 0;
420 }
421 #endif /* HAVE_WINDOW_SYSTEM */
422
423 static Lisp_Object
424 mark_gui_item (Lisp_Object obj, void (*markobj) (Lisp_Object))
425 {
426   struct Lisp_Gui_Item *p = XGUI_ITEM (obj);
427
428   markobj (p->name);
429   markobj (p->callback);
430   markobj (p->suffix);
431   markobj (p->active);
432   markobj (p->included);
433   markobj (p->config);
434   markobj (p->filter);
435   markobj (p->style);
436   markobj (p->selected);
437   markobj (p->keys);
438
439   return Qnil;
440 }
441
442 static unsigned long
443 gui_item_hash (Lisp_Object obj, int depth)
444 {
445   struct Lisp_Gui_Item *p = XGUI_ITEM (obj);
446
447   return HASH2 (HASH5 (internal_hash (p->name, depth + 1),
448                        internal_hash (p->callback, depth + 1),
449                        internal_hash (p->suffix, depth + 1),
450                        internal_hash (p->active, depth + 1),
451                        internal_hash (p->included, depth + 1)),
452                 HASH5 (internal_hash (p->config, depth + 1),
453                        internal_hash (p->filter, depth + 1),
454                        internal_hash (p->style, depth + 1),
455                        internal_hash (p->selected, depth + 1),
456                        internal_hash (p->keys, depth + 1)));
457 }
458
459 int
460 gui_item_id_hash (Lisp_Object hashtable, Lisp_Object gitem, int slot)
461 {
462   int hashid = gui_item_hash (gitem, 0);
463   int id = GUI_ITEM_ID_BITS (hashid, slot);
464   while (!NILP (Fgethash (make_int (id),
465                           hashtable, Qnil)))
466     {
467       id = GUI_ITEM_ID_BITS (id + 1, slot);
468     }
469   return id;
470 }
471
472 static int
473 gui_item_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
474 {
475   struct Lisp_Gui_Item *p1 = XGUI_ITEM (obj1);
476   struct Lisp_Gui_Item *p2 = XGUI_ITEM (obj2);
477
478   if (!(internal_equal (p1->name, p2->name, depth + 1)
479         &&
480         internal_equal (p1->callback, p2->callback, depth + 1)
481         &&
482         EQ (p1->suffix, p2->suffix)
483         &&
484         EQ (p1->active, p2->active)
485         &&
486         EQ (p1->included, p2->included)
487         &&
488         EQ (p1->config, p2->config)
489         &&
490         EQ (p1->filter, p2->filter)
491         &&
492         EQ (p1->style, p2->style)
493         &&
494         EQ (p1->selected, p2->selected)
495         &&
496         EQ (p1->keys, p2->keys)))
497     return 0;
498   return 1;
499 }
500
501 static void
502 print_gui_item (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
503 {
504   struct Lisp_Gui_Item *g = XGUI_ITEM (obj);
505   char buf[20];
506
507   if (print_readably)
508     error ("printing unreadable object #<gui-item 0x%x>", g->header.uid);
509
510   write_c_string ("#<gui-item ", printcharfun);
511   sprintf (buf, "0x%x>", g->header.uid);
512   write_c_string (buf, printcharfun);
513 }
514
515 DEFINE_LRECORD_IMPLEMENTATION ("gui-item", gui_item,
516                                mark_gui_item, print_gui_item,
517                                0, gui_item_equal,
518                                gui_item_hash,
519                                struct Lisp_Gui_Item);
520
521 void
522 syms_of_gui (void)
523 {
524   defkeyword (&Q_active,   ":active");
525   defkeyword (&Q_suffix,   ":suffix");
526   defkeyword (&Q_keys,     ":keys");
527   defkeyword (&Q_key_sequence,":key-sequence");
528   defkeyword (&Q_style,    ":style");
529   defkeyword (&Q_selected, ":selected");
530   defkeyword (&Q_filter,   ":filter");
531   defkeyword (&Q_config,   ":config");
532   defkeyword (&Q_included, ":included");
533   defkeyword (&Q_accelerator, ":accelerator");
534   defkeyword (&Q_label, ":label");
535   defkeyword (&Q_callback, ":callback");
536
537   defsymbol (&Qtoggle, "toggle");
538   defsymbol (&Qradio, "radio");
539
540 #ifdef HAVE_POPUPS
541   DEFSUBR (Fpopup_up_p);
542 #endif
543 }
544
545 void
546 vars_of_gui (void)
547 {
548 }