# HG changeset patch # User Christian Brabandt # Date 1520100006 -3600 # Node ID 244ff1b6d2ad16ea9401b6e0f67a5e867bd6255f # Parent a68814ee7449a16453a13f0f704f7c9be641a146 patch 8.0.1558: no right-click menu in a terminal commit https://github.com/vim/vim/commit/aef8c3da2ba59285b7cfde559ae21cdce6ba6919 Author: Bram Moolenaar Date: Sat Mar 3 18:59:16 2018 +0100 patch 8.0.1558: no right-click menu in a terminal Problem: No right-click menu in a terminal. Solution: Implement the right click menu for the terminal. diff --git a/src/feature.h b/src/feature.h --- a/src/feature.h +++ b/src/feature.h @@ -726,6 +726,13 @@ # endif #endif +/* + * popup menu in a terminal + */ +#if defined(FEAT_MENU) && !defined(ALWAYS_USE_GUI) && defined(FEAT_INS_EXPAND) +# define FEAT_TERM_POPUP_MENU +#endif + /* There are two ways to use XPM. */ #if (defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF)) \ || defined(HAVE_X11_XPM_H) diff --git a/src/menu.c b/src/menu.c --- a/src/menu.c +++ b/src/menu.c @@ -34,10 +34,6 @@ static int menu_namecmp(char_u *name, ch static int get_menu_cmd_modes(char_u *, int, int *, int *); static char_u *popup_mode_name(char_u *name, int idx); static char_u *menu_text(char_u *text, int *mnemonic, char_u **actext); -#ifdef FEAT_GUI -static int get_menu_mode(void); -static void gui_update_menus_recurse(vimmenu_T *, int); -#endif #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF) static void gui_create_tearoffs_recurse(vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx); @@ -1871,7 +1867,7 @@ menu_is_tearoff(char_u *name UNUSED) } #endif -#ifdef FEAT_GUI +#if defined(FEAT_GUI) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO) static int get_menu_mode(void) @@ -1896,6 +1892,60 @@ get_menu_mode(void) } /* + * Display the Special "PopUp" menu as a pop-up at the current mouse + * position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode, + * etc. + */ + void +show_popupmenu(void) +{ + vimmenu_T *menu; + int mode; + + mode = get_menu_mode(); + if (mode == MENU_INDEX_INVALID) + return; + mode = menu_mode_chars[mode]; + +# ifdef FEAT_AUTOCMD + { + char_u ename[2]; + + ename[0] = mode; + ename[1] = NUL; + apply_autocmds(EVENT_MENUPOPUP, ename, NULL, FALSE, curbuf); + } +# endif + + for (menu = root_menu; menu != NULL; menu = menu->next) + if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode) + break; + + /* Only show a popup when it is defined and has entries */ + if (menu != NULL && menu->children != NULL) + { +# if defined(FEAT_GUI) + if (gui.in_use) + { + /* Update the menus now, in case the MenuPopup autocommand did + * anything. */ + gui_update_menus(0); + gui_mch_show_popupmenu(menu); + } +# endif +# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU) + else +# endif +# if defined(FEAT_TERM_POPUP_MENU) + pum_show_popupmenu(menu); +# endif + } +} +#endif + +#if defined(FEAT_GUI) || defined(PROTO) + +/* * Check that a pointer appears in the menu tree. Used to protect from using * a menu that was deleted after it was selected but before the event was * handled. @@ -1955,28 +2005,28 @@ gui_update_menus_recurse(vimmenu_T *menu while (menu) { if ((menu->modes & menu->enabled & mode) -#if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF) +# if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF) || menu_is_tearoff(menu->dname) -#endif +# endif ) grey = FALSE; else grey = TRUE; -#ifdef FEAT_GUI_ATHENA +# ifdef FEAT_GUI_ATHENA /* Hiding menus doesn't work for Athena, it can cause a crash. */ gui_mch_menu_grey(menu, grey); -#else +# else /* Never hide a toplevel menu, it may make the menubar resize or * disappear. Same problem for ToolBar items. */ if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL -# ifdef FEAT_TOOLBAR +# ifdef FEAT_TOOLBAR || menu_is_toolbar(menu->parent->name) -# endif +# endif ) gui_mch_menu_grey(menu, grey); else gui_mch_menu_hidden(menu, grey); -#endif +# endif gui_update_menus_recurse(menu->children, mode); menu = menu->next; } @@ -2010,15 +2060,15 @@ gui_update_menus(int modes) gui_mch_draw_menubar(); prev_mode = mode; force_menu_update = FALSE; -#ifdef FEAT_GUI_W32 +# ifdef FEAT_GUI_W32 /* This can leave a tearoff as active window - make sure we * have the focus */ gui_mch_activate_window(); -#endif +# endif } } -#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) \ +# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) \ || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON) || defined(PROTO) /* * Check if a key is used as a mnemonic for a toplevel menu. @@ -2037,47 +2087,7 @@ gui_is_menu_shortcut(int key) return TRUE; return FALSE; } -#endif - -/* - * Display the Special "PopUp" menu as a pop-up at the current mouse - * position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode, - * etc. - */ - void -gui_show_popupmenu(void) -{ - vimmenu_T *menu; - int mode; - - mode = get_menu_mode(); - if (mode == MENU_INDEX_INVALID) - return; - mode = menu_mode_chars[mode]; - -#ifdef FEAT_AUTOCMD - { - char_u ename[2]; - - ename[0] = mode; - ename[1] = NUL; - apply_autocmds(EVENT_MENUPOPUP, ename, NULL, FALSE, curbuf); - } -#endif - - for (menu = root_menu; menu != NULL; menu = menu->next) - if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode) - break; - - /* Only show a popup when it is defined and has entries */ - if (menu != NULL && menu->children != NULL) - { - /* Update the menus now, in case the MenuPopup autocommand did - * anything. */ - gui_update_menus(0); - gui_mch_show_popupmenu(menu); - } -} +# endif #endif /* FEAT_GUI */ #if (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)) || defined(PROTO) @@ -2238,7 +2248,7 @@ gui_destroy_tearoffs_recurse(vimmenu_T * * Execute "menu". Use by ":emenu" and the window toolbar. * "eap" is NULL for the window toolbar. */ - static void + void execute_menu(exarg_T *eap, vimmenu_T *menu) { char_u *mode; diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -2286,12 +2286,12 @@ op_function(oparg_T *oap UNUSED) * Do the appropriate action for the current mouse click in the current mode. * Not used for Command-line mode. * - * Normal Mode: + * Normal and Visual Mode: * event modi- position visual change action * fier cursor window * left press - yes end yes * left press C yes end yes "^]" (2) - * left press S yes end yes "*" (2) + * left press S yes end (popup: extend) yes "*" (2) * left drag - yes start if moved no * left relse - yes start if moved no * middle press - yes if not active no put register @@ -2670,82 +2670,94 @@ do_mouse( if (which_button == MOUSE_RIGHT && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) { - /* - * NOTE: Ignore right button down and drag mouse events. - * Windows only shows the popup menu on the button up event. - */ -#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \ - || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) - if (!is_click) - return FALSE; -#endif -#if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) - if (is_click || is_drag) - return FALSE; -#endif #if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \ || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \ - || defined(FEAT_GUI_MAC) || defined(FEAT_GUI_PHOTON) + || defined(FEAT_GUI_MAC) || defined(FEAT_GUI_PHOTON) \ + || defined(FEAT_TERM_POPUP_MENU) +# ifdef FEAT_GUI if (gui.in_use) { - jump_flags = 0; - if (STRCMP(p_mousem, "popup_setpos") == 0) +# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \ + || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) + if (!is_click) + /* Ignore right button release events, only shows the popup + * menu on the button down event. */ + return FALSE; +# endif +# if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) + if (is_click || is_drag) + /* Ignore right button down and drag mouse events. Windows + * only shows the popup menu on the button up event. */ + return FALSE; +# endif + } +# endif +# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU) + else +# endif +# if defined(FEAT_TERM_POPUP_MENU) + if (!is_click) + /* Ignore right button release events, only shows the popup + * menu on the button down event. */ + return FALSE; +#endif + + jump_flags = 0; + if (STRCMP(p_mousem, "popup_setpos") == 0) + { + /* First set the cursor position before showing the popup + * menu. */ + if (VIsual_active) { - /* First set the cursor position before showing the popup - * menu. */ - if (VIsual_active) + pos_T m_pos; + + /* + * set MOUSE_MAY_STOP_VIS if we are outside the + * selection or the current window (might have false + * negative here) + */ + if (mouse_row < curwin->w_winrow + || mouse_row + > (curwin->w_winrow + curwin->w_height)) + jump_flags = MOUSE_MAY_STOP_VIS; + else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) + jump_flags = MOUSE_MAY_STOP_VIS; + else { - pos_T m_pos; - - /* - * set MOUSE_MAY_STOP_VIS if we are outside the - * selection or the current window (might have false - * negative here) - */ - if (mouse_row < curwin->w_winrow - || mouse_row - > (curwin->w_winrow + curwin->w_height)) + if ((LT_POS(curwin->w_cursor, VIsual) + && (LT_POS(m_pos, curwin->w_cursor) + || LT_POS(VIsual, m_pos))) + || (LT_POS(VIsual, curwin->w_cursor) + && (LT_POS(m_pos, VIsual) + || LT_POS(curwin->w_cursor, m_pos)))) + { jump_flags = MOUSE_MAY_STOP_VIS; - else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) - jump_flags = MOUSE_MAY_STOP_VIS; - else + } + else if (VIsual_mode == Ctrl_V) { - if ((LT_POS(curwin->w_cursor, VIsual) - && (LT_POS(m_pos, curwin->w_cursor) - || LT_POS(VIsual, m_pos))) - || (LT_POS(VIsual, curwin->w_cursor) - && (LT_POS(m_pos, VIsual) - || LT_POS(curwin->w_cursor, m_pos)))) - { + getvcols(curwin, &curwin->w_cursor, &VIsual, + &leftcol, &rightcol); + getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL); + if (m_pos.col < leftcol || m_pos.col > rightcol) jump_flags = MOUSE_MAY_STOP_VIS; - } - else if (VIsual_mode == Ctrl_V) - { - getvcols(curwin, &curwin->w_cursor, &VIsual, - &leftcol, &rightcol); - getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL); - if (m_pos.col < leftcol || m_pos.col > rightcol) - jump_flags = MOUSE_MAY_STOP_VIS; - } } } - else - jump_flags = MOUSE_MAY_STOP_VIS; } - if (jump_flags) - { - jump_flags = jump_to_mouse(jump_flags, NULL, which_button); - update_curbuf(VIsual_active ? INVERTED : VALID); - setcursor(); - out_flush(); /* Update before showing popup menu */ - } + else + jump_flags = MOUSE_MAY_STOP_VIS; + } + if (jump_flags) + { + jump_flags = jump_to_mouse(jump_flags, NULL, which_button); + update_curbuf(VIsual_active ? INVERTED : VALID); + setcursor(); + out_flush(); /* Update before showing popup menu */ + } # ifdef FEAT_MENU - gui_show_popupmenu(); + show_popupmenu(); + got_click = FALSE; /* ignore release events */ # endif - return (jump_flags & CURSOR_MOVED) != 0; - } - else - return FALSE; + return (jump_flags & CURSOR_MOVED) != 0; #else return FALSE; #endif diff --git a/src/popupmnu.c b/src/popupmnu.c --- a/src/popupmnu.c +++ b/src/popupmnu.c @@ -422,9 +422,11 @@ pum_redraw(void) char_u *st; int saved = *p; - *p = NUL; + if (saved != NUL) + *p = NUL; st = transstr(s); - *p = saved; + if (saved != NUL) + *p = saved; #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) { @@ -830,6 +832,43 @@ pum_get_height(void) return pum_height; } +# if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO) + static void +pum_position_at_mouse(int min_width) +{ + if (Rows - mouse_row > pum_size) + { + /* Enough space below the mouse row. */ + pum_row = mouse_row + 1; + if (pum_height > Rows - pum_row) + pum_height = Rows - pum_row; + } + else + { + /* Show above the mouse row, reduce height if it does not fit. */ + pum_row = mouse_row - pum_size; + if (pum_row < 0) + { + pum_height += pum_row; + pum_row = 0; + } + } + if (Columns - mouse_col >= pum_base_width + || Columns - mouse_col > min_width) + /* Enough space to show at mouse column. */ + pum_col = mouse_col; + else + /* Not enough space, right align with window. */ + pum_col = Columns - (pum_base_width > min_width + ? min_width : pum_base_width); + + pum_width = Columns - pum_col; + if (pum_width > pum_base_width + 1) + pum_width = pum_base_width + 1; +} + +# endif + # if defined(FEAT_BEVAL_TERM) || defined(PROTO) static pumitem_T *balloon_array = NULL; static int balloon_arraysize; @@ -1028,36 +1067,7 @@ ui_post_balloon(char_u *mesg, list_T *li pum_scrollbar = 0; pum_height = balloon_arraysize; - if (Rows - mouse_row > pum_size) - { - /* Enough space below the mouse row. */ - pum_row = mouse_row + 1; - if (pum_height > Rows - pum_row) - pum_height = Rows - pum_row; - } - else - { - /* Show above the mouse row, reduce height if it does not fit. */ - pum_row = mouse_row - pum_size; - if (pum_row < 0) - { - pum_height += pum_row; - pum_row = 0; - } - } - if (Columns - mouse_col >= pum_base_width - || Columns - mouse_col > BALLOON_MIN_WIDTH) - /* Enough space to show at mouse column. */ - pum_col = mouse_col; - else - /* Not enough space, right align with window. */ - pum_col = Columns - (pum_base_width > BALLOON_MIN_WIDTH - ? BALLOON_MIN_WIDTH : pum_base_width); - - pum_width = Columns - pum_col; - if (pum_width > pum_base_width + 1) - pum_width = pum_base_width + 1; - + pum_position_at_mouse(BALLOON_MIN_WIDTH); pum_selected = -1; pum_first = 0; pum_redraw(); @@ -1075,4 +1085,153 @@ ui_may_remove_balloon(void) } # endif +# if defined(FEAT_TERM_POPUP_MENU) || defined(PROTO) +/* + * Select the pum entry at the mouse position. + */ + static void +pum_select_mouse_pos(void) +{ + int idx = mouse_row - pum_row; + + if (idx < 0 || idx >= pum_size) + pum_selected = -1; + else if (*pum_array[idx].pum_text != NUL) + pum_selected = idx; +} + +/* + * Execute the currently selected popup menu item. + */ + static void +pum_execute_menu(vimmenu_T *menu) +{ + vimmenu_T *mp; + int idx = 0; + exarg_T ea; + + for (mp = menu->children; mp != NULL; mp = mp->next) + if (idx++ == pum_selected) + { + vim_memset(&ea, 0, sizeof(ea)); + execute_menu(&ea, mp); + break; + } +} + +/* + * Open the terminal version of the popup menu and don't return until it is + * closed. + */ + void +pum_show_popupmenu(vimmenu_T *menu) +{ + vimmenu_T *mp; + int idx = 0; + pumitem_T *array; +#ifdef FEAT_BEVAL_TERM + int save_bevalterm = p_bevalterm; #endif + + pum_undisplay(); + pum_size = 0; + + for (mp = menu->children; mp != NULL; mp = mp->next) + ++pum_size; + + array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * pum_size); + if (array == NULL) + return; + + for (mp = menu->children; mp != NULL; mp = mp->next) + if (menu_is_separator(mp->dname)) + array[idx++].pum_text = (char_u *)""; + else + array[idx++].pum_text = mp->dname; + + pum_array = array; + pum_compute_size(); + pum_scrollbar = 0; + pum_height = pum_size; + pum_position_at_mouse(20); + + pum_selected = -1; + pum_first = 0; +# ifdef FEAT_BEVAL_TERM + p_bevalterm = TRUE; /* track mouse movement */ + mch_setmouse(TRUE); +# endif + + for (;;) + { + int c; + + pum_redraw(); + setcursor(); + out_flush(); + + c = vgetc(); + if (c == ESC) + break; + else if (c == CAR || c == NL) + { + /* enter: select current item, if any, and close */ + pum_execute_menu(menu); + break; + } + else if (c == 'k' || c == K_UP || c == K_MOUSEUP) + { + /* cursor up: select previous item */ + while (pum_selected > 0) + { + --pum_selected; + if (*array[pum_selected].pum_text != NUL) + break; + } + } + else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN) + { + /* cursor down: select next item */ + while (pum_selected < pum_size - 1) + { + ++pum_selected; + if (*array[pum_selected].pum_text != NUL) + break; + } + } + else if (c == K_RIGHTMOUSE) + { + /* Right mouse down: reposition the menu. */ + vungetc(c); + break; + } + else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE) + { + /* mouse moved: selec item in the mouse row */ + pum_select_mouse_pos(); + } + else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE) + { + /* left mouse click: select clicked item, if any, and close; + * right mouse release: select clicked item, close if any */ + pum_select_mouse_pos(); + if (pum_selected >= 0) + { + pum_execute_menu(menu); + break; + } + if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) + break; + } + } + + vim_free(array); + pum_undisplay(); +# ifdef FEAT_BEVAL_TERM + p_bevalterm = save_bevalterm; + mch_setmouse(TRUE); +# endif +} +# endif + +#endif diff --git a/src/proto/menu.pro b/src/proto/menu.pro --- a/src/proto/menu.pro +++ b/src/proto/menu.pro @@ -12,12 +12,13 @@ int menu_is_popup(char_u *name); int menu_is_child_of_popup(vimmenu_T *menu); int menu_is_toolbar(char_u *name); int menu_is_separator(char_u *name); +void show_popupmenu(void); int check_menu_pointer(vimmenu_T *root, vimmenu_T *menu_to_check); void gui_create_initial_menus(vimmenu_T *menu); void gui_update_menus(int modes); int gui_is_menu_shortcut(int key); -void gui_show_popupmenu(void); void gui_mch_toggle_tearoffs(int enable); +void execute_menu(exarg_T *eap, vimmenu_T *menu); void ex_emenu(exarg_T *eap); void winbar_click(win_T *wp, int col); vimmenu_T *gui_find_menu(char_u *path_name); diff --git a/src/proto/popupmnu.pro b/src/proto/popupmnu.pro --- a/src/proto/popupmnu.pro +++ b/src/proto/popupmnu.pro @@ -9,4 +9,5 @@ int split_message(char_u *mesg, pumitem_ void ui_remove_balloon(void); void ui_post_balloon(char_u *mesg, list_T *list); void ui_may_remove_balloon(void); +void pum_show_popupmenu(vimmenu_T *menu); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -779,6 +779,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1558, +/**/ 1557, /**/ 1556,