A single vector slot containing lambda indicates the end of a submenu.
The submenu follows a menu item which is the way to reach the submenu.
+ A single vector slot containing quote indicates that the
+ following items should appear on the right of a dialog box.
+
Using a Lisp vector to hold this information while we decode it
takes care of protecting all the data from GC. */
menu_items_submenu_depth--;
}
+/* Indicate boundary between left and right. */
+
+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;
+}
+
/* Start a new menu pane in menu_items..
NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
{
item = Fcar (tail);
if (STRINGP (item))
- push_menu_item (item, Qnil, Qnil);
+ push_menu_item (item, Qnil, Qnil, Qnil);
+ else if (NILP (item))
+ push_left_right_boundary ();
else
{
CHECK_CONS (item, 0);
}
}
\f
-DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
+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 characters from the top left\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\
f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
- ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
+ ypos = (f->display.x->line_height * XWINDOW (window)->top);
}
else
/* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
return selection;
}
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 1, 2, 0,
+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 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 characters 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\
+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\
-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\
-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\
-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;
+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;
{
-#ifndef USE_X_TOOLKIT
- return Fx_popup_menu (position, menu);
-#else
- int number_of_panes, panes;
- Lisp_Object keymap, tem;
- int xpos, ypos;
- Lisp_Object title;
- char *error_name;
- Lisp_Object selection;
- int i, j;
FRAME_PTR f;
- Lisp_Object x, y, window;
- int keymaps = 0;
- int menubarp = 0;
- struct gcpro gcpro1;
+ Lisp_Object window;
check_x ();
- if (! NILP (position))
+ /* Decode the first argument: find the window or frame to use. */
+ if (EQ (position, Qt))
{
+#if 0 /* Using the frame the mouse is on may not be right. */
+ /* Use the mouse's current position. */
+ FRAME_PTR new_f = 0;
+ Lisp_Object bar_window;
+ int part;
+ unsigned long time;
+ Lisp_Object x, y;
+
+ (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+
+ if (new_f != 0)
+ XSET (window, Lisp_Frame, new_f);
+ else
+ window = selected_window;
+#endif
/* Decode the first argument: find the window and the coordinates. */
if (EQ (position, Qt))
+ window = selected_window;
+ }
+ else if (CONSP (position))
+ {
+ Lisp_Object tem;
+ tem = Fcar (position);
+ if (XTYPE (tem) == Lisp_Cons)
+ window = Fcar (Fcdr (position));
+ else
{
- /* Use the mouse's current position. */
- FRAME_PTR new_f = 0;
- Lisp_Object bar_window;
- int part;
- unsigned long time;
-
- (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
-
- if (new_f != 0)
- XSET (window, Lisp_Frame, new_f);
- else
- {
- window = selected_window;
- XFASTINT (x) = 0;
- XFASTINT (y) = 0;
- }
- }
-
- CHECK_NUMBER (x, 0);
- CHECK_NUMBER (y, 0);
-
- /* Decode where to put the menu. */
-
- if (XTYPE (window) == Lisp_Frame)
- {
- f = XFRAME (window);
-
- xpos = 0;
- ypos = 0;
- }
- else if (XTYPE (window) == Lisp_Window)
- {
- CHECK_LIVE_WINDOW (window, 0);
- f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-
- xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
- ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
+ tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
+ window = Fcar (tem); /* POSN_WINDOW (tem) */
}
- else
- /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
- but I don't want to make one now. */
- CHECK_WINDOW (window, 0);
-
- xpos += XINT (x);
- ypos += XINT (y);
}
+ else if (WINDOWP (position) || FRAMEP (position))
+ window = position;
- title = Qnil;
- GCPRO1 (title);
+ /* Decode where to put the menu. */
- /* Decode the dialog items from what was specified. */
+ if (XTYPE (window) == Lisp_Frame)
+ f = XFRAME (window);
+ else if (XTYPE (window) == Lisp_Window)
{
- /* We were given an old-fashioned menu. */
- title = Fcar (menu);
- CHECK_STRING (title, 1);
-
- list_of_panes (Fcdr (menu));
-
- keymaps = 0;
- }
-
- if (NILP (position))
- {
- discard_menu_items ();
- UNGCPRO;
- return Qnil;
+ CHECK_LIVE_WINDOW (window, 0);
+ f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
}
+ else
+ /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
+ but I don't want to make one now. */
+ CHECK_WINDOW (window, 0);
- /* Display them in a dialog box. */
- BLOCK_INPUT;
+#ifndef USE_X_TOOLKIT
+ /* Display a menu with these alternatives
+ in the middle of frame F. */
+ {
+ Lisp_Object x, y, frame, newpos;
+ XSET (frame, Lisp_Frame, f);
+ XSET (x, Lisp_Int, x_pixel_width (f) / 2);
+ XSET (y, Lisp_Int, 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;
- selection = xdialog_show (f, xpos, ypos, menubarp,
- keymaps, title, &error_name);
- UNBLOCK_INPUT;
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title, 1);
- discard_menu_items ();
+ list_of_panes (Fcons (contents, Qnil));
- UNGCPRO;
+ /* Display them in a dialog box. */
+ BLOCK_INPUT;
+ selection = xdialog_show (f, 0, 0, title, &error_name);
+ UNBLOCK_INPUT;
- if (error_name) error (error_name);
- return selection;
+ discard_menu_items ();
+
+ if (error_name) error (error_name);
+ return selection;
+ }
#endif
}
\f
dummy.x = x;
dummy.y = y;
- XtDispatchEvent (&dummy);
+ XtDispatchEvent ((XEvent *) &dummy);
}
static int
extern void EmacsFrameSetCharSize ();
static void
-update_one_frame_psheets (f)
+update_frame_menubar (f)
FRAME_PTR f;
{
struct x_display *x = f->display.x;
}
void
-set_frame_menubar (f)
+set_frame_menubar (f, first_time)
FRAME_PTR f;
+ int first_time;
{
Widget menubar_widget = f->display.x->menubar_widget;
int id = (int) f;
wv->enabled = 1;
save_wv = first_wv = wv;
- items = FRAME_MENU_BAR_ITEMS (f);
+ if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
+ items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
for (i = 0; i < XVECTOR (items)->size; i += 3)
{
prev_wv->next = wv;
else
save_wv->contents = wv;
- wv->name = XSTRING (string)->data;
+ wv->name = (char *) XSTRING (string)->data;
wv->value = 0;
wv->enabled = 1;
prev_wv = wv;
free_menubar_widget_value_tree (first_wv);
- update_one_frame_psheets (f);
+ /* Don't update the menubar the first time it is created via x_window. */
+ if (!first_time)
+ update_frame_menubar (f);
UNBLOCK_INPUT;
}
UNBLOCK_INPUT;
}
}
+/* Called from Fx_create_frame to create the inital menubar of a frame
+ before it is mapped, so that the window is mapped with the menubar already
+ there instead of us tacking it on later and thrashing the window after it
+ is visible. */
+void
+initialize_frame_menubar (f)
+ FRAME_PTR f;
+{
+ set_frame_menubar (f, 1);
+}
\f
-/* Nonzero if position X, Y relative to inside of frame F
- is in some other menu bar item. */
+/* Horizontal bounds of the current menu bar item. */
static int this_menu_bar_item_beg;
static int this_menu_bar_item_end;
+/* Horizontal position of the end of the last menu bar item. */
+
+static int last_menu_bar_item_end;
+
+/* Nonzero if position X, Y is in the menu bar and in some menu bar item
+ but not in the current menu bar item. */
+
static int
other_menu_bar_item_p (f, x, y)
FRAME_PTR f;
return (y >= 0
&& y < f->display.x->menubar_widget->core.height
&& x >= 0
- && x < f->display.x->menubar_widget->core.width
+ && x < last_menu_bar_item_end
&& (x >= this_menu_bar_item_end
|| x < this_menu_bar_item_beg));
}
struct event_queue *queue = NULL;
struct event_queue *queue_tmp;
+ Position root_x, root_y;
+
*error = NULL;
this_menu_bar_item_beg = -1;
this_menu_bar_item_end = -1;
+ last_menu_bar_item_end = -1;
/* Figure out which menu bar item, if any, this menu is for. */
if (menubarp)
xend += (string_width (menubar, menubar_item->name)
+ 2 * (menubar->menu.horizontal_spacing
+ menubar->menu.shadow_thickness));
- if (x < xend)
+ if (x >= xbeg && x < xend)
{
x = xbeg + 4;
y = 0;
to a different item. */
this_menu_bar_item_beg = xbeg;
this_menu_bar_item_end = xend;
- break;
}
}
+ last_menu_bar_item_end = xend;
}
if (menubar_item == 0)
menubarp = 0;
/* Offset the coordinates to root-relative. */
- x += (f->display.x->widget->core.x
- + f->display.x->widget->core.border_width);
- y += (f->display.x->widget->core.y
- + f->display.x->widget->core.border_width
- + f->display.x->menubar_widget->core.height);
+ XtTranslateCoords (f->display.x->widget,
+ x, y + f->display.x->menubar_widget->core.height,
+ &root_x, &root_y);
+ x = root_x;
+ y = root_y;
/* Create a tree of widget_value objects
representing the panes and their items. */
else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
&& submenu_depth != 0)
i += MENU_ITEMS_PANE_LENGTH;
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ i += 1;
else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
/* Create a new pane. */
prev_wv->next = wv;
else
save_wv->contents = wv;
- wv->name = XSTRING (item_name)->data;
+ wv->name = (char *) XSTRING (item_name)->data;
if (!NILP (descrip))
- wv->key = XSTRING (descrip)->data;
+ wv->key = (char *) XSTRING (descrip)->data;
wv->value = 0;
wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
wv->enabled = !NILP (enable);
dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
}
-#if 0 /* No need to do that. The menu has disappeared. */
+ /* fp turned off the following statement and wrote a comment
+ that it is unnecessary--that the menu has already disappeared.
+ I observer that is not so. -- rms. */
/* Make sure the menu disappears. */
lw_destroy_all_widgets (menu_id);
-#endif
/* Unread any events that we got but did not handle. */
while (queue != NULL)
XPutBackEvent (XDISPLAY &queue_tmp->event);
queue = queue_tmp->next;
free ((char *)queue_tmp);
+ /* Cause these events to get read as soon as we UNBLOCK_INPUT. */
+ interrupt_input_pending = 1;
}
/* Find the selected item, and its pane, to return
if (!NILP (prefix))
entry = Fcons (prefix, entry);
for (j = submenu_depth - 1; j >= 0; j--)
- entry = Fcons (subprefix_stack[j], entry);
+ if (!NILP (subprefix_stack[j]))
+ entry = Fcons (subprefix_stack[j], entry);
}
return entry;
}
"button6", "button7", "button8", "button9", "button10" };
static Lisp_Object
-xdialog_show (f, x, y, menubarp, keymaps, title, error)
+xdialog_show (f, menubarp, keymaps, title, error)
FRAME_PTR f;
- int x;
- int y;
int menubarp;
int keymaps;
Lisp_Object title;
int dialog_id;
Widget menu;
XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+ char dialog_name[6];
/* This is the menu bar item (if any) that led to this menu. */
widget_value *menubar_item = 0;
struct event_queue *queue = NULL;
struct event_queue *queue_tmp;
+ /* Number of elements seen so far, before boundary. */
+ int left_count = 0;
+ /* 1 means we've seen the boundary between left-hand elts and right-hand. */
+ int boundary_seen = 0;
+
*error = NULL;
+ if (menu_items_n_panes > 1)
+ {
+ *error = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
/* Create a tree of widget_value objects
representing the text label and buttons. */
{
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))
+ {
+ /* This is the boundary between left-side elts
+ and right-side elts. Stop incrementing right_count. */
+ boundary_seen = 1;
+ i++;
+ continue;
+ }
+ if (nb_buttons >= 10)
+ {
+ free_menubar_widget_value_tree (first_wv);
+ *error = "Too many dialog items";
+ return Qnil;
+ }
+
wv = malloc_widget_value ();
prev_wv->next = wv;
- wv->name = (char *) button_names [nb_buttons];
+ wv->name = (char *) button_names[nb_buttons];
if (!NILP (descrip))
- wv->key = XSTRING (descrip)->data;
- wv->value = XSTRING (item_name)->data;
+ 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 the boundary was not specified,
+ by default put half on the left and half on the right. */
+ if (! boundary_seen)
+ left_count = nb_buttons - nb_buttons / 2;
+
wv = malloc_widget_value ();
- wv->name = "Q2BR1";
+ wv->name = dialog_name;
+
+ /* Dialog boxes use a really stupid name encoding
+ which specifies how many buttons to use
+ and how many buttons are on the right.
+ The Q means something also. */
+ dialog_name[0] = 'Q';
+ dialog_name[1] = '0' + nb_buttons;
+ dialog_name[2] = 'B';
+ dialog_name[3] = 'R';
+ /* Number of buttons to put on the right. */
+ dialog_name[4] = '0' + nb_buttons - left_count;
+ dialog_name[5] = 0;
wv->contents = first_wv;
first_wv = wv;
-
}
/* Actually create the dialog. */
menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
f->display.x->widget, 1, 0,
dialog_selection_callback, 0);
+#if 0 /* This causes crashes, and seems to be redundant -- rms. */
lw_modify_all_widgets (dialog_id, first_wv, True);
- lw_modify_all_widgets (dialog_id, first_wv->contents, True);
+#endif
+ lw_modify_all_widgets (dialog_id, first_wv->contents->next, True);
/* Free the widget_value objects we used to specify the contents. */
free_menubar_widget_value_tree (first_wv);
}
pop_down:
+ /* State that no mouse buttons are now held.
+ That is not necessarily true, but the fiction leads to reasonable
+ results, and it is a pain to ask which are actually held now
+ or track this in the loop above. */
+ x_mouse_grabbed = 0;
+
/* Unread any events that we got but did not handle. */
while (queue != NULL)
{
XPutBackEvent (XDISPLAY &queue_tmp->event);
queue = queue_tmp->next;
free ((char *)queue_tmp);
+ /* Cause these events to get read as soon as we UNBLOCK_INPUT. */
+ interrupt_input_pending = 1;
}
/* Find the selected item, and its pane, to return
char *datap;
int ulx, uly, width, height;
int dispwidth, dispheight;
- int i;
+ int i, j;
+ int maxwidth;
int dummy_int;
unsigned int dummy_uint;
return Qnil;
}
i += MENU_ITEMS_PANE_LENGTH;
+
+ /* Find the width of the widest item in this pane. */
+ 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 = XSTRING (item)->size;
+ if (width > maxwidth)
+ maxwidth = width;
+
+ j += MENU_ITEMS_ITEM_LENGTH;
+ }
}
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ i += 1;
else
{
/* Create a new item within current pane. */
Lisp_Object item_name, enable, descrip;
+ unsigned char *item_data;
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 (descrip))
- item_name = concat2 (item_name, descrip);
+ {
+ int gap = maxwidth - XSTRING (item_name)->size;
+#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
+ /* if alloca is fast, use that to make the space,
+ to reduce gc needs. */
+ item_data
+ = (unsigned char *) alloca (maxwidth
+ + XSTRING (descrip)->size + 1);
+ bcopy (XSTRING (item_name)->data, item_data,
+ XSTRING (item_name)->size);
+ for (j = XSTRING (item_name)->size; j < maxwidth; j++)
+ item_data[j] = ' ';
+ bcopy (XSTRING (descrip)->data, item_data + j,
+ XSTRING (descrip)->size);
+ item_data[j + XSTRING (descrip)->size] = 0;
+#endif
+ }
+ else
+ item_data = XSTRING (item_name)->data;
- if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
- XSTRING (item_name)->data,
+ if (XMenuAddSelection (XDISPLAY menu, lpane, 0, item_data,
!NILP (enable))
== XM_FAILURE)
{
i += MENU_ITEMS_ITEM_LENGTH;
}
}
-
+
/* All set and ready to fly. */
XMenuRecompute (XDISPLAY menu);
dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
}
if (ulx < 0) x -= ulx;
if (uly < 0) y -= uly;
-
+
+ XMenuSetAEQ (menu, TRUE);
XMenuSetFreeze (menu, TRUE);
pane = selidx = 0;
break;
}
XMenuDestroy (XDISPLAY menu);
+
+ /* State that no mouse buttons are now held.
+ (The oldXMenu code doesn't track this info for us.)
+ That is not necessarily true, but the fiction leads to reasonable
+ results, and it is a pain to ask which are actually held now. */
+ x_mouse_grabbed = 0;
+
return entry;
}
#endif /* not USE_X_TOOLKIT */