XEmacs 21.2-b1
[chise/xemacs-chise.git.1] / src / menubar.c
diff --git a/src/menubar.c b/src/menubar.c
new file mode 100644 (file)
index 0000000..7b1fb16
--- /dev/null
@@ -0,0 +1,595 @@
+/* Implements an elisp-programmable menubar.
+   Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* #### There ain't much here because menubars have not been
+   properly abstracted yet. */
+
+#include <config.h>
+#include "lisp.h"
+
+#include "buffer.h"
+#include "device.h"
+#include "frame.h"
+#include "gui.h"
+#include "menubar.h"
+#include "redisplay.h"
+#include "window.h"
+
+int menubar_show_keybindings;
+Lisp_Object Vmenubar_configuration;
+
+Lisp_Object Qcurrent_menubar;
+
+Lisp_Object Qactivate_menubar_hook, Vactivate_menubar_hook;
+
+Lisp_Object Vmenubar_visible_p;
+
+static Lisp_Object Vcurrent_menubar; /* DO NOT ever reference this.
+                                       Always go through Qcurrent_menubar.
+                                       See below. */
+
+Lisp_Object Vblank_menubar;
+
+int popup_menu_titles;
+
+Lisp_Object Vmenubar_pointer_glyph;
+
+static int
+menubar_variable_changed (Lisp_Object sym, Lisp_Object *val,
+                         Lisp_Object in_object, int flags)
+{
+  MARK_MENUBAR_CHANGED;
+  return 0;
+}
+
+void
+update_frame_menubars (struct frame *f)
+{
+  if (f->menubar_changed || f->windows_changed)
+    MAYBE_FRAMEMETH (f, update_frame_menubars, (f));
+
+  f->menubar_changed = 0;
+}
+
+void
+free_frame_menubars (struct frame *f)
+{
+  /* If we had directly allocated any memory for the menubars instead
+     of using all Lisp_Objects this is where we would now free it. */
+
+  MAYBE_FRAMEMETH (f, free_frame_menubars, (f));
+}
+
+static void
+menubar_visible_p_changed (Lisp_Object specifier, struct window *w,
+                          Lisp_Object oldval)
+{
+  MARK_MENUBAR_CHANGED;
+}
+
+static void
+menubar_visible_p_changed_in_frame (Lisp_Object specifier, struct frame *f,
+                                   Lisp_Object oldval)
+{
+  update_frame_menubars (f);
+}
+
+Lisp_Object
+current_frame_menubar (CONST struct frame* f)
+{
+  struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
+  return symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
+}
+
+Lisp_Object
+menu_parse_submenu_keywords (Lisp_Object desc, struct gui_item* pgui_item)
+{
+  /* Menu descriptor should be a list */
+  CHECK_CONS (desc);
+
+  /* First element may be menu name, although can be omitted.
+     Let's think that if stuff begins with anything than a keyword
+     or a list (submenu), this is a menu name, expected to be a stirng */
+  if (!KEYWORDP (XCAR (desc)) && !CONSP (XCAR (desc)))
+    {
+      CHECK_STRING (XCAR (desc));
+      pgui_item->name = XCAR (desc);
+      desc = XCDR (desc);
+      if (!NILP (desc))
+       CHECK_CONS (desc);
+    }
+
+  /* Walk along all key-value pairs */
+  while (!NILP(desc) && KEYWORDP (XCAR (desc)))
+    {
+      Lisp_Object key, val;
+      key = XCAR (desc);
+      desc = XCDR (desc);
+      CHECK_CONS (desc);
+      val = XCAR (desc);
+      desc = XCDR (desc);
+      if (!NILP (desc))
+       CHECK_CONS (desc);
+      gui_item_add_keyval_pair (pgui_item, key, val);
+    }
+
+  /* Return the rest - supposed to be a list of items */
+  return desc;
+}
+
+DEFUN ("menu-find-real-submenu", Fmenu_find_real_submenu, 2, 2, 0, /*
+Find a submenu descriptor within DESC by following PATH.
+This function finds a submenu descriptor, either from the description
+DESC or generated by a filter within DESC. The function regards :config
+and :included keywords in the DESC, and expands submenus along the
+PATH using :filter functions. Return value is a descriptor for the
+submenu, NOT expanded and NOT checked against :config and :included.
+Also, individual menu items are not looked for, only submenus.
+
+See also 'find-menu-item'.
+*/
+       (desc, path))
+{
+  Lisp_Object path_entry, submenu_desc, submenu;
+  struct gcpro gcpro1;
+  struct gui_item gui_item;
+
+  gui_item_init (&gui_item);
+  GCPRO_GUI_ITEM (&gui_item);
+  
+  EXTERNAL_LIST_LOOP (path_entry, path)
+    {
+      /* Verify that DESC describes a menu, not single item */
+      if (!CONSP (desc))
+       RETURN_UNGCPRO (Qnil);
+
+      /* Parse this menu */
+      desc = menu_parse_submenu_keywords (desc, &gui_item);
+
+      /* Check that this (sub)menu is active */
+      if (!gui_item_active_p (&gui_item))
+       RETURN_UNGCPRO (Qnil);
+
+      /* Apply :filter */
+      if (!NILP (gui_item.filter))
+       desc = call1 (gui_item.filter, desc);
+
+      /* Find the next menu on the path inside this one */
+      EXTERNAL_LIST_LOOP (submenu_desc, desc)
+       {
+         submenu = XCAR (submenu_desc);
+         if (CONSP (submenu)
+             && STRINGP (XCAR (submenu))
+             && !NILP (Fstring_equal (XCAR (submenu), XCAR (path_entry))))
+           {
+             desc = submenu;
+             goto descend;
+           }
+       }
+      /* Submenu not found */
+      RETURN_UNGCPRO (Qnil);
+
+    descend:
+      /* Prepare for the next iteration */
+      gui_item_init (&gui_item);
+    }
+
+  /* We have successfully descended down the end of the path */
+  UNGCPRO;
+  return desc;
+}
+
+DEFUN ("popup-menu", Fpopup_menu, 1, 2, 0, /*
+Pop up the given menu.
+A menu description is a list of menu items, strings, and submenus.
+
+The first element of a menu must be a string, which is the name of the menu.
+This is the string that will be displayed in the parent menu, if any.  For
+toplevel menus, it is ignored.  This string is not displayed in the menu
+itself.
+
+If an element of a menu is a string, then that string will be presented in
+the menu as unselectable text.
+
+If an element of a menu is a string consisting solely of hyphens, then that
+item will be presented as a solid horizontal line.
+
+If an element of a menu is a list, it is treated as a submenu.  The name of
+that submenu (the first element in the list) will be used as the name of the
+item representing this menu on the parent.
+
+Otherwise, the element must be a vector, which describes a menu item.
+A menu item can have any of the following forms:
+
+ [ "name" callback <active-p> ]
+ [ "name" callback <active-p> <suffix> ]
+ [ "name" callback :<keyword> <value>  :<keyword> <value> ... ]
+
+The name is the string to display on the menu; it is filtered through the
+resource database, so it is possible for resources to override what string
+is actually displayed.
+
+If the `callback' of a menu item is a symbol, then it must name a command.
+It will be invoked with `call-interactively'.  If it is a list, then it is
+evaluated with `eval'.
+
+The possible keywords are this:
+
+ :active   <form>    Same as <active-p> in the first two forms: the
+                     expression is evaluated just before the menu is
+                     displayed, and the menu will be selectable only if
+                     the result is non-nil.
+
+ :suffix   <form>    Same as <suffix> in the second form: the expression
+                     is evaluated just before the menu is displayed and
+                    resulting string is appended to the displayed name,
+                    providing a convenient way of adding the name of a
+                    command's ``argument'' to the menu, like
+                    ``Kill Buffer NAME''.
+
+ :keys     "string"  Normally, the keyboard equivalents of commands in
+                     menus are displayed when the `callback' is a symbol.
+                     This can be used to specify keys for more complex menu
+                     items.  It is passed through `substitute-command-keys'
+                     first.
+
+ :style    <style>   Specifies what kind of object this menu item is:
+
+                        nil     A normal menu item.
+                        toggle  A toggle button.
+                        radio   A radio button.
+
+                     The only difference between toggle and radio buttons is
+                     how they are displayed.  But for consistency, a toggle
+                     button should be used when there is one option whose
+                     value can be turned on or off, and radio buttons should
+                     be used when there is a set of mutually exclusive
+                     options.  When using a group of radio buttons, you
+                     should arrange for no more than one to be marked as
+                     selected at a time.
+
+ :selected <form>    Meaningful only when STYLE is `toggle' or `radio'.
+                     This specifies whether the button will be in the
+                     selected or unselected state.
+
+For example:
+
+ [ "Save As..."    write-file  t ]
+ [ "Revert Buffer" revert-buffer (buffer-modified-p) ]
+ [ "Read Only"     toggle-read-only :style toggle :selected buffer-read-only ]
+
+See menubar.el for many more examples.
+*/
+       (menu_desc, event))
+{
+  struct frame *f = decode_frame(Qnil);
+  MAYBE_FRAMEMETH (f, popup_menu, (menu_desc,event));
+  return Qnil;
+}
+
+DEFUN ("normalize-menu-item-name", Fnormalize_menu_item_name, 1, 2, 0, /*
+Convert a menu item name string into normal form, and return the new string.
+Menu item names should be converted to normal form before being compared.
+*/
+       (name, buffer))
+{
+  struct buffer *buf = decode_buffer (buffer, 0);
+  struct Lisp_String *n;
+  Charcount end;
+  int i;
+  Bufbyte *name_data;
+  Bufbyte *string_result;
+  Bufbyte *string_result_ptr;
+  Emchar elt;
+  int expecting_underscore = 0;
+
+  CHECK_STRING (name);
+
+  n = XSTRING (name);
+  end = string_char_length (n);
+  name_data = string_data (n);
+
+  string_result = (Bufbyte *) alloca (end * MAX_EMCHAR_LEN);
+  string_result_ptr = string_result;
+  for (i = 0; i < end; i++)
+    {
+      elt = charptr_emchar (name_data);
+      elt = DOWNCASE (buf, elt);
+      if (expecting_underscore)
+       {
+         expecting_underscore = 0;
+         switch (elt)
+           {
+           case '%':
+             /* Allow `%%' to mean `%'.  */
+             string_result_ptr += set_charptr_emchar (string_result_ptr, '%');
+             break;
+           case '_':
+             break;
+           default:
+             string_result_ptr += set_charptr_emchar (string_result_ptr, '%');
+             string_result_ptr += set_charptr_emchar (string_result_ptr, elt);
+           }
+       }
+      else if (elt == '%')
+       expecting_underscore = 1;
+      else
+       string_result_ptr += set_charptr_emchar (string_result_ptr, elt);
+      INC_CHARPTR (name_data);
+    }
+
+  return make_string (string_result, string_result_ptr - string_result);
+}
+
+void
+syms_of_menubar (void)
+{
+  defsymbol (&Qcurrent_menubar, "current-menubar");
+  DEFSUBR (Fpopup_menu);
+  DEFSUBR (Fnormalize_menu_item_name);
+  DEFSUBR (Fmenu_find_real_submenu);
+}
+
+void
+vars_of_menubar (void)
+{
+  {
+    /* put in Vblank_menubar a menubar value which has no visible
+     * items.  This is a bit tricky due to various quirks.  We
+     * could use '(["" nil nil]), but this is apparently equivalent
+     * to '(nil), and a new frame created with this menubar will
+     * get a vertically-squished menubar.  If we use " " as the
+     * button title instead of "", we get an etched button border.
+     * So we use
+     *  '(("No active menubar" ["" nil nil]))
+     * which creates a menu whose title is "No active menubar",
+     * and this works fine.
+     */
+
+    Lisp_Object menu_item[3];
+    static CONST char *blank_msg = "No active menubar";
+
+    menu_item[0] = build_string ("");
+    menu_item[1] = Qnil;
+    menu_item[2] = Qnil;
+    Vblank_menubar = Fcons (Fcons (build_string (blank_msg),
+                                  Fcons (Fvector (3, &menu_item[0]),
+                                         Qnil)),
+                           Qnil);
+    Vblank_menubar = Fpurecopy (Vblank_menubar);
+    staticpro (&Vblank_menubar);
+  }
+
+  DEFVAR_BOOL ("popup-menu-titles", &popup_menu_titles /*
+If true, popup menus will have title bars at the top.
+*/ );
+  popup_menu_titles = 1;
+
+  /* #### Replace current menubar with a specifier. */
+
+  /* All C code must access the menubar via Qcurrent_menubar
+     because it can be buffer-local.  Note that Vcurrent_menubar
+     doesn't need to exist at all, except for the magic function. */
+
+  DEFVAR_LISP_MAGIC ("current-menubar", &Vcurrent_menubar /*
+The current menubar.  This may be buffer-local.
+
+When the menubar is changed, the function `set-menubar-dirty-flag' has to
+be called for the menubar to be updated on the frame.  See `set-menubar'
+and `set-buffer-menubar'.
+
+A menubar is a list of menus and menu-items.
+A menu is a list of menu items, keyword-value pairs, strings, and submenus.
+
+The first element of a menu must be a string, which is the name of the menu.
+This is the string that will be displayed in the parent menu, if any.  For
+toplevel menus, it is ignored.  This string is not displayed in the menu
+itself.
+
+Immediately following the name string of the menu, any of three
+optional keyword-value pairs is permitted.
+
+If an element of a menu (or menubar) is a string, then that string will be
+presented as unselectable text.
+
+If an element of a menu is a string consisting solely of hyphens, then that
+item will be presented as a solid horizontal line.
+
+If an element of a menu is a list, it is treated as a submenu.  The name of
+that submenu (the first element in the list) will be used as the name of the
+item representing this menu on the parent.
+
+If an element of a menubar is `nil', then it is used to represent the
+division between the set of menubar-items which are flushleft and those
+which are flushright.
+
+Otherwise, the element must be a vector, which describes a menu item.
+A menu item can have any of the following forms:
+
+ [ "name" callback <active-p> ]
+ [ "name" callback <active-p> <suffix> ]
+ [ "name" callback :<keyword> <value>  :<keyword> <value> ... ]
+
+The name is the string to display on the menu; it is filtered through the
+resource database, so it is possible for resources to override what string
+is actually displayed.
+
+If the `callback' of a menu item is a symbol, then it must name a command.
+It will be invoked with `call-interactively'.  If it is a list, then it is
+evaluated with `eval'.
+
+The possible keywords are this:
+
+ :active   <form>    Same as <active-p> in the first two forms: the
+                     expression is evaluated just before the menu is
+                     displayed, and the menu will be selectable only if
+                     the result is non-nil.
+
+ :suffix   <form>    Same as <suffix> in the second form: the expression
+                     is evaluated just before the menu is displayed and
+                    resulting string is appended to the displayed name,
+                    providing a convenient way of adding the name of a
+                    command's ``argument'' to the menu, like
+                    ``Kill Buffer NAME''.
+
+ :keys     "string"  Normally, the keyboard equivalents of commands in
+                     menus are displayed when the `callback' is a symbol.
+                     This can be used to specify keys for more complex menu
+                     items.  It is passed through `substitute-command-keys'
+                     first.
+
+ :style    <style>   Specifies what kind of object this menu item is:
+
+                        nil     A normal menu item.
+                        toggle  A toggle button.
+                        radio   A radio button.
+                        button  A menubar button.
+
+                     The only difference between toggle and radio buttons is
+                     how they are displayed.  But for consistency, a toggle
+                     button should be used when there is one option whose
+                     value can be turned on or off, and radio buttons should
+                     be used when there is a set of mutually exclusive
+                     options.  When using a group of radio buttons, you
+                     should arrange for no more than one to be marked as
+                     selected at a time.
+
+ :selected <form>    Meaningful only when STYLE is `toggle', `radio' or
+                     `button'.  This specifies whether the button will be in
+                    the selected or unselected state.
+
+ :included <form>    This can be used to control the visibility of a menu or
+                    menu item.  The form is evaluated and the menu or menu
+                    item is only displayed if the result is non-nil.
+
+ :config  <symbol>   This is an efficient shorthand for
+                        :included (memq symbol menubar-configuration)
+                    See the variable `menubar-configuration'.
+
+ :filter <function>  A menu filter can only be used in a menu item list.
+                    (i.e.:  not in a menu item itself).  It is used to
+                    sensitize or incrementally create a submenu only when
+                    it is selected by the user and not every time the
+                    menubar is activated.  The filter function is passed
+                    the list of menu items in the submenu and must return a
+                    list of menu items to be used for the menu.  It is
+                    called only when the menu is about to be displayed, so
+                    other menus may already be displayed.  Vile and
+                    terrible things will happen if a menu filter function
+                    changes the current buffer, window, or frame.  It
+                    also should not raise, lower, or iconify any frames.
+                    Basically, the filter function should have no
+                    side-effects.
+
+For example:
+
+ ("File"
+  :filter file-menu-filter     ; file-menu-filter is a function that takes
+                               ; one argument (a list of menu items) and
+                               ; returns a list of menu items
+  [ "Save As..."    write-file  t ]
+  [ "Revert Buffer" revert-buffer (buffer-modified-p) ]
+  [ "Read Only"     toggle-read-only :style toggle
+                     :selected buffer-read-only ]
+  )
+
+See x-menubar.el for many more examples.
+
+After the menubar is clicked upon, but before any menus are popped up,
+the functions on the `activate-menubar-hook' are invoked to make top-level
+changes to the menus and menubar.  Note, however, that the use of menu
+filters (using the :filter keyword) is usually a more efficient way to
+dynamically alter or sensitize menus.
+*/, menubar_variable_changed);
+
+  Vcurrent_menubar = Qnil;
+
+  DEFVAR_LISP ("activate-menubar-hook", &Vactivate_menubar_hook /*
+Function or functions called before a menubar menu is pulled down.
+These functions are called with no arguments, and should interrogate and
+modify the value of `current-menubar' as desired.
+
+The functions on this hook are invoked after the mouse goes down, but before
+the menu is mapped, and may be used to activate, deactivate, add, or delete
+items from the menus.  However, it is probably the case that using a :filter
+keyword in a submenu would be a more efficient way of updating menus.  See
+the documentation of `current-menubar'.
+
+These functions may return the symbol `t' to assert that they have made
+no changes to the menubar.  If any other value is returned, the menubar is
+recomputed.  If `t' is returned but the menubar has been changed, then the
+changes may not show up right away.  Returning `nil' when the menubar has
+not changed is not so bad; more computation will be done, but redisplay of
+the menubar will still be performed optimally.
+*/ );
+  Vactivate_menubar_hook = Qnil;
+  defsymbol (&Qactivate_menubar_hook, "activate-menubar-hook");
+
+  DEFVAR_BOOL ("menubar-show-keybindings", &menubar_show_keybindings /*
+If true, the menubar will display keyboard equivalents.
+If false, only the command names will be displayed.
+*/ );
+  menubar_show_keybindings = 1;
+
+  DEFVAR_LISP_MAGIC ("menubar-configuration", &Vmenubar_configuration /*
+A list of symbols, against which the value of the :config tag for each
+menubar item will be compared.  If a menubar item has a :config tag, then
+it is omitted from the menubar if that tag is not a member of the
+`menubar-configuration' list.
+*/ , menubar_variable_changed);
+  Vmenubar_configuration = Qnil;
+
+  DEFVAR_LISP ("menubar-pointer-glyph", &Vmenubar_pointer_glyph /*
+*The shape of the mouse-pointer when over the menubar.
+This is a glyph; use `set-glyph-image' to change it.
+If unspecified in a particular domain, the window-system-provided
+default pointer is used.
+*/ );
+
+  Fprovide (intern ("menubar"));
+}
+
+void
+specifier_vars_of_menubar (void)
+{
+  DEFVAR_SPECIFIER ("menubar-visible-p", &Vmenubar_visible_p /*
+*Whether the menubar is visible.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+  Vmenubar_visible_p = Fmake_specifier (Qboolean);
+
+  set_specifier_fallback (Vmenubar_visible_p, list1 (Fcons (Qnil, Qt)));
+  set_specifier_caching (Vmenubar_visible_p,
+                        slot_offset (struct window,
+                                     menubar_visible_p),
+                        menubar_visible_p_changed,
+                        slot_offset (struct frame,
+                                     menubar_visible_p),
+                        menubar_visible_p_changed_in_frame);
+}
+
+void
+complex_vars_of_menubar (void)
+{
+  Vmenubar_pointer_glyph = Fmake_glyph_internal (Qpointer);
+}