0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

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.
This commit is contained in:
Bram Moolenaar 2018-03-03 18:59:16 +01:00
parent c71807db9c
commit aef8c3da2b
7 changed files with 349 additions and 157 deletions

View File

@ -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)

View File

@ -34,10 +34,6 @@ static int menu_namecmp(char_u *name, char_u *mname);
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)
@ -1895,6 +1891,60 @@ get_menu_mode(void)
return MENU_INDEX_INVALID;
}
/*
* 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
@ -1955,17 +2005,17 @@ gui_update_menus_recurse(vimmenu_T *menu, int mode)
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
@ -1976,7 +2026,7 @@ gui_update_menus_recurse(vimmenu_T *menu, int mode)
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 <negri>*/
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 *menu)
* 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;

View File

@ -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,24 +2670,38 @@ 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)
{
# 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)
{
@ -2740,12 +2754,10 @@ do_mouse(
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;
#else
return FALSE;
#endif

View File

@ -422,8 +422,10 @@ pum_redraw(void)
char_u *st;
int saved = *p;
if (saved != NUL)
*p = NUL;
st = transstr(s);
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 *list)
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

View File

@ -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);

View File

@ -9,4 +9,5 @@ int split_message(char_u *mesg, pumitem_T **array);
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 : */

View File

@ -778,6 +778,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1558,
/**/
1557,
/**/