/* 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Synched up with: Not in FSF. */

#include <config.h>
#include "lisp.h"

#include "device.h"
#include "frame.h"
#include "redisplay.h"
#include "window.h"

int menubar_show_keybindings;
Lisp_Object Vmenubar_configuration;

Lisp_Object Qcurrent_menubar;
Lisp_Object Qmenu_no_selection_hook;

Lisp_Object Q_active, Q_suffix, Q_keys, Q_style, Q_selected;
Lisp_Object Q_filter, Q_config, Q_included;
Lisp_Object Qtoggle, Qradio, Qbutton;

Lisp_Object Qactivate_menubar_hook, Vactivate_menubar_hook;

Lisp_Object Vmenubar_visible_p;

/* #### This function should disappear.  Replace `current-menubar'
   with a specifier. */
DEFUN ("set-menubar-dirty-flag", Fset_menubar_dirty_flag,
       Sset_menubar_dirty_flag, 0, 0, 0,
       "Tell XEmacs that the menubar has to be updated.")
     ()
{
  MARK_MENUBAR_CHANGED;
  return Qnil;
}

void
update_frame_menubars (struct frame *f)
{
  struct device *d = XDEVICE (f->device);

  if (f->menubar_changed || f->windows_changed)
    MAYBE_DEVMETH (d, update_frame_menubars, (f));

  f->menubar_changed = 0;
}

static void
menubar_visible_p_changed (Lisp_Object specifier, struct window *w,
				   Lisp_Object oldval)
{
  Fset_menubar_dirty_flag ();
}

void
syms_of_menubar (void)
{
  defkeyword (&Q_active,   ":active");
  defkeyword (&Q_suffix,   ":suffix");
  defkeyword (&Q_keys,     ":keys");
  defkeyword (&Q_style,    ":style");
  defkeyword (&Q_selected, ":selected");
  defkeyword (&Q_filter,   ":filter");
  defkeyword (&Q_config,   ":config");
  defkeyword (&Q_included, ":included");

  defsymbol (&Qtoggle, "toggle");
  defsymbol (&Qradio, "radio");
  defsymbol (&Qbutton, "button");

  defsymbol (&Qmenu_no_selection_hook, "menu-no-selection-hook");
  defsymbol (&Qcurrent_menubar, "current-menubar");

  defsubr (&Sset_menubar_dirty_flag);
}

