X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/99fe880dc3e3e7d03db81657b76db196f9f2acfd..d43e0b1674f5d7250b5e452e78d64cc048d8f9d3:/src/xmenu.c diff --git a/src/xmenu.c b/src/xmenu.c index 354bf778f0..87a74a1a09 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -119,6 +119,9 @@ static void list_of_items (); 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. */ @@ -226,6 +229,17 @@ push_submenu_end () 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. */ @@ -609,6 +623,8 @@ list_of_items (pane) item = Fcar (tail); if (STRINGP (item)) push_menu_item (item, Qnil, Qnil, Qnil); + else if (NILP (item)) + push_left_right_boundary (); else { CHECK_CONS (item, 0); @@ -623,7 +639,7 @@ 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\ @@ -730,7 +746,7 @@ cached information about equivalent key sequences.") 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, @@ -844,7 +860,11 @@ The dialog box appears in the middle of the specified frame.\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.") +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; { @@ -856,6 +876,7 @@ The return value is VALUE from the chosen item.") /* 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; @@ -869,6 +890,10 @@ The return value is VALUE from the chosen item.") 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)) { @@ -956,7 +981,7 @@ dispatch_dummy_expose (w, x, y) dummy.x = x; dummy.y = y; - XtDispatchEvent (&dummy); + XtDispatchEvent ((XEvent *) &dummy); } static int @@ -1093,7 +1118,7 @@ free_menubar_widget_value_tree (wv) extern void EmacsFrameSetCharSize (); static void -update_one_frame_psheets (f) +update_frame_menubar (f) FRAME_PTR f; { struct x_display *x = f->display.x; @@ -1145,8 +1170,9 @@ update_one_frame_psheets (f) } 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; @@ -1162,7 +1188,8 @@ set_frame_menubar (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) { @@ -1177,7 +1204,7 @@ set_frame_menubar (f) 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; @@ -1202,7 +1229,9 @@ set_frame_menubar (f) 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; } @@ -1224,13 +1253,29 @@ free_frame_menubar (f) 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); +} -/* 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; @@ -1239,7 +1284,7 @@ other_menu_bar_item_p (f, x, y) 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)); } @@ -1348,10 +1393,13 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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) @@ -1367,7 +1415,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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; @@ -1375,19 +1423,19 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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. */ @@ -1417,6 +1465,10 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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. */ @@ -1465,9 +1517,9 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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); @@ -1617,10 +1669,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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) @@ -1629,6 +1682,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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 @@ -1674,7 +1729,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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; } @@ -1720,6 +1776,11 @@ xdialog_show (f, menubarp, keymaps, title, error) 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) @@ -1763,6 +1824,14 @@ xdialog_show (f, menubarp, keymaps, title, error) *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); @@ -1774,16 +1843,24 @@ xdialog_show (f, menubarp, keymaps, title, error) prev_wv->next = wv; 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 = dialog_name; @@ -1795,11 +1872,11 @@ xdialog_show (f, menubarp, keymaps, title, error) dialog_name[1] = '0' + nb_buttons; dialog_name[2] = 'B'; dialog_name[3] = 'R'; - dialog_name[4] = '0' + nb_buttons / 2; + /* 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. */ @@ -1848,6 +1925,12 @@ xdialog_show (f, menubarp, keymaps, title, error) } 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) { @@ -1855,6 +1938,8 @@ xdialog_show (f, menubarp, keymaps, title, error) 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 @@ -1914,7 +1999,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) char *datap; int ulx, uly, width, height; int dispwidth, dispheight; - int i; + int i, j; + int maxwidth; int dummy_int; unsigned int dummy_uint; @@ -1993,21 +2079,70 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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) { @@ -2018,7 +2153,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) i += MENU_ITEMS_ITEM_LENGTH; } } - + /* All set and ready to fly. */ XMenuRecompute (XDISPLAY menu); dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display)); @@ -2041,7 +2176,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) } if (ulx < 0) x -= ulx; if (uly < 0) y -= uly; - + + XMenuSetAEQ (menu, TRUE); XMenuSetFreeze (menu, TRUE); pane = selidx = 0; @@ -2098,6 +2234,13 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error) 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 */