#include <config.h>
#include <signal.h>
#include <stdio.h>
#include "lisp.h"
#include "termhooks.h"
#include "keyboard.h"
#include "frame.h"
#include "window.h"
#include "blockinput.h"
#include "buffer.h"
#ifdef MSDOS
#include "msdos.h"
#endif
#ifdef HAVE_X_WINDOWS
#include "xterm.h"
#endif
#ifndef makedev
#include <sys/types.h>
#endif
#include "dispextern.h"
#ifdef HAVE_X_WINDOWS
#undef HAVE_MULTILINGUAL_MENU
#ifdef USE_X_TOOLKIT
#include "widget.h"
#include <X11/Xlib.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#ifdef USE_LUCID
#include <X11/Xaw/Paned.h>
#endif
#include "../lwlib/lwlib.h"
#else
#include "../oldXMenu/XMenu.h"
#endif
#endif
#define min(x,y) (((x) < (y)) ? (x) : (y))
#define max(x,y) (((x) > (y)) ? (x) : (y))
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
Lisp_Object Vmenu_updating_frame;
Lisp_Object Qdebug_on_next_call;
extern Lisp_Object Qmenu_bar;
extern Lisp_Object Qmouse_click, Qevent_kind;
extern Lisp_Object QCtoggle, QCradio;
extern Lisp_Object Voverriding_local_map;
extern Lisp_Object Voverriding_local_map_menu_flag;
extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
extern Lisp_Object Qmenu_bar_update_hook;
#ifdef USE_X_TOOLKIT
extern void set_frame_menubar ();
extern void process_expose_from_menu ();
extern XtAppContext Xt_app_con;
static Lisp_Object xdialog_show ();
void popup_get_selection ();
#endif
#ifdef USE_X_TOOLKIT
#define HAVE_BOXES 1
#endif
static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object));
static int update_frame_menubar P_ ((struct frame *));
static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
Lisp_Object, char **));
static void keymap_panes P_ ((Lisp_Object *, int, int));
static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
int, int));
static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object *,
int, int, int *));
static void list_of_panes P_ ((Lisp_Object));
static void list_of_items P_ ((Lisp_Object));
#define MENU_ITEMS_PANE_NAME 1
#define MENU_ITEMS_PANE_PREFIX 2
#define MENU_ITEMS_PANE_LENGTH 3
enum menu_item_idx
{
MENU_ITEMS_ITEM_NAME = 0,
MENU_ITEMS_ITEM_ENABLE,
MENU_ITEMS_ITEM_VALUE,
MENU_ITEMS_ITEM_EQUIV_KEY,
MENU_ITEMS_ITEM_DEFINITION,
MENU_ITEMS_ITEM_TYPE,
MENU_ITEMS_ITEM_SELECTED,
MENU_ITEMS_ITEM_HELP,
MENU_ITEMS_ITEM_LENGTH
};
static Lisp_Object menu_items;
static int menu_items_allocated;
static int menu_items_used;
static int menu_items_n_panes;
static int menu_items_submenu_depth;
int popup_activated_flag;
static int next_menubar_widget_id;
int pending_menu_activation;
#ifdef USE_X_TOOLKIT
static struct frame *
menubar_id_to_frame (id)
LWLIB_ID id;
{
Lisp_Object tail, frame;
FRAME_PTR f;
for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
{
frame = XCAR (tail);
if (!GC_FRAMEP (frame))
continue;
f = XFRAME (frame);
if (!FRAME_WINDOW_P (f))
continue;
if (f->output_data.x->id == id)
return f;
}
return 0;
}
#endif
static void
init_menu_items ()
{
if (NILP (menu_items))
{
menu_items_allocated = 60;
menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
}
menu_items_used = 0;
menu_items_n_panes = 0;
menu_items_submenu_depth = 0;
}
static void
finish_menu_items ()
{
}
static void
discard_menu_items ()
{
if (menu_items_allocated > 200)
{
menu_items = Qnil;
menu_items_allocated = 0;
}
}
static void
grow_menu_items ()
{
Lisp_Object old;
int old_size = menu_items_allocated;
old = menu_items;
menu_items_allocated *= 2;
menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
old_size * sizeof (Lisp_Object));
}
static void
push_submenu_start ()
{
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
menu_items_submenu_depth++;
}
static void
push_submenu_end ()
{
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
menu_items_submenu_depth--;
}
static void
push_left_right_boundary ()
{
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
}
static void
push_menu_pane (name, prefix_vec)
Lisp_Object name, prefix_vec;
{
if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
grow_menu_items ();
if (menu_items_submenu_depth == 0)
menu_items_n_panes++;
XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
XVECTOR (menu_items)->contents[menu_items_used++] = name;
XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
}
static void
push_menu_item (name, enable, key, def, equiv, type, selected, help)
Lisp_Object name, enable, key, def, equiv, type, selected, help;
{
if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
grow_menu_items ();
XVECTOR (menu_items)->contents[menu_items_used++] = name;
XVECTOR (menu_items)->contents[menu_items_used++] = enable;
XVECTOR (menu_items)->contents[menu_items_used++] = key;
XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
XVECTOR (menu_items)->contents[menu_items_used++] = def;
XVECTOR (menu_items)->contents[menu_items_used++] = type;
XVECTOR (menu_items)->contents[menu_items_used++] = selected;
XVECTOR (menu_items)->contents[menu_items_used++] = help;
}
static void
keymap_panes (keymaps, nmaps, notreal)
Lisp_Object *keymaps;
int nmaps;
int notreal;
{
int mapno;
init_menu_items ();
for (mapno = 0; mapno < nmaps; mapno++)
single_keymap_panes (keymaps[mapno],
map_prompt (keymaps[mapno]), Qnil, notreal, 10);
finish_menu_items ();
}
static void
single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
Lisp_Object keymap;
Lisp_Object pane_name;
Lisp_Object prefix;
int notreal;
int maxdepth;
{
Lisp_Object pending_maps = Qnil;
Lisp_Object tail, item;
struct gcpro gcpro1, gcpro2;
int notbuttons = 0;
if (maxdepth <= 0)
return;
push_menu_pane (pane_name, prefix);
#ifndef HAVE_BOXES
notbuttons = menu_items_used;
#endif
for (tail = keymap; CONSP (tail); tail = XCDR (tail))
{
GCPRO2 (keymap, pending_maps);
item = XCAR (tail);
if (CONSP (item))
single_menu_item (XCAR (item), XCDR (item),
&pending_maps, notreal, maxdepth, ¬buttons);
else if (VECTORP (item))
{
int len = XVECTOR (item)->size;
int c;
for (c = 0; c < len; c++)
{
Lisp_Object character;
XSETFASTINT (character, c);
single_menu_item (character, XVECTOR (item)->contents[c],
&pending_maps, notreal, maxdepth, ¬buttons);
}
}
UNGCPRO;
}
while (!NILP (pending_maps))
{
Lisp_Object elt, eltcdr, string;
elt = Fcar (pending_maps);
eltcdr = XCDR (elt);
string = XCAR (eltcdr);
single_keymap_panes (Fcar (elt), string,
XCDR (eltcdr), notreal, maxdepth - 1);
pending_maps = Fcdr (pending_maps);
}
}
static void
single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth,
notbuttons_ptr)
Lisp_Object key, item;
Lisp_Object *pending_maps_ptr;
int maxdepth, notreal;
int *notbuttons_ptr;
{
Lisp_Object map, item_string, enabled;
struct gcpro gcpro1, gcpro2;
int res;
GCPRO2 (key, item);
res = parse_menu_item (item, notreal, 0);
UNGCPRO;
if (!res)
return;
map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
if (notreal)
{
if (!NILP (map))
single_keymap_panes (map, Qnil, key, 1, maxdepth - 1);
return;
}
enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
if (!NILP (map) && XSTRING (item_string)->data[0] == '@')
{
if (!NILP (enabled))
*pending_maps_ptr = Fcons (Fcons (map, Fcons (item_string, key)),
*pending_maps_ptr);
return;
}
#ifndef HAVE_BOXES
{
Lisp_Object prefix = Qnil;
Lisp_Object type = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE];
if (!NILP (type))
{
Lisp_Object selected
= XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED];
if (*notbuttons_ptr)
{
int index = *notbuttons_ptr;
int submenu = 0;
Lisp_Object tem;
while (index < menu_items_used)
{
tem
= XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME];
if (NILP (tem))
{
index++;
submenu++;
}
else if (EQ (tem, Qlambda))
{
index++;
submenu--;
}
else if (EQ (tem, Qt))
index += 3;
else if (EQ (tem, Qquote))
index++;
else
{
if (!submenu && XSTRING (tem)->data[0] != '\0'
&& XSTRING (tem)->data[0] != '-')
XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME]
= concat2 (build_string (" "), tem);
index += MENU_ITEMS_ITEM_LENGTH;
}
}
*notbuttons_ptr = 0;
}
if (EQ (type, QCtoggle))
prefix = build_string (NILP (selected) ? "[ ] " : "[X] ");
else if (EQ (type, QCradio))
prefix = build_string (NILP (selected) ? "( ) " : "(*) ");
}
else if (!*notbuttons_ptr && XSTRING (item_string)->data[0] != '\0'
&& XSTRING (item_string)->data[0] != '-')
prefix = build_string (" ");
if (!NILP (prefix))
item_string = concat2 (prefix, item_string);
}
#endif
#ifndef USE_X_TOOLKIT
if (!NILP(map))
item_string = concat2 (item_string, build_string (" >"));
#endif
push_menu_item (item_string, enabled, key,
XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
#ifdef USE_X_TOOLKIT
if (! (NILP (map) || NILP (enabled)))
{
push_submenu_start ();
single_keymap_panes (map, Qnil, key, 0, maxdepth - 1);
push_submenu_end ();
}
#endif
}
static void
list_of_panes (menu)
Lisp_Object menu;
{
Lisp_Object tail;
init_menu_items ();
for (tail = menu; !NILP (tail); tail = Fcdr (tail))
{
Lisp_Object elt, pane_name, pane_data;
elt = Fcar (tail);
pane_name = Fcar (elt);
CHECK_STRING (pane_name, 0);
push_menu_pane (pane_name, Qnil);
pane_data = Fcdr (elt);
CHECK_CONS (pane_data, 0);
list_of_items (pane_data);
}
finish_menu_items ();
}
static void
list_of_items (pane)
Lisp_Object pane;
{
Lisp_Object tail, item, item1;
for (tail = pane; !NILP (tail); tail = Fcdr (tail))
{
item = Fcar (tail);
if (STRINGP (item))
push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
else if (NILP (item))
push_left_right_boundary ();
else
{
CHECK_CONS (item, 0);
item1 = Fcar (item);
CHECK_STRING (item1, 1);
push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil);
}
}
}
DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
"Pop up a deck-of-cards menu and return user's selection.\n\
POSITION is a position specification. This is either a mouse button event\n\
or a list ((XOFFSET YOFFSET) WINDOW)\n\
where XOFFSET and YOFFSET are positions in pixels from the top left\n\
corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
This controls the position of the center of the first line\n\
in the first pane of the menu, not the top left of the menu as a whole.\n\
If POSITION is t, it means to use the current mouse position.\n\
\n\
MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
The menu items come from key bindings that have a menu string as well as\n\
a definition; actually, the \"definition\" in such a key binding looks like\n\
\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\
the keymap as a top-level element.\n\n\
If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.\n\
Otherwise, REAL-DEFINITION should be a valid key binding definition.\n\
\n\
You can also use a list of keymaps as MENU.\n\
Then each keymap makes a separate pane.\n\
When MENU is a keymap or a list of keymaps, the return value\n\
is a list of events.\n\n\
\n\
Alternatively, you can specify a menu of multiple panes\n\
with a list of the form (TITLE PANE1 PANE2...),\n\
where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
Each ITEM is normally a cons cell (STRING . VALUE);\n\
but a string can appear as an item--that makes a nonselectable line\n\
in the menu.\n\
With this form of menu, the return value is VALUE from the chosen item.\n\
\n\
If POSITION is nil, don't display the menu at all, just precalculate the\n\
cached information about equivalent key sequences.")
(position, menu)
Lisp_Object position, menu;
{
Lisp_Object keymap, tem;
int xpos = 0, ypos = 0;
Lisp_Object title;
char *error_name;
Lisp_Object selection;
struct frame *f = NULL;
Lisp_Object x, y, window;
int keymaps = 0;
int for_click = 0;
struct gcpro gcpro1;
#ifdef HAVE_MENUS
if (! NILP (position))
{
check_x ();
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
|| EQ (XCAR (position), Qtool_bar))))
{
FRAME_PTR new_f = SELECTED_FRAME ();
Lisp_Object bar_window;
enum scroll_bar_part part;
unsigned long time;
if (mouse_position_hook)
(*mouse_position_hook) (&new_f, 1, &bar_window,
&part, &x, &y, &time);
if (new_f != 0)
XSETFRAME (window, new_f);
else
{
window = selected_window;
XSETFASTINT (x, 0);
XSETFASTINT (y, 0);
}
}
else
{
tem = Fcar (position);
if (CONSP (tem))
{
window = Fcar (Fcdr (position));
x = Fcar (tem);
y = Fcar (Fcdr (tem));
}
else
{
for_click = 1;
tem = Fcar (Fcdr (position));
window = Fcar (tem);
tem = Fcar (Fcdr (Fcdr (tem)));
x = Fcar (tem);
y = Fcdr (tem);
}
}
CHECK_NUMBER (x, 0);
CHECK_NUMBER (y, 0);
if (FRAMEP (window))
{
f = XFRAME (window);
xpos = 0;
ypos = 0;
}
else if (WINDOWP (window))
{
CHECK_LIVE_WINDOW (window, 0);
f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
xpos = (FONT_WIDTH (f->output_data.x->font)
* XFASTINT (XWINDOW (window)->left));
ypos = (f->output_data.x->line_height
* XFASTINT (XWINDOW (window)->top));
}
else
CHECK_WINDOW (window, 0);
xpos += XINT (x);
ypos += XINT (y);
XSETFRAME (Vmenu_updating_frame, f);
}
Vmenu_updating_frame = Qnil;
#endif
title = Qnil;
GCPRO1 (title);
keymap = get_keymap (menu, 0, 0);
if (CONSP (keymap))
{
Lisp_Object prompt;
keymap_panes (&menu, 1, NILP (position));
prompt = map_prompt (keymap);
if (NILP (title) && !NILP (prompt))
title = prompt;
if (!NILP (prompt) && menu_items_n_panes >= 0)
XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
keymaps = 1;
}
else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
{
int nmaps = XFASTINT (Flength (menu));
Lisp_Object *maps
= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
int i;
title = Qnil;
for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
{
Lisp_Object prompt;
maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
prompt = map_prompt (keymap);
if (NILP (title) && !NILP (prompt))
title = prompt;
}
keymap_panes (maps, nmaps, NILP (position));
if (!NILP (title) && menu_items_n_panes >= 0)
XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
keymaps = 1;
}
else
{
title = Fcar (menu);
CHECK_STRING (title, 1);
list_of_panes (Fcdr (menu));
keymaps = 0;
}
if (NILP (position))
{
discard_menu_items ();
UNGCPRO;
return Qnil;
}
#ifdef HAVE_MENUS
BLOCK_INPUT;
selection = xmenu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
UNBLOCK_INPUT;
discard_menu_items ();
UNGCPRO;
#endif
if (error_name) error (error_name);
return selection;
}
#ifdef HAVE_MENUS
DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
"Pop up a dialog box and return user's selection.\n\
POSITION specifies which frame to use.\n\
This is normally a mouse button event or a window or frame.\n\
If POSITION is t, it means to use the frame the mouse is on.\n\
The dialog box appears in the middle of the specified frame.\n\
\n\
CONTENTS specifies the alternatives to display in the dialog box.\n\
It is a list of the form (TITLE ITEM1 ITEM2...).\n\
Each ITEM is a cons cell (STRING . VALUE).\n\
The return value is VALUE from the chosen item.\n\n\
An ITEM may also be just a string--that makes a nonselectable item.\n\
An ITEM may also be nil--that means to put all preceding items\n\
on the left of the dialog box and all following items on the right.\n\
\(By default, approximately half appear on each side.)")
(position, contents)
Lisp_Object position, contents;
{
struct frame * f = NULL;
Lisp_Object window;
check_x ();
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
|| EQ (XCAR (position), Qtool_bar))))
{
#if 0
FRAME_PTR new_f = SELECTED_FRAME ();
Lisp_Object bar_window;
int part;
unsigned long time;
Lisp_Object x, y;
(*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
if (new_f != 0)
XSETFRAME (window, new_f);
else
window = selected_window;
#endif
window = selected_window;
}
else if (CONSP (position))
{
Lisp_Object tem;
tem = Fcar (position);
if (CONSP (tem))
window = Fcar (Fcdr (position));
else
{
tem = Fcar (Fcdr (position));
window = Fcar (tem);
}
}
else if (WINDOWP (position) || FRAMEP (position))
window = position;
else
window = Qnil;
if (FRAMEP (window))
f = XFRAME (window);
else if (WINDOWP (window))
{
CHECK_LIVE_WINDOW (window, 0);
f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
}
else
CHECK_WINDOW (window, 0);
#ifndef USE_X_TOOLKIT
{
Lisp_Object x, y, frame, newpos;
XSETFRAME (frame, f);
XSETINT (x, x_pixel_width (f) / 2);
XSETINT (y, x_pixel_height (f) / 2);
newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
return Fx_popup_menu (newpos,
Fcons (Fcar (contents), Fcons (contents, Qnil)));
}
#else
{
Lisp_Object title;
char *error_name;
Lisp_Object selection;
title = Fcar (contents);
CHECK_STRING (title, 1);
list_of_panes (Fcons (contents, Qnil));
BLOCK_INPUT;
selection = xdialog_show (f, 0, title, &error_name);
UNBLOCK_INPUT;
discard_menu_items ();
if (error_name) error (error_name);
return selection;
}
#endif
}
#ifdef USE_X_TOOLKIT
void
popup_get_selection (initial_event, dpyinfo, id)
XEvent *initial_event;
struct x_display_info *dpyinfo;
LWLIB_ID id;
{
XEvent event;
struct event_queue
{
XEvent event;
struct event_queue *next;
};
struct event_queue *queue = NULL;
struct event_queue *queue_tmp;
if (initial_event)
event = *initial_event;
else
XtAppNextEvent (Xt_app_con, &event);
while (1)
{
if (event.type == Expose)
process_expose_from_menu (event);
else if (event.type == ButtonRelease
&& dpyinfo->display == event.xbutton.display)
{
dpyinfo->grabbed &= ~(1 << event.xbutton.button);
popup_activated_flag = 0;
#ifdef USE_MOTIF
event.xbutton.button = 1;
#endif
}
else if (event.type == KeyPress
&& dpyinfo->display == event.xbutton.display)
{
KeySym keysym = XLookupKeysym (&event.xkey, 0);
if (!IsModifierKey (keysym))
{
popup_activated_flag = 0;
break;
}
}
else if (event.type == ButtonPress
&& event.xany.display == dpyinfo->display
&& x_any_window_to_frame (dpyinfo, event.xany.window))
{
popup_activated_flag = 0;
break;
}
if (event.type != Expose
&& !(event.type == ButtonRelease
&& dpyinfo->display == event.xbutton.display)
&& (event.xany.display != dpyinfo->display
|| x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
{
queue_tmp = (struct event_queue *) xmalloc (sizeof *queue_tmp);
queue_tmp->event = event;
queue_tmp->next = queue;
queue = queue_tmp;
}
else
XtDispatchEvent (&event);
if (!popup_activated ())
break;
XtAppNextEvent (Xt_app_con, &event);
}
while (queue != NULL)
{
queue_tmp = queue;
XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event);
queue = queue_tmp->next;
xfree ((char *)queue_tmp);
interrupt_input_pending = 1;
}
}
void
x_activate_menubar (f)
FRAME_PTR f;
{
if (!f->output_data.x->saved_menu_event->type)
return;
set_frame_menubar (f, 0, 1);
BLOCK_INPUT;
XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event);
UNBLOCK_INPUT;
#ifdef USE_MOTIF
if (f->output_data.x->saved_menu_event->type == ButtonRelease)
pending_menu_activation = 1;
#endif
f->output_data.x->saved_menu_event->type = 0;
}
int
popup_activated ()
{
return popup_activated_flag;
}
static void
popup_activate_callback (widget, id, client_data)
Widget widget;
LWLIB_ID id;
XtPointer client_data;
{
popup_activated_flag = 1;
}
static void
popup_deactivate_callback (widget, id, client_data)
Widget widget;
LWLIB_ID id;
XtPointer client_data;
{
popup_activated_flag = 0;
}
void
menu_highlight_callback (widget, id, call_data)
Widget widget;
LWLIB_ID id;
void *call_data;
{
widget_value *wv = (widget_value *) call_data;
struct frame *f;
Lisp_Object frame, help;
help = wv && wv->help ? build_string (wv->help) : Qnil;
f = menubar_id_to_frame (id);
if (f)
{
XSETFRAME (frame, f);
kbd_buffer_store_help_event (frame, help);
}
else
{
Widget frame_widget = XtParent (widget);
Lisp_Object tail;
for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
{
frame = XCAR (tail);
if (GC_FRAMEP (frame)
&& (f = XFRAME (frame),
FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
break;
}
show_help_echo (help, Qnil, Qnil, Qnil, 1);
}
}
static void
menubar_selection_callback (widget, id, client_data)
Widget widget;
LWLIB_ID id;
XtPointer client_data;
{
Lisp_Object prefix, entry;
FRAME_PTR f = menubar_id_to_frame (id);
Lisp_Object vector;
Lisp_Object *subprefix_stack;
int submenu_depth = 0;
int i;
if (!f)
return;
entry = Qnil;
subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
vector = f->menu_bar_vector;
prefix = Qnil;
i = 0;
while (i < f->menu_bar_items_used)
{
if (EQ (XVECTOR (vector)->contents[i], Qnil))
{
subprefix_stack[submenu_depth++] = prefix;
prefix = entry;
i++;
}
else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
{
prefix = subprefix_stack[--submenu_depth];
i++;
}
else if (EQ (XVECTOR (vector)->contents[i], Qt))
{
prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
if ((int) (EMACS_INT) client_data == i)
{
int j;
struct input_event buf;
Lisp_Object frame;
XSETFRAME (frame, f);
buf.kind = MENU_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = frame;
kbd_buffer_store_event (&buf);
for (j = 0; j < submenu_depth; j++)
if (!NILP (subprefix_stack[j]))
{
buf.kind = MENU_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = subprefix_stack[j];
kbd_buffer_store_event (&buf);
}
if (!NILP (prefix))
{
buf.kind = MENU_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = prefix;
kbd_buffer_store_event (&buf);
}
buf.kind = MENU_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = entry;
kbd_buffer_store_event (&buf);
return;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
}
widget_value *
xmalloc_widget_value ()
{
widget_value *value;
BLOCK_INPUT;
value = malloc_widget_value ();
UNBLOCK_INPUT;
return value;
}
void
free_menubar_widget_value_tree (wv)
widget_value *wv;
{
if (! wv) return;
wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
if (wv->contents && (wv->contents != (widget_value*)1))
{
free_menubar_widget_value_tree (wv->contents);
wv->contents = (widget_value *) 0xDEADBEEF;
}
if (wv->next)
{
free_menubar_widget_value_tree (wv->next);
wv->next = (widget_value *) 0xDEADBEEF;
}
BLOCK_INPUT;
free_widget_value (wv);
UNBLOCK_INPUT;
}
static widget_value *
single_submenu (item_key, item_name, maps)
Lisp_Object item_key, item_name, maps;
{
widget_value *wv, *prev_wv, *save_wv, *first_wv;
int i;
int submenu_depth = 0;
Lisp_Object length;
int len;
Lisp_Object *mapvec;
widget_value **submenu_stack;
int previous_items = menu_items_used;
int top_level_items = 0;
length = Flength (maps);
len = XINT (length);
mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
for (i = 0; i < len; i++)
{
mapvec[i] = Fcar (maps);
maps = Fcdr (maps);
}
menu_items_n_panes = 0;
for (i = 0; i < len; i++)
{
if (SYMBOLP (mapvec[i])
|| (CONSP (mapvec[i]) && !KEYMAPP (mapvec[i])))
{
top_level_items = 1;
push_menu_pane (Qnil, Qnil);
push_menu_item (item_name, Qt, item_key, mapvec[i],
Qnil, Qnil, Qnil, Qnil);
}
else
single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
}
submenu_stack
= (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
wv = xmalloc_widget_value ();
wv->name = "menu";
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
first_wv = wv;
save_wv = 0;
prev_wv = 0;
i = previous_items;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
{
submenu_stack[submenu_depth++] = save_wv;
save_wv = prev_wv;
prev_wv = 0;
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
{
prev_wv = save_wv;
save_wv = submenu_stack[--submenu_depth];
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
&& submenu_depth != 0)
i += MENU_ITEMS_PANE_LENGTH;
else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
i += 1;
else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
Lisp_Object pane_name, prefix;
char *pane_string;
pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
pane_name = string_make_unibyte (pane_name);
AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
}
#endif
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
if (menu_items_n_panes == 1)
pane_string = "";
if (strcmp (pane_string, ""))
{
wv = xmalloc_widget_value ();
if (save_wv)
save_wv->next = wv;
else
first_wv->contents = wv;
wv->name = pane_string;
if (!NILP (prefix) && wv->name[0] == '@')
wv->name++;
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
}
save_wv = wv;
prev_wv = 0;
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
Lisp_Object item_name, enable, descrip, def, type, selected;
Lisp_Object help;
item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
#ifndef HAVE_MULTILINGUAL_MENU
if (STRING_MULTIBYTE (item_name))
{
item_name = string_make_unibyte (item_name);
AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
}
if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
{
descrip = string_make_unibyte (descrip);
AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
}
#endif
wv = xmalloc_widget_value ();
if (prev_wv)
prev_wv->next = wv;
else
save_wv->contents = wv;
wv->name = (char *) XSTRING (item_name)->data;
if (!NILP (descrip))
wv->key = (char *) XSTRING (descrip)->data;
wv->value = 0;
wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
wv->enabled = !NILP (enable);
if (NILP (type))
wv->button_type = BUTTON_TYPE_NONE;
else if (EQ (type, QCradio))
wv->button_type = BUTTON_TYPE_RADIO;
else if (EQ (type, QCtoggle))
wv->button_type = BUTTON_TYPE_TOGGLE;
else
abort ();
wv->selected = !NILP (selected);
if (STRINGP (help))
wv->help = XSTRING (help)->data;
prev_wv = wv;
i += MENU_ITEMS_ITEM_LENGTH;
}
}
if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
{
wv = first_wv->contents;
free_widget_value (first_wv);
return wv;
}
return first_wv;
}
static int
update_frame_menubar (f)
FRAME_PTR f;
{
struct x_output *x = f->output_data.x;
int columns, rows;
if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
return 0;
BLOCK_INPUT;
columns = f->width;
rows = f->height;
lw_refigure_widget (x->column_widget, False);
XtUnmanageChild (x->edit_widget);
XtManageChild (x->menubar_widget);
XtMapWidget (x->menubar_widget);
XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
XtManageChild (x->edit_widget);
lw_refigure_widget (x->column_widget, True);
EmacsFrameSetCharSize (x->edit_widget, columns, rows);
UNBLOCK_INPUT;
return 1;
}
void
set_frame_menubar (f, first_time, deep_p)
FRAME_PTR f;
int first_time;
int deep_p;
{
Widget menubar_widget = f->output_data.x->menubar_widget;
Lisp_Object items;
widget_value *wv, *first_wv, *prev_wv = 0;
int i;
LWLIB_ID id;
XSETFRAME (Vmenu_updating_frame, f);
if (f->output_data.x->id == 0)
f->output_data.x->id = next_menubar_widget_id++;
id = f->output_data.x->id;
if (! menubar_widget)
deep_p = 1;
else if (pending_menu_activation && !deep_p)
deep_p = 1;
else if (!f->output_data.x->saved_menu_event && !deep_p)
{
deep_p = 1;
f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
f->output_data.x->saved_menu_event->type = 0;
}
wv = xmalloc_widget_value ();
wv->name = "menubar";
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
first_wv = wv;
if (deep_p)
{
struct buffer *prev = current_buffer;
Lisp_Object buffer;
int specpdl_count = specpdl_ptr - specpdl;
int previous_menu_items_used = f->menu_bar_items_used;
Lisp_Object *previous_items
= (Lisp_Object *) alloca (previous_menu_items_used
* sizeof (Lisp_Object));
if (! menubar_widget)
previous_menu_items_used = 0;
buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
specbind (Qinhibit_quit, Qt);
specbind (Qdebug_on_next_call, Qnil);
record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
if (NILP (Voverriding_local_map_menu_flag))
{
specbind (Qoverriding_terminal_local_map, Qnil);
specbind (Qoverriding_local_map, Qnil);
}
set_buffer_internal_1 (XBUFFER (buffer));
safe_run_hooks (Qactivate_menubar_hook);
if (! NILP (Vlucid_menu_bar_dirty_flag))
call0 (Qrecompute_lucid_menubar);
safe_run_hooks (Qmenu_bar_update_hook);
FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
items = FRAME_MENU_BAR_ITEMS (f);
inhibit_garbage_collection ();
if (previous_menu_items_used)
bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
previous_menu_items_used * sizeof (Lisp_Object));
menu_items = f->menu_bar_vector;
menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
init_menu_items ();
for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object key, string, maps;
key = XVECTOR (items)->contents[i];
string = XVECTOR (items)->contents[i + 1];
maps = XVECTOR (items)->contents[i + 2];
if (NILP (string))
break;
wv = single_submenu (key, string, maps);
if (prev_wv)
prev_wv->next = wv;
else
first_wv->contents = wv;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
prev_wv = wv;
}
finish_menu_items ();
set_buffer_internal_1 (prev);
unbind_to (specpdl_count, Qnil);
for (i = 0; i < previous_menu_items_used; i++)
if (menu_items_used == i
|| (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
break;
if (i == menu_items_used && i == previous_menu_items_used && i != 0)
{
free_menubar_widget_value_tree (first_wv);
menu_items = Qnil;
return;
}
wv = first_wv->contents;
for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object string;
string = XVECTOR (items)->contents[i + 1];
if (NILP (string))
break;
wv->name = (char *) XSTRING (string)->data;
wv = wv->next;
}
f->menu_bar_vector = menu_items;
f->menu_bar_items_used = menu_items_used;
menu_items = Qnil;
}
else
{
items = FRAME_MENU_BAR_ITEMS (f);
for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object string;
string = XVECTOR (items)->contents[i + 1];
if (NILP (string))
break;
wv = xmalloc_widget_value ();
wv->name = (char *) XSTRING (string)->data;
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
wv->call_data = (void *) (EMACS_INT) (-1);
if (prev_wv)
prev_wv->next = wv;
else
first_wv->contents = wv;
prev_wv = wv;
}
f->menu_bar_items_used = 0;
}
BLOCK_INPUT;
if (menubar_widget)
{
lw_allow_resizing (f->output_data.x->widget, False);
lw_modify_all_widgets (id, first_wv, deep_p);
lw_allow_resizing (f->output_data.x->widget, True);
}
else
{
menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv,
f->output_data.x->column_widget,
0,
popup_activate_callback,
menubar_selection_callback,
popup_deactivate_callback,
menu_highlight_callback);
f->output_data.x->menubar_widget = menubar_widget;
}
{
int menubar_size
= (f->output_data.x->menubar_widget
? (f->output_data.x->menubar_widget->core.height
+ f->output_data.x->menubar_widget->core.border_width)
: 0);
#if 0
#ifdef USE_LUCID
if (FRAME_EXTERNAL_MENU_BAR (f))
{
Dimension ibw = 0;
XtVaGetValues (f->output_data.x->column_widget,
XtNinternalBorderWidth, &ibw, NULL);
menubar_size += ibw;
}
#endif
#endif
f->output_data.x->menubar_height = menubar_size;
}
free_menubar_widget_value_tree (first_wv);
update_frame_menubar (f);
UNBLOCK_INPUT;
}
void
initialize_frame_menubar (f)
FRAME_PTR f;
{
FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
set_frame_menubar (f, 1, 1);
}
void
free_frame_menubar (f)
FRAME_PTR f;
{
Widget menubar_widget;
menubar_widget = f->output_data.x->menubar_widget;
f->output_data.x->menubar_height = 0;
if (menubar_widget)
{
#ifdef USE_MOTIF
Position x0, y0, x1, y1;
#endif
BLOCK_INPUT;
#ifdef USE_MOTIF
if (f->output_data.x->widget)
XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL);
#endif
lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
f->output_data.x->menubar_widget = NULL;
#ifdef USE_MOTIF
if (f->output_data.x->widget)
{
XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL);
if (x1 == 0 && y1 == 0)
XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
}
#endif
UNBLOCK_INPUT;
}
}
#endif
#ifdef USE_X_TOOLKIT
LWLIB_ID widget_id_tick;
static Lisp_Object *volatile menu_item_selection;
static void
popup_selection_callback (widget, id, client_data)
Widget widget;
LWLIB_ID id;
XtPointer client_data;
{
menu_item_selection = (Lisp_Object *) client_data;
}
static Lisp_Object
xmenu_show (f, x, y, for_click, keymaps, title, error)
FRAME_PTR f;
int x;
int y;
int for_click;
int keymaps;
Lisp_Object title;
char **error;
{
int i;
LWLIB_ID menu_id;
Widget menu;
Arg av[2];
int ac = 0;
widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
widget_value **submenu_stack
= (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
Lisp_Object *subprefix_stack
= (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
int submenu_depth = 0;
XButtonPressedEvent dummy;
int first_pane;
*error = NULL;
if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
{
*error = "Empty menu";
return Qnil;
}
wv = xmalloc_widget_value ();
wv->name = "menu";
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
first_wv = wv;
first_pane = 1;
i = 0;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
{
submenu_stack[submenu_depth++] = save_wv;
save_wv = prev_wv;
prev_wv = 0;
first_pane = 1;
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
{
prev_wv = save_wv;
save_wv = submenu_stack[--submenu_depth];
first_pane = 0;
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
&& submenu_depth != 0)
i += MENU_ITEMS_PANE_LENGTH;
else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
i += 1;
else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
Lisp_Object pane_name, prefix;
char *pane_string;
pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
pane_name = string_make_unibyte (pane_name);
AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
}
#endif
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
if (menu_items_n_panes == 1)
pane_string = "";
if (!keymaps && strcmp (pane_string, ""))
{
wv = xmalloc_widget_value ();
if (save_wv)
save_wv->next = wv;
else
first_wv->contents = wv;
wv->name = pane_string;
if (keymaps && !NILP (prefix))
wv->name++;
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
save_wv = wv;
prev_wv = 0;
}
else if (first_pane)
{
save_wv = wv;
prev_wv = 0;
}
first_pane = 0;
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
Lisp_Object item_name, enable, descrip, def, type, selected, help;
item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
{
item_name = string_make_unibyte (item_name);
AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
}
if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
{
descrip = string_make_unibyte (descrip);
AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
}
#endif
wv = xmalloc_widget_value ();
if (prev_wv)
prev_wv->next = wv;
else
save_wv->contents = wv;
wv->name = (char *) XSTRING (item_name)->data;
if (!NILP (descrip))
wv->key = (char *) XSTRING (descrip)->data;
wv->value = 0;
wv->call_data
= (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
wv->enabled = !NILP (enable);
if (NILP (type))
wv->button_type = BUTTON_TYPE_NONE;
else if (EQ (type, QCtoggle))
wv->button_type = BUTTON_TYPE_TOGGLE;
else if (EQ (type, QCradio))
wv->button_type = BUTTON_TYPE_RADIO;
else
abort ();
wv->selected = !NILP (selected);
if (STRINGP (help))
wv->help = XSTRING (help)->data;
prev_wv = wv;
i += MENU_ITEMS_ITEM_LENGTH;
}
}
if (!NILP (title))
{
widget_value *wv_title = xmalloc_widget_value ();
widget_value *wv_sep1 = xmalloc_widget_value ();
widget_value *wv_sep2 = xmalloc_widget_value ();
wv_sep2->name = "--";
wv_sep2->next = first_wv->contents;
wv_sep1->name = "--";
wv_sep1->next = wv_sep2;
#ifndef HAVE_MULTILINGUAL_MENU
if (STRING_MULTIBYTE (title))
title = string_make_unibyte (title);
#endif
wv_title->name = (char *) XSTRING (title)->data;
wv_title->enabled = True;
wv_title->button_type = BUTTON_TYPE_NONE;
wv_title->next = wv_sep1;
first_wv->contents = wv_title;
}
menu_id = widget_id_tick++;
menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
f->output_data.x->widget, 1, 0,
popup_selection_callback,
popup_deactivate_callback,
menu_highlight_callback);
{
Window child;
int win_x = 0, win_y = 0;
if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
{
BLOCK_INPUT;
XTranslateCoordinates (FRAME_X_DISPLAY (f),
f->output_data.x->window_desc,
f->output_data.x->parent_desc,
0, 0, &win_x, &win_y,
&child);
UNBLOCK_INPUT;
x += win_x;
y += win_y;
}
}
x += f->output_data.x->left_pos;
y += f->output_data.x->top_pos;
dummy.type = ButtonPress;
dummy.serial = 0;
dummy.send_event = 0;
dummy.display = FRAME_X_DISPLAY (f);
dummy.time = CurrentTime;
dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
dummy.window = dummy.root;
dummy.subwindow = dummy.root;
dummy.x_root = x;
dummy.y_root = y;
dummy.x = x;
dummy.y = y;
dummy.state = (FRAME_X_DISPLAY_INFO (f)->grabbed >> 1) * Button1Mask;
dummy.button = 0;
for (i = 0; i < 5; i++)
if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
dummy.button = i;
XtSetArg (av[ac], XtNgeometry, 0); ac++;
XtSetValues (menu, av, ac);
free_menubar_widget_value_tree (first_wv);
menu_item_selection = 0;
lw_popup_menu (menu, (XEvent *) &dummy);
popup_activated_flag = 1;
popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id);
lw_destroy_all_widgets (menu_id);
if (menu_item_selection != 0)
{
Lisp_Object prefix, entry;
prefix = entry = Qnil;
i = 0;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
{
subprefix_stack[submenu_depth++] = prefix;
prefix = entry;
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
{
prefix = subprefix_stack[--submenu_depth];
i++;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
prefix
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
i += MENU_ITEMS_PANE_LENGTH;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
i += 1;
else
{
entry
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
{
if (keymaps != 0)
{
int j;
entry = Fcons (entry, Qnil);
if (!NILP (prefix))
entry = Fcons (prefix, entry);
for (j = submenu_depth - 1; j >= 0; j--)
if (!NILP (subprefix_stack[j]))
entry = Fcons (subprefix_stack[j], entry);
}
return entry;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
}
return Qnil;
}
static void
dialog_selection_callback (widget, id, client_data)
Widget widget;
LWLIB_ID id;
XtPointer client_data;
{
if ((int) (EMACS_INT) client_data != -1)
menu_item_selection = (Lisp_Object *) client_data;
BLOCK_INPUT;
lw_destroy_all_widgets (id);
UNBLOCK_INPUT;
popup_activated_flag = 0;
}
static char * button_names [] = {
"button1", "button2", "button3", "button4", "button5",
"button6", "button7", "button8", "button9", "button10" };
static Lisp_Object
xdialog_show (f, keymaps, title, error)
FRAME_PTR f;
int keymaps;
Lisp_Object title;
char **error;
{
int i, nb_buttons=0;
LWLIB_ID dialog_id;
Widget menu;
char dialog_name[6];
widget_value *wv, *first_wv = 0, *prev_wv = 0;
int left_count = 0;
int boundary_seen = 0;
*error = NULL;
if (menu_items_n_panes > 1)
{
*error = "Multiple panes in dialog box";
return Qnil;
}
{
Lisp_Object pane_name, prefix;
char *pane_string;
pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
prev_wv = xmalloc_widget_value ();
prev_wv->value = pane_string;
if (keymaps && !NILP (prefix))
prev_wv->name++;
prev_wv->enabled = 1;
prev_wv->name = "message";
first_wv = prev_wv;
i = MENU_ITEMS_PANE_LENGTH;
while (i < menu_items_used)
{
Lisp_Object item_name, enable, descrip;
item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
descrip
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
if (NILP (item_name))
{
free_menubar_widget_value_tree (first_wv);
*error = "Submenu in dialog items";
return Qnil;
}
if (EQ (item_name, Qquote))
{
boundary_seen = 1;
i++;
continue;
}
if (nb_buttons >= 9)
{
free_menubar_widget_value_tree (first_wv);
*error = "Too many dialog items";
return Qnil;
}
wv = xmalloc_widget_value ();
prev_wv->next = wv;
wv->name = (char *) button_names[nb_buttons];
if (!NILP (descrip))
wv->key = (char *) XSTRING (descrip)->data;
wv->value = (char *) XSTRING (item_name)->data;
wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
wv->enabled = !NILP (enable);
prev_wv = wv;
if (! boundary_seen)
left_count++;
nb_buttons++;
i += MENU_ITEMS_ITEM_LENGTH;
}
if (! boundary_seen)
left_count = nb_buttons - nb_buttons / 2;
wv = xmalloc_widget_value ();
wv->name = dialog_name;
dialog_name[0] = 'Q';
dialog_name[1] = '0' + nb_buttons;
dialog_name[2] = 'B';
dialog_name[3] = 'R';
dialog_name[4] = '0' + nb_buttons - left_count;
dialog_name[5] = 0;
wv->contents = first_wv;
first_wv = wv;
}
dialog_id = widget_id_tick++;
menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
f->output_data.x->widget, 1, 0,
dialog_selection_callback, 0, 0);
lw_modify_all_widgets (dialog_id, first_wv->contents, True);
free_menubar_widget_value_tree (first_wv);
menu_item_selection = 0;
lw_pop_up_all_widgets (dialog_id);
popup_activated_flag = 1;
popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
lw_destroy_all_widgets (dialog_id);
if (menu_item_selection != 0)
{
Lisp_Object prefix;
prefix = Qnil;
i = 0;
while (i < menu_items_used)
{
Lisp_Object entry;
if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
prefix
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
i += MENU_ITEMS_PANE_LENGTH;
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
{
++i;
}
else
{
entry
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
{
if (keymaps != 0)
{
entry = Fcons (entry, Qnil);
if (!NILP (prefix))
entry = Fcons (prefix, entry);
}
return entry;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
}
return Qnil;
}
#else
static struct frame *menu_help_frame;
static void
menu_help_callback (help_string, pane, item)
char *help_string;
int pane, item;
{
extern Lisp_Object Qmenu_item;
Lisp_Object *first_item;
Lisp_Object pane_name;
Lisp_Object menu_object;
first_item = XVECTOR (menu_items)->contents;
if (EQ (first_item[0], Qt))
pane_name = first_item[MENU_ITEMS_PANE_NAME];
else if (EQ (first_item[0], Qquote))
pane_name = build_string ("");
else
pane_name = first_item[MENU_ITEMS_ITEM_NAME];
menu_object = Fcons (Qmenu_item,
Fcons (pane_name,
Fcons (make_number (pane), Qnil)));
show_help_echo (help_string ? build_string (help_string) : Qnil,
Qnil, menu_object, make_number (item), 1);
}
static Lisp_Object
xmenu_show (f, x, y, for_click, keymaps, title, error)
FRAME_PTR f;
int x, y;
int for_click;
int keymaps;
Lisp_Object title;
char **error;
{
Window root;
XMenu *menu;
int pane, selidx, lpane, status;
Lisp_Object entry, pane_prefix;
char *datap;
int ulx, uly, width, height;
int dispwidth, dispheight;
int i, j;
int maxwidth;
int dummy_int;
unsigned int dummy_uint;
*error = 0;
if (menu_items_n_panes == 0)
return Qnil;
if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
{
*error = "Empty menu";
return Qnil;
}
XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
&dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
&dummy_uint, &dummy_uint);
menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
if (menu == NULL)
{
*error = "Can't create menu";
return Qnil;
}
#ifdef HAVE_X_WINDOWS
{
Window child;
int win_x = 0, win_y = 0;
if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
{
BLOCK_INPUT;
XTranslateCoordinates (FRAME_X_DISPLAY (f),
f->output_data.x->window_desc,
f->output_data.x->parent_desc,
0, 0, &win_x, &win_y,
&child);
UNBLOCK_INPUT;
x += win_x;
y += win_y;
}
}
#endif
x += f->output_data.x->left_pos;
y += f->output_data.x->top_pos;
i = 0;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
Lisp_Object pane_name, prefix;
char *pane_string;
pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
if (keymaps && !NILP (prefix))
pane_string++;
lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
if (lpane == XM_FAILURE)
{
XMenuDestroy (FRAME_X_DISPLAY (f), menu);
*error = "Can't create pane";
return Qnil;
}
i += MENU_ITEMS_PANE_LENGTH;
maxwidth = 0;
j = i;
while (j < menu_items_used)
{
Lisp_Object item;
item = XVECTOR (menu_items)->contents[j];
if (EQ (item, Qt))
break;
if (NILP (item))
{
j++;
continue;
}
width = STRING_BYTES (XSTRING (item));
if (width > maxwidth)
maxwidth = width;
j += MENU_ITEMS_ITEM_LENGTH;
}
}
else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
i += 1;
else
{
Lisp_Object item_name, enable, descrip, help;
unsigned char *item_data;
char *help_string;
item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
descrip
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
help_string = STRINGP (help) ? XSTRING (help)->data : NULL;
if (!NILP (descrip))
{
int gap = maxwidth - STRING_BYTES (XSTRING (item_name));
#ifdef C_ALLOCA
Lisp_Object spacer;
spacer = Fmake_string (make_number (gap), make_number (' '));
item_name = concat2 (item_name, spacer);
item_name = concat2 (item_name, descrip);
item_data = XSTRING (item_name)->data;
#else
item_data
= (unsigned char *) alloca (maxwidth
+ STRING_BYTES (XSTRING (descrip)) + 1);
bcopy (XSTRING (item_name)->data, item_data,
STRING_BYTES (XSTRING (item_name)));
for (j = XSTRING (item_name)->size; j < maxwidth; j++)
item_data[j] = ' ';
bcopy (XSTRING (descrip)->data, item_data + j,
STRING_BYTES (XSTRING (descrip)));
item_data[j + STRING_BYTES (XSTRING (descrip))] = 0;
#endif
}
else
item_data = XSTRING (item_name)->data;
if (XMenuAddSelection (FRAME_X_DISPLAY (f),
menu, lpane, 0, item_data,
!NILP (enable), help_string)
== XM_FAILURE)
{
XMenuDestroy (FRAME_X_DISPLAY (f), menu);
*error = "Can't add selection to menu";
return Qnil;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
XMenuRecompute (FRAME_X_DISPLAY (f), menu);
dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
x = min (x, dispwidth);
y = min (y, dispheight);
x = max (x, 1);
y = max (y, 1);
XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
&ulx, &uly, &width, &height);
if (ulx+width > dispwidth)
{
x -= (ulx + width) - dispwidth;
ulx = dispwidth - width;
}
if (uly+height > dispheight)
{
y -= (uly + height) - dispheight;
uly = dispheight - height;
}
if (ulx < 0) x -= ulx;
if (uly < 0) y -= uly;
XMenuSetAEQ (menu, TRUE);
XMenuSetFreeze (menu, TRUE);
pane = selidx = 0;
menu_help_frame = f;
status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
x, y, ButtonReleaseMask, &datap,
menu_help_callback);
#ifdef HAVE_X_WINDOWS
x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
#endif
switch (status)
{
case XM_SUCCESS:
#ifdef XDEBUG
fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
#endif
i = 0;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
if (pane == 0)
pane_prefix
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
pane--;
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
if (pane == -1)
{
if (selidx == 0)
{
entry
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
if (keymaps != 0)
{
entry = Fcons (entry, Qnil);
if (!NILP (pane_prefix))
entry = Fcons (pane_prefix, entry);
}
break;
}
selidx--;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
break;
case XM_FAILURE:
*error = "Can't activate menu";
case XM_IA_SELECT:
case XM_NO_SELECT:
entry = Qnil;
break;
}
XMenuDestroy (FRAME_X_DISPLAY (f), menu);
#ifdef HAVE_X_WINDOWS
FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
#endif
return entry;
}
#endif
#endif
void
syms_of_xmenu ()
{
staticpro (&menu_items);
menu_items = Qnil;
Qdebug_on_next_call = intern ("debug-on-next-call");
staticpro (&Qdebug_on_next_call);
DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
"Frame for which we are updating a menu.\n\
The enable predicate for a menu command should check this variable.");
Vmenu_updating_frame = Qnil;
#ifdef USE_X_TOOLKIT
widget_id_tick = (1<<16);
next_menubar_widget_id = 1;
#endif
defsubr (&Sx_popup_menu);
#ifdef HAVE_MENUS
defsubr (&Sx_popup_dialog);
#endif
}