void
vars_of_menubar (void)
{
/* #### Replace current menubar with a specifier. */
/*
 *
 *  This DEFVAR_LISP is just for the benefit of make-docfile.  there is no
 *  C variable Vcurrent_menubar - all C code must access the menubar via
 *  Qcurrent_menubar because it can be buffer-local.
 *

  DEFVAR_LISP ("current-menubar", &Vcurrent_menubar,
   "The current menubar.  This may be buffer-local.\n\
\n\
When the menubar is changed, the function `set-menubar-dirty-flag' has to\n\
be called for the menubar to be updated on the frame.  See `set-menubar'\n\
and `set-buffer-menubar'.\n\
\n\
A menubar is a list of menus and menu-items.\n\
A menu is a list of menu items, keyword-value pairs, strings, and submenus.\n\
\n\
The first element of a menu must be a string, which is the name of the menu.\n\
This is the string that will be displayed in the parent menu, if any.  For\n\
toplevel menus, it is ignored.  This string is not displayed in the menu\n\
itself.\n\
\n\
Immediately following the name string of the menu, any of three\n\
optional keyword-value pairs is permitted.\n\
\n\
If an element of a menu (or menubar) is a string, then that string will be\n\
presented as unselectable text.\n\
\n\
If an element of a menu is a string consisting solely of hyphens, then that\n\
item will be presented as a solid horizontal line.\n\
\n\
If an element of a menu is a list, it is treated as a submenu.  The name of\n\
that submenu (the first element in the list) will be used as the name of the\n\
item representing this menu on the parent.\n\
\n\
If an element of a menubar is `nil', then it is used to represent the\n\
division between the set of menubar-items which are flushleft and those\n\
which are flushright.\n\
\n\
Otherwise, the element must be a vector, which describes a menu item.\n\
A menu item can have any of the following forms:\n\
\n\
 [ \"name\" callback <active-p> ]\n\
 [ \"name\" callback <active-p> \"suffix\" ]\n\
 [ \"name\" callback :<keyword> <value>  :<keyword> <value> ... ]\n\
\n\
The name is the string to display on the menu; it is filtered through the\n\
resource database, so it is possible for resources to override what string\n\
is actually displayed.\n\
\n\
If the `callback' of a menu item is a symbol, then it must name a command.\n\
It will be invoked with `call-interactively'.  If it is a list, then it is\n\
evaluated with `eval'.\n\
\n\
The possible keywords are this:\n\
\n\
 :active   <form>    Same as <active-p> in the first two forms: the\n\
                     expression is evaluated just before the menu is\n\
                     displayed, and the menu will be selectable only if\n\
                     the result is non-nil.\n\
\n\
 :suffix   \"string\"  Same as \"suffix\" in the second form: the suffix is\n\
                     appended to the displayed name, providing a convenient\n\
                     way of adding the name of a command's ``argument'' to\n\
                     the menu, like ``Kill Buffer NAME''.\n\
\n\
 :keys     \"string\"  Normally, the keyboard equivalents of commands in\n\
                     menus are displayed when the `callback' is a symbol.\n\
                     This can be used to specify keys for more complex menu\n\
                     items.  It is passed through `substitute-command-keys'\n\
                     first.\n\
\n\
 :style    <style>   Specifies what kind of object this menu item is:\n\
\n\
                        nil     A normal menu item.\n\
                        toggle  A toggle button.\n\
                        radio   A radio button.\n\
                        button  A menubar button.\n\
\n\
                     The only difference between toggle and radio buttons is\n\
                     how they are displayed.  But for consistency, a toggle\n\
                     button should be used when there is one option whose\n\
                     value can be turned on or off, and radio buttons should\n\
                     be used when there is a set of mutally exclusive\n\
                     options.  When using a group of radio buttons, you\n\
                     should arrange for no more than one to be marked as\n\
                     selected at a time.\n\
\n\
 :selected <form>    Meaningful only when STYLE is `toggle', `radio' or\n\
                     `button'.  This specifies whether the button will be in\n\
		     the selected or unselected state.\n\
\n\
 :included <form>    This can be used to control the visibility of a menu or\n\
		     menu item.  The form is evaluated and the menu or menu\n\
		     item is only displayed if the result is non-nil.\n\
\n\
 :config  <symbol>   This is an efficient shorthand for\n\
		         :included (memq symbol menubar-configuration)\n\
	             See the variable `menubar-configuration'.\n\
\n\
 :filter <function>  A menu filter can only be used in a menu item list.\n\
		     (i.e.:  not in a menu item itself).  It is used to\n\
		     sensitize or incrementally create a submenu only when\n\
		     it is selected by the user and not every time the\n\
		     menubar is activated.  The filter function is passed\n\
		     the list of menu items in the submenu and must return a\n\
		     list of menu items to be used for the menu.  It is\n\
		     called only when the menu is about to be displayed, so\n\
		     other menus may already be displayed.  Vile and\n\
		     terrible things will happen if a menu filter function\n\
		     changes the current buffer, window, or frame.  It\n\
		     also should not raise, lower, or iconify any frames.\n\
		     Basically, the filter function should have no\n\
		     side-effects.\n\
\n\
For example:\n\
\n\
 (\"File\"\n\
  :filter file-menu-filter	; file-menu-filter is a function that takes\n\
				; one argument (a list of menu items) and\n\
				; returns a list of menu items\n\
  [ \"Save As...\"    write-file  t ]\n\
  [ \"Revert Buffer\" revert-buffer (buffer-modified-p) ]\n\
  [ \"Read Only\"     toggle-read-only :style toggle\n\
		      :selected buffer-read-only ]\n\
  )\n\
\n\
See x-menubar.el for many more examples.\n\
\n\
After the menubar is clicked upon, but before any menus are popped up,\n\
the functions on the `activate-menubar-hook' are invoked to make top-level\n\
changes to the menus and menubar.  Note, however, that the use of menu\n\
filters (using the :filter keyword) is usually a more efficient way to\n\
dynamically alter or sensitize menus.");
  */

  Fset (Qcurrent_menubar, Qnil);

  DEFVAR_LISP ("activate-menubar-hook", &Vactivate_menubar_hook,
   "Function or functions called before a menubar menu is pulled down.\n\
These functions are called with no arguments, and should interrogate and\n\
modify the value of `current-menubar' as desired.\n\
\n\
The functions on this hook are invoked after the mouse goes down, but before\n\
the menu is mapped, and may be used to activate, deactivate, add, or delete\n\
items from the menus.  However, it is probably the case that using a :filter\n\
keyword in a submenu would be a more efficient way of updating menus.  See\n\
the documentation of `current-menubar'.\n\
\n\
These functions may return the symbol `t' to assert that they have made\n\
no changes to the menubar.  If any other value is returned, the menubar is\n\
recomputed.  If `t' is returned but the menubar has been changed, then the\n\
changes may not show up right away.  Returning `nil' when the menubar has\n\
not changed is not so bad; more computation will be done, but redisplay of\n\
the menubar will still be performed optimally.");
  Vactivate_menubar_hook = Qnil;
  defsymbol (&Qactivate_menubar_hook, "activate-menubar-hook");

/*
 *  This DEFVAR_LISP is just for the benefit of make-docfile.
  DEFVAR_LISP ("menu-no-selection-hook", &Vmenu_no_selection_hook,
   "Function or functions to call when a menu or dialog box is dismissed\n\
without a selection having been made.");
 */
  Fset (Qmenu_no_selection_hook, Qnil);

  DEFVAR_BOOL ("menubar-show-keybindings", &menubar_show_keybindings,
    "If true, the menubar will display keyboard equivalents.\n\
If false, only the command names will be displayed.");
  menubar_show_keybindings = 1;

  DEFVAR_LISP ("menubar-configuration", &Vmenubar_configuration,
    "A list of symbols, against which the value of the :config tag for each\n\
menubar item will be compared.  If a menubar item has a :config tag, then\n\
it is omitted from the menubar if that tag is not a member of the\n\
`menubar-configuration' list.");
  Vmenubar_configuration = Qnil;
}

void
specifier_vars_of_menubar (void)
{
  DEFVAR_SPECIFIER ("menubar-visible-p", &Vmenubar_visible_p,
    "*Whether the menubar is visible.\n\
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,
			 0, 0);
}
