summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-03-03 18:59:16 +0100
committerBram Moolenaar <Bram@vim.org>2018-03-03 18:59:16 +0100
commitaef8c3da2ba59285b7cfde559ae21cdce6ba6919 (patch)
tree14eea82ad1aa76a79e902500d39ba73de63c21a3
parentc71807db9c1821baf86796cd76952df36ff1a29a (diff)
downloadvim-git-aef8c3da2ba59285b7cfde559ae21cdce6ba6919.tar.gz
patch 8.0.1558: no right-click menu in a terminalv8.0.1558
Problem: No right-click menu in a terminal. Solution: Implement the right click menu for the terminal.
-rw-r--r--src/feature.h7
-rw-r--r--src/menu.c124
-rw-r--r--src/normal.c136
-rw-r--r--src/popupmnu.c223
-rw-r--r--src/proto/menu.pro3
-rw-r--r--src/proto/popupmnu.pro1
-rw-r--r--src/version.c2
7 files changed, 344 insertions, 152 deletions
diff --git a/src/feature.h b/src/feature.h
index d29e599a9..2d57d6704 100644
--- 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
index 310cf5928..e453135ae 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -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)
@@ -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, 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
-# 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 <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;
diff --git a/src/normal.c b/src/normal.c
index c234c3206..cea8c9f16 100644
--- 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_ATHENA) || defined(FEAT_GUI_MSWIN) \
+ || 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
-#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)
- if (gui.in_use)
+
+ jump_flags = 0;
+ if (STRCMP(p_mousem, "popup_setpos") == 0)
{
- 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))
- jump_flags = MOUSE_MAY_STOP_VIS;
- else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
+ 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
+ }
+ 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
index d9def3664..6cfd95a4e 100644
--- 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 *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
diff --git a/src/proto/menu.pro b/src/proto/menu.pro
index bd3d26b92..4c8e710d8 100644
--- 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
index 272730433..d2f6baf46 100644
--- a/src/proto/popupmnu.pro
+++ b/src/proto/popupmnu.pro
@@ -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 : */
diff --git a/src/version.c b/src/version.c
index 7d5418592..f6972163c 100644
--- 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,