summaryrefslogtreecommitdiff
path: root/src/mouse.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-09-21 20:48:04 +0200
committerBram Moolenaar <Bram@vim.org>2019-09-21 20:48:04 +0200
commitb20b9e14ddd8db111e886ad0494e15b955159426 (patch)
tree81bf23be4dbc17d4b793a29f3252448f5f1e7322 /src/mouse.c
parent4d5c12626c98df23e1a5d953692d946310ddfa9c (diff)
downloadvim-git-b20b9e14ddd8db111e886ad0494e15b955159426.tar.gz
patch 8.1.2062: the mouse code is spread outv8.1.2062
Problem: The mouse code is spread out. Solution: Move all the mouse code to mouse.c. (Yegappan Lakshmanan, closes #4959)
Diffstat (limited to 'src/mouse.c')
-rw-r--r--src/mouse.c2314
1 files changed, 2314 insertions, 0 deletions
diff --git a/src/mouse.c b/src/mouse.c
new file mode 100644
index 000000000..f7a92a507
--- /dev/null
+++ b/src/mouse.c
@@ -0,0 +1,2314 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * mouse.c: mouse handling functions
+ */
+
+#include "vim.h"
+
+#if defined(FEAT_MOUSE) || defined(PROTO)
+
+static int get_fpos_of_mouse(pos_T *mpos);
+
+/*
+ * Get class of a character for selection: same class means same word.
+ * 0: blank
+ * 1: punctuation groups
+ * 2: normal word character
+ * >2: multi-byte word character.
+ */
+ static int
+get_mouse_class(char_u *p)
+{
+ int c;
+
+ if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
+ return mb_get_class(p);
+
+ c = *p;
+ if (c == ' ' || c == '\t')
+ return 0;
+
+ if (vim_iswordc(c))
+ return 2;
+
+ // There are a few special cases where we want certain combinations of
+ // characters to be considered as a single word. These are things like
+ // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
+ // character is in its own class.
+ if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
+ return 1;
+ return c;
+}
+
+/*
+ * Move "pos" back to the start of the word it's in.
+ */
+ static void
+find_start_of_word(pos_T *pos)
+{
+ char_u *line;
+ int cclass;
+ int col;
+
+ line = ml_get(pos->lnum);
+ cclass = get_mouse_class(line + pos->col);
+
+ while (pos->col > 0)
+ {
+ col = pos->col - 1;
+ col -= (*mb_head_off)(line, line + col);
+ if (get_mouse_class(line + col) != cclass)
+ break;
+ pos->col = col;
+ }
+}
+
+/*
+ * Move "pos" forward to the end of the word it's in.
+ * When 'selection' is "exclusive", the position is just after the word.
+ */
+ static void
+find_end_of_word(pos_T *pos)
+{
+ char_u *line;
+ int cclass;
+ int col;
+
+ line = ml_get(pos->lnum);
+ if (*p_sel == 'e' && pos->col > 0)
+ {
+ --pos->col;
+ pos->col -= (*mb_head_off)(line, line + pos->col);
+ }
+ cclass = get_mouse_class(line + pos->col);
+ while (line[pos->col] != NUL)
+ {
+ col = pos->col + (*mb_ptr2len)(line + pos->col);
+ if (get_mouse_class(line + col) != cclass)
+ {
+ if (*p_sel == 'e')
+ pos->col = col;
+ break;
+ }
+ pos->col = col;
+ }
+}
+
+/*
+ * Do the appropriate action for the current mouse click in the current mode.
+ * Not used for Command-line 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 (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
+ * middle press - yes if active no yank and put
+ * right press - yes start or extend yes
+ * right press S yes no change yes "#" (2)
+ * right drag - yes extend no
+ * right relse - yes extend no
+ *
+ * Insert or Replace Mode:
+ * event modi- position visual change action
+ * fier cursor window
+ * left press - yes (cannot be active) yes
+ * left press C yes (cannot be active) yes "CTRL-O^]" (2)
+ * left press S yes (cannot be active) yes "CTRL-O*" (2)
+ * left drag - yes start or extend (1) no CTRL-O (1)
+ * left relse - yes start or extend (1) no CTRL-O (1)
+ * middle press - no (cannot be active) no put register
+ * right press - yes start or extend yes CTRL-O
+ * right press S yes (cannot be active) yes "CTRL-O#" (2)
+ *
+ * (1) only if mouse pointer moved since press
+ * (2) only if click is in same buffer
+ *
+ * Return TRUE if start_arrow() should be called for edit mode.
+ */
+ int
+do_mouse(
+ oparg_T *oap, // operator argument, can be NULL
+ int c, // K_LEFTMOUSE, etc
+ int dir, // Direction to 'put' if necessary
+ long count,
+ int fixindent) // PUT_FIXINDENT if fixing indent necessary
+{
+ static int do_always = FALSE; // ignore 'mouse' setting next time
+ static int got_click = FALSE; // got a click some time back
+
+ int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
+ int is_click = FALSE; // If FALSE it's a drag or release event
+ int is_drag = FALSE; // If TRUE it's a drag event
+ int jump_flags = 0; // flags for jump_to_mouse()
+ pos_T start_visual;
+ int moved; // Has cursor moved?
+ int in_status_line; // mouse in status line
+ static int in_tab_line = FALSE; // mouse clicked in tab line
+ int in_sep_line; // mouse in vertical separator line
+ int c1, c2;
+#if defined(FEAT_FOLDING)
+ pos_T save_cursor;
+#endif
+ win_T *old_curwin = curwin;
+ static pos_T orig_cursor;
+ colnr_T leftcol, rightcol;
+ pos_T end_visual;
+ int diff;
+ int old_active = VIsual_active;
+ int old_mode = VIsual_mode;
+ int regname;
+
+#if defined(FEAT_FOLDING)
+ save_cursor = curwin->w_cursor;
+#endif
+
+ // When GUI is active, always recognize mouse events, otherwise:
+ // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
+ // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
+ // - For command line and insert mode 'mouse' is checked before calling
+ // do_mouse().
+ if (do_always)
+ do_always = FALSE;
+ else
+#ifdef FEAT_GUI
+ if (!gui.in_use)
+#endif
+ {
+ if (VIsual_active)
+ {
+ if (!mouse_has(MOUSE_VISUAL))
+ return FALSE;
+ }
+ else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
+ return FALSE;
+ }
+
+ for (;;)
+ {
+ which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
+ if (is_drag)
+ {
+ // If the next character is the same mouse event then use that
+ // one. Speeds up dragging the status line.
+ if (vpeekc() != NUL)
+ {
+ int nc;
+ int save_mouse_row = mouse_row;
+ int save_mouse_col = mouse_col;
+
+ // Need to get the character, peeking doesn't get the actual
+ // one.
+ nc = safe_vgetc();
+ if (c == nc)
+ continue;
+ vungetc(nc);
+ mouse_row = save_mouse_row;
+ mouse_col = save_mouse_col;
+ }
+ }
+ break;
+ }
+
+ if (c == K_MOUSEMOVE)
+ {
+ // Mouse moved without a button pressed.
+#ifdef FEAT_BEVAL_TERM
+ ui_may_remove_balloon();
+ if (p_bevalterm)
+ {
+ profile_setlimit(p_bdlay, &bevalexpr_due);
+ bevalexpr_due_set = TRUE;
+ }
+#endif
+#ifdef FEAT_TEXT_PROP
+ popup_handle_mouse_moved();
+#endif
+ return FALSE;
+ }
+
+#ifdef FEAT_MOUSESHAPE
+ // May have stopped dragging the status or separator line. The pointer is
+ // most likely still on the status or separator line.
+ if (!is_drag && drag_status_line)
+ {
+ drag_status_line = FALSE;
+ update_mouseshape(SHAPE_IDX_STATUS);
+ }
+ if (!is_drag && drag_sep_line)
+ {
+ drag_sep_line = FALSE;
+ update_mouseshape(SHAPE_IDX_VSEP);
+ }
+#endif
+
+ // Ignore drag and release events if we didn't get a click.
+ if (is_click)
+ got_click = TRUE;
+ else
+ {
+ if (!got_click) // didn't get click, ignore
+ return FALSE;
+ if (!is_drag) // release, reset got_click
+ {
+ got_click = FALSE;
+ if (in_tab_line)
+ {
+ in_tab_line = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ // CTRL right mouse button does CTRL-T
+ if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
+ {
+ if (State & INSERT)
+ stuffcharReadbuff(Ctrl_O);
+ if (count > 1)
+ stuffnumReadbuff(count);
+ stuffcharReadbuff(Ctrl_T);
+ got_click = FALSE; // ignore drag&release now
+ return FALSE;
+ }
+
+ // CTRL only works with left mouse button
+ if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
+ return FALSE;
+
+ // When a modifier is down, ignore drag and release events, as well as
+ // multiple clicks and the middle mouse button.
+ // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
+ if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
+ | MOD_MASK_META))
+ && (!is_click
+ || (mod_mask & MOD_MASK_MULTI_CLICK)
+ || which_button == MOUSE_MIDDLE)
+ && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
+ && mouse_model_popup()
+ && which_button == MOUSE_LEFT)
+ && !((mod_mask & MOD_MASK_ALT)
+ && !mouse_model_popup()
+ && which_button == MOUSE_RIGHT)
+ )
+ return FALSE;
+
+ // If the button press was used as the movement command for an operator
+ // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
+ // drag/release events.
+ if (!is_click && which_button == MOUSE_MIDDLE)
+ return FALSE;
+
+ if (oap != NULL)
+ regname = oap->regname;
+ else
+ regname = 0;
+
+ // Middle mouse button does a 'put' of the selected text
+ if (which_button == MOUSE_MIDDLE)
+ {
+ if (State == NORMAL)
+ {
+ // If an operator was pending, we don't know what the user wanted
+ // to do. Go back to normal mode: Clear the operator and beep().
+ if (oap != NULL && oap->op_type != OP_NOP)
+ {
+ clearopbeep(oap);
+ return FALSE;
+ }
+
+ // If visual was active, yank the highlighted text and put it
+ // before the mouse pointer position.
+ // In Select mode replace the highlighted text with the clipboard.
+ if (VIsual_active)
+ {
+ if (VIsual_select)
+ {
+ stuffcharReadbuff(Ctrl_G);
+ stuffReadbuff((char_u *)"\"+p");
+ }
+ else
+ {
+ stuffcharReadbuff('y');
+ stuffcharReadbuff(K_MIDDLEMOUSE);
+ }
+ do_always = TRUE; // ignore 'mouse' setting next time
+ return FALSE;
+ }
+ // The rest is below jump_to_mouse()
+ }
+
+ else if ((State & INSERT) == 0)
+ return FALSE;
+
+ // Middle click in insert mode doesn't move the mouse, just insert the
+ // contents of a register. '.' register is special, can't insert that
+ // with do_put().
+ // Also paste at the cursor if the current mode isn't in 'mouse' (only
+ // happens for the GUI).
+ if ((State & INSERT) || !mouse_has(MOUSE_NORMAL))
+ {
+ if (regname == '.')
+ insert_reg(regname, TRUE);
+ else
+ {
+#ifdef FEAT_CLIPBOARD
+ if (clip_star.available && regname == 0)
+ regname = '*';
+#endif
+ if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
+ insert_reg(regname, TRUE);
+ else
+ {
+ do_put(regname, BACKWARD, 1L, fixindent | PUT_CURSEND);
+
+ // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
+ AppendCharToRedobuff(Ctrl_R);
+ AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
+ AppendCharToRedobuff(regname == 0 ? '"' : regname);
+ }
+ }
+ return FALSE;
+ }
+ }
+
+ // When dragging or button-up stay in the same window.
+ if (!is_click)
+ jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
+
+ start_visual.lnum = 0;
+
+ // Check for clicking in the tab page line.
+ if (mouse_row == 0 && firstwin->w_winrow > 0)
+ {
+ if (is_drag)
+ {
+ if (in_tab_line)
+ {
+ c1 = TabPageIdxs[mouse_col];
+ tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
+ ? c1 - 1 : c1);
+ }
+ return FALSE;
+ }
+
+ // click in a tab selects that tab page
+ if (is_click
+# ifdef FEAT_CMDWIN
+ && cmdwin_type == 0
+# endif
+ && mouse_col < Columns)
+ {
+ in_tab_line = TRUE;
+ c1 = TabPageIdxs[mouse_col];
+ if (c1 >= 0)
+ {
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ {
+ // double click opens new page
+ end_visual_mode();
+ tabpage_new();
+ tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ }
+ else
+ {
+ // Go to specified tab page, or next one if not clicking
+ // on a label.
+ goto_tabpage(c1);
+
+ // It's like clicking on the status line of a window.
+ if (curwin != old_curwin)
+ end_visual_mode();
+ }
+ }
+ else
+ {
+ tabpage_T *tp;
+
+ // Close the current or specified tab page.
+ if (c1 == -999)
+ tp = curtab;
+ else
+ tp = find_tabpage(-c1);
+ if (tp == curtab)
+ {
+ if (first_tabpage->tp_next != NULL)
+ tabpage_close(FALSE);
+ }
+ else if (tp != NULL)
+ tabpage_close_other(tp, FALSE);
+ }
+ }
+ return TRUE;
+ }
+ else if (is_drag && in_tab_line)
+ {
+ c1 = TabPageIdxs[mouse_col];
+ tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
+ return FALSE;
+ }
+
+ // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
+ // right button up -> pop-up menu
+ // shift-left button -> right button
+ // alt-left button -> alt-right button
+ if (mouse_model_popup())
+ {
+ if (which_button == MOUSE_RIGHT
+ && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
+ {
+#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
+
+ jump_flags = 0;
+ if (STRCMP(p_mousem, "popup_setpos") == 0)
+ {
+ // 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
+ {
+ 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 (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
+ }
+# ifdef FEAT_MENU
+ show_popupmenu();
+ got_click = FALSE; // ignore release events
+# endif
+ return (jump_flags & CURSOR_MOVED) != 0;
+#else
+ return FALSE;
+#endif
+ }
+ if (which_button == MOUSE_LEFT
+ && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
+ {
+ which_button = MOUSE_RIGHT;
+ mod_mask &= ~MOD_MASK_SHIFT;
+ }
+ }
+
+ if ((State & (NORMAL | INSERT))
+ && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
+ {
+ if (which_button == MOUSE_LEFT)
+ {
+ if (is_click)
+ {
+ // stop Visual mode for a left click in a window, but not when
+ // on a status line
+ if (VIsual_active)
+ jump_flags |= MOUSE_MAY_STOP_VIS;
+ }
+ else if (mouse_has(MOUSE_VISUAL))
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ else if (which_button == MOUSE_RIGHT)
+ {
+ if (is_click && VIsual_active)
+ {
+ // Remember the start and end of visual before moving the
+ // cursor.
+ if (LT_POS(curwin->w_cursor, VIsual))
+ {
+ start_visual = curwin->w_cursor;
+ end_visual = VIsual;
+ }
+ else
+ {
+ start_visual = VIsual;
+ end_visual = curwin->w_cursor;
+ }
+ }
+ jump_flags |= MOUSE_FOCUS;
+ if (mouse_has(MOUSE_VISUAL))
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ }
+
+ // If an operator is pending, ignore all drags and releases until the
+ // next mouse click.
+ if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
+ {
+ got_click = FALSE;
+ oap->motion_type = MCHAR;
+ }
+
+ // When releasing the button let jump_to_mouse() know.
+ if (!is_click && !is_drag)
+ jump_flags |= MOUSE_RELEASED;
+
+ // JUMP!
+ jump_flags = jump_to_mouse(jump_flags,
+ oap == NULL ? NULL : &(oap->inclusive), which_button);
+
+#ifdef FEAT_MENU
+ // A click in the window toolbar has no side effects.
+ if (jump_flags & MOUSE_WINBAR)
+ return FALSE;
+#endif
+ moved = (jump_flags & CURSOR_MOVED);
+ in_status_line = (jump_flags & IN_STATUS_LINE);
+ in_sep_line = (jump_flags & IN_SEP_LINE);
+
+#ifdef FEAT_NETBEANS_INTG
+ if (isNetbeansBuffer(curbuf)
+ && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
+ {
+ int key = KEY2TERMCAP1(c);
+
+ if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
+ || key == (int)KE_RIGHTRELEASE)
+ netbeans_button_release(which_button);
+ }
+#endif
+
+ // When jumping to another window, clear a pending operator. That's a bit
+ // friendlier than beeping and not jumping to that window.
+ if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
+ clearop(oap);
+
+#ifdef FEAT_FOLDING
+ if (mod_mask == 0
+ && !is_drag
+ && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
+ && which_button == MOUSE_LEFT)
+ {
+ // open or close a fold at this line
+ if (jump_flags & MOUSE_FOLD_OPEN)
+ openFold(curwin->w_cursor.lnum, 1L);
+ else
+ closeFold(curwin->w_cursor.lnum, 1L);
+ // don't move the cursor if still in the same window
+ if (curwin == old_curwin)
+ curwin->w_cursor = save_cursor;
+ }
+#endif
+
+#if defined(FEAT_CLIPBOARD) && defined(FEAT_CMDWIN)
+ if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
+ {
+ clip_modeless(which_button, is_click, is_drag);
+ return FALSE;
+ }
+#endif
+
+ // Set global flag that we are extending the Visual area with mouse
+ // dragging; temporarily minimize 'scrolloff'.
+ if (VIsual_active && is_drag && get_scrolloff_value())
+ {
+ // In the very first line, allow scrolling one line
+ if (mouse_row == 0)
+ mouse_dragging = 2;
+ else
+ mouse_dragging = 1;
+ }
+
+ // When dragging the mouse above the window, scroll down.
+ if (is_drag && mouse_row < 0 && !in_status_line)
+ {
+ scroll_redraw(FALSE, 1L);
+ mouse_row = 0;
+ }
+
+ if (start_visual.lnum) // right click in visual mode
+ {
+ // When ALT is pressed make Visual mode blockwise.
+ if (mod_mask & MOD_MASK_ALT)
+ VIsual_mode = Ctrl_V;
+
+ // In Visual-block mode, divide the area in four, pick up the corner
+ // that is in the quarter that the cursor is in.
+ if (VIsual_mode == Ctrl_V)
+ {
+ getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
+ if (curwin->w_curswant > (leftcol + rightcol) / 2)
+ end_visual.col = leftcol;
+ else
+ end_visual.col = rightcol;
+ if (curwin->w_cursor.lnum >=
+ (start_visual.lnum + end_visual.lnum) / 2)
+ end_visual.lnum = start_visual.lnum;
+
+ // move VIsual to the right column
+ start_visual = curwin->w_cursor; // save the cursor pos
+ curwin->w_cursor = end_visual;
+ coladvance(end_visual.col);
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = start_visual; // restore the cursor
+ }
+ else
+ {
+ // If the click is before the start of visual, change the start.
+ // If the click is after the end of visual, change the end. If
+ // the click is inside the visual, change the closest side.
+ if (LT_POS(curwin->w_cursor, start_visual))
+ VIsual = end_visual;
+ else if (LT_POS(end_visual, curwin->w_cursor))
+ VIsual = start_visual;
+ else
+ {
+ // In the same line, compare column number
+ if (end_visual.lnum == start_visual.lnum)
+ {
+ if (curwin->w_cursor.col - start_visual.col >
+ end_visual.col - curwin->w_cursor.col)
+ VIsual = start_visual;
+ else
+ VIsual = end_visual;
+ }
+
+ // In different lines, compare line number
+ else
+ {
+ diff = (curwin->w_cursor.lnum - start_visual.lnum) -
+ (end_visual.lnum - curwin->w_cursor.lnum);
+
+ if (diff > 0) // closest to end
+ VIsual = start_visual;
+ else if (diff < 0) // closest to start
+ VIsual = end_visual;
+ else // in the middle line
+ {
+ if (curwin->w_cursor.col <
+ (start_visual.col + end_visual.col) / 2)
+ VIsual = end_visual;
+ else
+ VIsual = start_visual;
+ }
+ }
+ }
+ }
+ }
+ // If Visual mode started in insert mode, execute "CTRL-O"
+ else if ((State & INSERT) && VIsual_active)
+ stuffcharReadbuff(Ctrl_O);
+
+ // Middle mouse click: Put text before cursor.
+ if (which_button == MOUSE_MIDDLE)
+ {
+#ifdef FEAT_CLIPBOARD
+ if (clip_star.available && regname == 0)
+ regname = '*';
+#endif
+ if (yank_register_mline(regname))
+ {
+ if (mouse_past_bottom)
+ dir = FORWARD;
+ }
+ else if (mouse_past_eol)
+ dir = FORWARD;
+
+ if (fixindent)
+ {
+ c1 = (dir == BACKWARD) ? '[' : ']';
+ c2 = 'p';
+ }
+ else
+ {
+ c1 = (dir == FORWARD) ? 'p' : 'P';
+ c2 = NUL;
+ }
+ prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
+
+ // Remember where the paste started, so in edit() Insstart can be set
+ // to this position
+ if (restart_edit != 0)
+ where_paste_started = curwin->w_cursor;
+ do_put(regname, dir, count, fixindent | PUT_CURSEND);
+ }
+
+#if defined(FEAT_QUICKFIX)
+ // Ctrl-Mouse click or double click in a quickfix window jumps to the
+ // error under the mouse pointer.
+ else if (((mod_mask & MOD_MASK_CTRL)
+ || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ && bt_quickfix(curbuf))
+ {
+ if (curwin->w_llist_ref == NULL) // quickfix window
+ do_cmdline_cmd((char_u *)".cc");
+ else // location list window
+ do_cmdline_cmd((char_u *)".ll");
+ got_click = FALSE; // ignore drag&release now
+ }
+#endif
+
+ // Ctrl-Mouse click (or double click in a help window) jumps to the tag
+ // under the mouse pointer.
+ else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
+ && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
+ {
+ if (State & INSERT)
+ stuffcharReadbuff(Ctrl_O);
+ stuffcharReadbuff(Ctrl_RSB);
+ got_click = FALSE; // ignore drag&release now
+ }
+
+ // Shift-Mouse click searches for the next occurrence of the word under
+ // the mouse pointer
+ else if ((mod_mask & MOD_MASK_SHIFT))
+ {
+ if ((State & INSERT) || (VIsual_active && VIsual_select))
+ stuffcharReadbuff(Ctrl_O);
+ if (which_button == MOUSE_LEFT)
+ stuffcharReadbuff('*');
+ else // MOUSE_RIGHT
+ stuffcharReadbuff('#');
+ }
+
+ // Handle double clicks, unless on status line
+ else if (in_status_line)
+ {
+#ifdef FEAT_MOUSESHAPE
+ if ((is_drag || is_click) && !drag_status_line)
+ {
+ drag_status_line = TRUE;
+ update_mouseshape(-1);
+ }
+#endif
+ }
+ else if (in_sep_line)
+ {
+#ifdef FEAT_MOUSESHAPE
+ if ((is_drag || is_click) && !drag_sep_line)
+ {
+ drag_sep_line = TRUE;
+ update_mouseshape(-1);
+ }
+#endif
+ }
+ else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))
+ && mouse_has(MOUSE_VISUAL))
+ {
+ if (is_click || !VIsual_active)
+ {
+ if (VIsual_active)
+ orig_cursor = VIsual;
+ else
+ {
+ check_visual_highlight();
+ VIsual = curwin->w_cursor;
+ orig_cursor = VIsual;
+ VIsual_active = TRUE;
+ VIsual_reselect = TRUE;
+ // start Select mode if 'selectmode' contains "mouse"
+ may_start_select('o');
+ setmouse();
+ }
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ {
+ // Double click with ALT pressed makes it blockwise.
+ if (mod_mask & MOD_MASK_ALT)
+ VIsual_mode = Ctrl_V;
+ else
+ VIsual_mode = 'v';
+ }
+ else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
+ VIsual_mode = 'V';
+ else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
+ VIsual_mode = Ctrl_V;
+#ifdef FEAT_CLIPBOARD
+ // Make sure the clipboard gets updated. Needed because start and
+ // end may still be the same, and the selection needs to be owned
+ clip_star.vmode = NUL;
+#endif
+ }
+ // A double click selects a word or a block.
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ {
+ pos_T *pos = NULL;
+ int gc;
+
+ if (is_click)
+ {
+ // If the character under the cursor (skipping white space) is
+ // not a word character, try finding a match and select a (),
+ // {}, [], #if/#endif, etc. block.
+ end_visual = curwin->w_cursor;
+ while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
+ inc(&end_visual);
+ if (oap != NULL)
+ oap->motion_type = MCHAR;
+ if (oap != NULL
+ && VIsual_mode == 'v'
+ && !vim_iswordc(gchar_pos(&end_visual))
+ && EQUAL_POS(curwin->w_cursor, VIsual)
+ && (pos = findmatch(oap, NUL)) != NULL)
+ {
+ curwin->w_cursor = *pos;
+ if (oap->motion_type == MLINE)
+ VIsual_mode = 'V';
+ else if (*p_sel == 'e')
+ {
+ if (LT_POS(curwin->w_cursor, VIsual))
+ ++VIsual.col;
+ else
+ ++curwin->w_cursor.col;
+ }
+ }
+ }
+
+ if (pos == NULL && (is_click || is_drag))
+ {
+ // When not found a match or when dragging: extend to include
+ // a word.
+ if (LT_POS(curwin->w_cursor, orig_cursor))
+ {
+ find_start_of_word(&curwin->w_cursor);
+ find_end_of_word(&VIsual);
+ }
+ else
+ {
+ find_start_of_word(&VIsual);
+ if (*p_sel == 'e' && *ml_get_cursor() != NUL)
+ curwin->w_cursor.col +=
+ (*mb_ptr2len)(ml_get_cursor());
+ find_end_of_word(&curwin->w_cursor);
+ }
+ }
+ curwin->w_set_curswant = TRUE;
+ }
+ if (is_click)
+ redraw_curbuf_later(INVERTED); // update the inversion
+ }
+ else if (VIsual_active && !old_active)
+ {
+ if (mod_mask & MOD_MASK_ALT)
+ VIsual_mode = Ctrl_V;
+ else
+ VIsual_mode = 'v';
+ }
+
+ // If Visual mode changed show it later.
+ if ((!VIsual_active && old_active && mode_displayed)
+ || (VIsual_active && p_smd && msg_silent == 0
+ && (!old_active || VIsual_mode != old_mode)))
+ redraw_cmdline = TRUE;
+
+ return moved;
+}
+
+ void
+ins_mouse(int c)
+{
+ pos_T tpos;
+ win_T *old_curwin = curwin;
+
+# ifdef FEAT_GUI
+ // When GUI is active, also move/paste when 'mouse' is empty
+ if (!gui.in_use)
+# endif
+ if (!mouse_has(MOUSE_INSERT))
+ return;
+
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (do_mouse(NULL, c, BACKWARD, 1L, 0))
+ {
+ win_T *new_curwin = curwin;
+
+ if (curwin != old_curwin && win_valid(old_curwin))
+ {
+ // Mouse took us to another window. We need to go back to the
+ // previous one to stop insert there properly.
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+#ifdef FEAT_JOB_CHANNEL
+ if (bt_prompt(curbuf))
+ // Restart Insert mode when re-entering the prompt buffer.
+ curbuf->b_prompt_insert = 'A';
+#endif
+ }
+ start_arrow(curwin == old_curwin ? &tpos : NULL);
+ if (curwin != new_curwin && win_valid(new_curwin))
+ {
+ curwin = new_curwin;
+ curbuf = curwin->w_buffer;
+ }
+# ifdef FEAT_CINDENT
+ set_can_cindent(TRUE);
+# endif
+ }
+
+ // redraw status lines (in case another window became active)
+ redraw_statuslines();
+}
+
+ void
+ins_mousescroll(int dir)
+{
+ pos_T tpos;
+ win_T *old_curwin = curwin, *wp;
+ int did_scroll = FALSE;
+
+ tpos = curwin->w_cursor;
+
+ if (mouse_row >= 0 && mouse_col >= 0)
+ {
+ int row, col;
+
+ row = mouse_row;
+ col = mouse_col;
+
+ // find the window at the pointer coordinates
+ wp = mouse_find_win(&row, &col, FIND_POPUP);
+ if (wp == NULL)
+ return;
+ curwin = wp;
+ curbuf = curwin->w_buffer;
+ }
+ if (curwin == old_curwin)
+ undisplay_dollar();
+
+ // Don't scroll the window in which completion is being done.
+ if (!pum_visible() || curwin != old_curwin)
+ {
+ if (dir == MSCR_DOWN || dir == MSCR_UP)
+ {
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
+ scroll_redraw(dir,
+ (long)(curwin->w_botline - curwin->w_topline));
+ else
+ scroll_redraw(dir, 3L);
+# ifdef FEAT_TEXT_PROP
+ if (WIN_IS_POPUP(curwin))
+ popup_set_firstline(curwin);
+# endif
+ }
+#ifdef FEAT_GUI
+ else
+ {
+ int val, step = 6;
+
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
+ step = curwin->w_width;
+ val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
+ if (val < 0)
+ val = 0;
+ gui_do_horiz_scroll(val, TRUE);
+ }
+#endif
+ did_scroll = TRUE;
+ }
+
+ curwin->w_redr_status = TRUE;
+
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+
+ // The popup menu may overlay the window, need to redraw it.
+ // TODO: Would be more efficient to only redraw the windows that are
+ // overlapped by the popup menu.
+ if (pum_visible() && did_scroll)
+ {
+ redraw_all_later(NOT_VALID);
+ ins_compl_show_pum();
+ }
+
+ if (!EQUAL_POS(curwin->w_cursor, tpos))
+ {
+ start_arrow(&tpos);
+# ifdef FEAT_CINDENT
+ set_can_cindent(TRUE);
+# endif
+ }
+}
+
+/*
+ * Return TRUE if "c" is a mouse key.
+ */
+ int
+is_mouse_key(int c)
+{
+ return c == K_LEFTMOUSE
+ || c == K_LEFTMOUSE_NM
+ || c == K_LEFTDRAG
+ || c == K_LEFTRELEASE
+ || c == K_LEFTRELEASE_NM
+ || c == K_MOUSEMOVE
+ || c == K_MIDDLEMOUSE
+ || c == K_MIDDLEDRAG
+ || c == K_MIDDLERELEASE
+ || c == K_RIGHTMOUSE
+ || c == K_RIGHTDRAG
+ || c == K_RIGHTRELEASE
+ || c == K_MOUSEDOWN
+ || c == K_MOUSEUP
+ || c == K_MOUSELEFT
+ || c == K_MOUSERIGHT
+ || c == K_X1MOUSE
+ || c == K_X1DRAG
+ || c == K_X1RELEASE
+ || c == K_X2MOUSE
+ || c == K_X2DRAG
+ || c == K_X2RELEASE;
+}
+
+static struct mousetable
+{
+ int pseudo_code; // Code for pseudo mouse event
+ int button; // Which mouse button is it?
+ int is_click; // Is it a mouse button click event?
+ int is_drag; // Is it a mouse drag event?
+} mouse_table[] =
+{
+ {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
+#ifdef FEAT_GUI
+ {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
+#endif
+ {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
+ {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
+#ifdef FEAT_GUI
+ {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
+#endif
+ {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
+ {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
+ {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
+ {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
+ {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
+ {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
+ {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
+ {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
+ {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
+ {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
+ {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
+ {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
+ // DRAG without CLICK
+ {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
+ // RELEASE without CLICK
+ {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
+ {0, 0, 0, 0},
+};
+
+/*
+ * Look up the given mouse code to return the relevant information in the other
+ * arguments. Return which button is down or was released.
+ */
+ int
+get_mouse_button(int code, int *is_click, int *is_drag)
+{
+ int i;
+
+ for (i = 0; mouse_table[i].pseudo_code; i++)
+ if (code == mouse_table[i].pseudo_code)
+ {
+ *is_click = mouse_table[i].is_click;
+ *is_drag = mouse_table[i].is_drag;
+ return mouse_table[i].button;
+ }
+ return 0; // Shouldn't get here
+}
+
+/*
+ * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
+ * the given information about which mouse button is down, and whether the
+ * mouse was clicked, dragged or released.
+ */
+ int
+get_pseudo_mouse_code(
+ int button, // eg MOUSE_LEFT
+ int is_click,
+ int is_drag)
+{
+ int i;
+
+ for (i = 0; mouse_table[i].pseudo_code; i++)
+ if (button == mouse_table[i].button
+ && is_click == mouse_table[i].is_click
+ && is_drag == mouse_table[i].is_drag)
+ {
+#ifdef FEAT_GUI
+ // Trick: a non mappable left click and release has mouse_col -1
+ // or added MOUSE_COLOFF. Used for 'mousefocus' in
+ // gui_mouse_moved()
+ if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
+ {
+ if (mouse_col < 0)
+ mouse_col = 0;
+ else
+ mouse_col -= MOUSE_COLOFF;
+ if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
+ return (int)KE_LEFTMOUSE_NM;
+ if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
+ return (int)KE_LEFTRELEASE_NM;
+ }
+#endif
+ return mouse_table[i].pseudo_code;
+ }
+ return (int)KE_IGNORE; // not recognized, ignore it
+}
+
+# ifdef FEAT_MOUSE_TTY
+# define HMT_NORMAL 1
+# define HMT_NETTERM 2
+# define HMT_DEC 4
+# define HMT_JSBTERM 8
+# define HMT_PTERM 16
+# define HMT_URXVT 32
+# define HMT_GPM 64
+# define HMT_SGR 128
+# define HMT_SGR_REL 256
+static int has_mouse_termcode = 0;
+# endif
+
+# if (!defined(UNIX) || defined(FEAT_MOUSE_TTY)) || defined(PROTO)
+ void
+set_mouse_termcode(
+ int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
+ char_u *s)
+{
+ char_u name[2];
+
+ name[0] = n;
+ name[1] = KE_FILLER;
+ add_termcode(name, s, FALSE);
+# ifdef FEAT_MOUSE_TTY
+# ifdef FEAT_MOUSE_JSB
+ if (n == KS_JSBTERM_MOUSE)
+ has_mouse_termcode |= HMT_JSBTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_NET
+ if (n == KS_NETTERM_MOUSE)
+ has_mouse_termcode |= HMT_NETTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_DEC
+ if (n == KS_DEC_MOUSE)
+ has_mouse_termcode |= HMT_DEC;
+ else
+# endif
+# ifdef FEAT_MOUSE_PTERM
+ if (n == KS_PTERM_MOUSE)
+ has_mouse_termcode |= HMT_PTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_URXVT
+ if (n == KS_URXVT_MOUSE)
+ has_mouse_termcode |= HMT_URXVT;
+ else
+# endif
+# ifdef FEAT_MOUSE_GPM
+ if (n == KS_GPM_MOUSE)
+ has_mouse_termcode |= HMT_GPM;
+ else
+# endif
+ if (n == KS_SGR_MOUSE)
+ has_mouse_termcode |= HMT_SGR;
+ else if (n == KS_SGR_MOUSE_RELEASE)
+ has_mouse_termcode |= HMT_SGR_REL;
+ else
+ has_mouse_termcode |= HMT_NORMAL;
+# endif
+}
+# endif
+
+# if ((defined(UNIX) || defined(VMS)) \
+ && defined(FEAT_MOUSE_TTY)) || defined(PROTO)
+ void
+del_mouse_termcode(
+ int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
+{
+ char_u name[2];
+
+ name[0] = n;
+ name[1] = KE_FILLER;
+ del_termcode(name);
+# ifdef FEAT_MOUSE_TTY
+# ifdef FEAT_MOUSE_JSB
+ if (n == KS_JSBTERM_MOUSE)
+ has_mouse_termcode &= ~HMT_JSBTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_NET
+ if (n == KS_NETTERM_MOUSE)
+ has_mouse_termcode &= ~HMT_NETTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_DEC
+ if (n == KS_DEC_MOUSE)
+ has_mouse_termcode &= ~HMT_DEC;
+ else
+# endif
+# ifdef FEAT_MOUSE_PTERM
+ if (n == KS_PTERM_MOUSE)
+ has_mouse_termcode &= ~HMT_PTERM;
+ else
+# endif
+# ifdef FEAT_MOUSE_URXVT
+ if (n == KS_URXVT_MOUSE)
+ has_mouse_termcode &= ~HMT_URXVT;
+ else
+# endif
+# ifdef FEAT_MOUSE_GPM
+ if (n == KS_GPM_MOUSE)
+ has_mouse_termcode &= ~HMT_GPM;
+ else
+# endif
+ if (n == KS_SGR_MOUSE)
+ has_mouse_termcode &= ~HMT_SGR;
+ else if (n == KS_SGR_MOUSE_RELEASE)
+ has_mouse_termcode &= ~HMT_SGR_REL;
+ else
+ has_mouse_termcode &= ~HMT_NORMAL;
+# endif
+}
+# endif
+
+/*
+ * setmouse() - switch mouse on/off depending on current mode and 'mouse'
+ */
+ void
+setmouse(void)
+{
+# ifdef FEAT_MOUSE_TTY
+ int checkfor;
+# endif
+
+# ifdef FEAT_MOUSESHAPE
+ update_mouseshape(-1);
+# endif
+
+# ifdef FEAT_MOUSE_TTY // Should be outside proc, but may break MOUSESHAPE
+# ifdef FEAT_GUI
+ // In the GUI the mouse is always enabled.
+ if (gui.in_use)
+ return;
+# endif
+ // be quick when mouse is off
+ if (*p_mouse == NUL || has_mouse_termcode == 0)
+ return;
+
+ // don't switch mouse on when not in raw mode (Ex mode)
+ if (cur_tmode != TMODE_RAW)
+ {
+ mch_setmouse(FALSE);
+ return;
+ }
+
+ if (VIsual_active)
+ checkfor = MOUSE_VISUAL;
+ else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE)
+ checkfor = MOUSE_RETURN;
+ else if (State & INSERT)
+ checkfor = MOUSE_INSERT;
+ else if (State & CMDLINE)
+ checkfor = MOUSE_COMMAND;
+ else if (State == CONFIRM || State == EXTERNCMD)
+ checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
+ else
+ checkfor = MOUSE_NORMAL; // assume normal mode
+
+ if (mouse_has(checkfor))
+ mch_setmouse(TRUE);
+ else
+ mch_setmouse(FALSE);
+# endif
+}
+
+/*
+ * Return TRUE if
+ * - "c" is in 'mouse', or
+ * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
+ * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
+ * normal editing mode (not at hit-return message).
+ */
+ int
+mouse_has(int c)
+{
+ char_u *p;
+
+ for (p = p_mouse; *p; ++p)
+ switch (*p)
+ {
+ case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
+ return TRUE;
+ break;
+ case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
+ return TRUE;
+ break;
+ default: if (c == *p) return TRUE; break;
+ }
+ return FALSE;
+}
+
+/*
+ * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
+ */
+ int
+mouse_model_popup(void)
+{
+ return (p_mousem[0] == 'p');
+}
+
+/*
+ * Move the cursor to the specified row and column on the screen.
+ * Change current window if necessary. Returns an integer with the
+ * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
+ *
+ * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
+ * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
+ *
+ * If flags has MOUSE_FOCUS, then the current window will not be changed, and
+ * if the mouse is outside the window then the text will scroll, or if the
+ * mouse was previously on a status line, then the status line may be dragged.
+ *
+ * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
+ * cursor is moved unless the cursor was on a status line.
+ * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
+ * IN_SEP_LINE depending on where the cursor was clicked.
+ *
+ * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
+ * the mouse is on the status line of the same window.
+ *
+ * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
+ * the last call.
+ *
+ * If flags has MOUSE_SETPOS, nothing is done, only the current position is
+ * remembered.
+ */
+ int
+jump_to_mouse(
+ int flags,
+ int *inclusive, // used for inclusive operator, can be NULL
+ int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
+{
+ static int on_status_line = 0; // #lines below bottom of window
+ static int on_sep_line = 0; // on separator right of window
+#ifdef FEAT_MENU
+ static int in_winbar = FALSE;
+#endif
+#ifdef FEAT_TEXT_PROP
+ static int in_popup_win = FALSE;
+ static win_T *click_in_popup_win = NULL;
+#endif
+ static int prev_row = -1;
+ static int prev_col = -1;
+ static win_T *dragwin = NULL; // window being dragged
+ static int did_drag = FALSE; // drag was noticed
+
+ win_T *wp, *old_curwin;
+ pos_T old_cursor;
+ int count;
+ int first;
+ int row = mouse_row;
+ int col = mouse_col;
+#ifdef FEAT_FOLDING
+ int mouse_char;
+#endif
+
+ mouse_past_bottom = FALSE;
+ mouse_past_eol = FALSE;
+
+ if (flags & MOUSE_RELEASED)
+ {
+ // On button release we may change window focus if positioned on a
+ // status line and no dragging happened.
+ if (dragwin != NULL && !did_drag)
+ flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
+ dragwin = NULL;
+ did_drag = FALSE;
+#ifdef FEAT_TEXT_PROP
+ if (click_in_popup_win != NULL && popup_dragwin == NULL)
+ popup_close_for_mouse_click(click_in_popup_win);
+
+ popup_dragwin = NULL;
+ click_in_popup_win = NULL;
+#endif
+ }
+
+ if ((flags & MOUSE_DID_MOVE)
+ && prev_row == mouse_row
+ && prev_col == mouse_col)
+ {
+retnomove:
+ // before moving the cursor for a left click which is NOT in a status
+ // line, stop Visual mode
+ if (on_status_line)
+ return IN_STATUS_LINE;
+ if (on_sep_line)
+ return IN_SEP_LINE;
+#ifdef FEAT_MENU
+ if (in_winbar)
+ {
+ // A quick second click may arrive as a double-click, but we use it
+ // as a second click in the WinBar.
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
+ {
+ wp = mouse_find_win(&row, &col, FAIL_POPUP);
+ if (wp == NULL)
+ return IN_UNKNOWN;
+ winbar_click(wp, col);
+ }
+ return IN_OTHER_WIN | MOUSE_WINBAR;
+ }
+#endif
+ if (flags & MOUSE_MAY_STOP_VIS)
+ {
+ end_visual_mode();
+ redraw_curbuf_later(INVERTED); // delete the inversion
+ }
+#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
+ // Continue a modeless selection in another window.
+ if (cmdwin_type != 0 && row < curwin->w_winrow)
+ return IN_OTHER_WIN;
+#endif
+#ifdef FEAT_TEXT_PROP
+ // Continue a modeless selection in a popup window or dragging it.
+ if (in_popup_win)
+ {
+ click_in_popup_win = NULL; // don't close it on release
+ if (popup_dragwin != NULL)
+ {
+ // dragging a popup window
+ popup_drag(popup_dragwin);
+ return IN_UNKNOWN;
+ }
+ return IN_OTHER_WIN;
+ }
+#endif
+ return IN_BUFFER;
+ }
+
+ prev_row = mouse_row;
+ prev_col = mouse_col;
+
+ if (flags & MOUSE_SETPOS)
+ goto retnomove; // ugly goto...
+
+#ifdef FEAT_FOLDING
+ // Remember the character under the mouse, it might be a '-' or '+' in the
+ // fold column.
+ if (row >= 0 && row < Rows && col >= 0 && col <= Columns
+ && ScreenLines != NULL)
+ mouse_char = ScreenLines[LineOffset[row] + col];
+ else
+ mouse_char = ' ';
+#endif
+
+ old_curwin = curwin;
+ old_cursor = curwin->w_cursor;
+
+ if (!(flags & MOUSE_FOCUS))
+ {
+ if (row < 0 || col < 0) // check if it makes sense
+ return IN_UNKNOWN;
+
+ // find the window where the row is in and adjust "row" and "col" to be
+ // relative to top-left of the window
+ wp = mouse_find_win(&row, &col, FIND_POPUP);
+ if (wp == NULL)
+ return IN_UNKNOWN;
+ dragwin = NULL;
+
+#ifdef FEAT_TEXT_PROP
+ // Click in a popup window may start dragging or modeless selection,
+ // but not much else.
+ if (WIN_IS_POPUP(wp))
+ {
+ on_sep_line = 0;
+ in_popup_win = TRUE;
+ if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
+ {
+ return IN_UNKNOWN;
+ }
+ else if ((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
+ && popup_on_border(wp, row, col))
+ {
+ popup_dragwin = wp;
+ popup_start_drag(wp, row, col);
+ return IN_UNKNOWN;
+ }
+ // Only close on release, otherwise it's not possible to drag or do
+ // modeless selection.
+ else if (wp->w_popup_close == POPCLOSE_CLICK
+ && which_button == MOUSE_LEFT)
+ {
+ click_in_popup_win = wp;
+ }
+ else if (which_button == MOUSE_LEFT)
+ // If the click is in the scrollbar, may scroll up/down.
+ popup_handle_scrollbar_click(wp, row, col);
+# ifdef FEAT_CLIPBOARD
+ return IN_OTHER_WIN;
+# else
+ return IN_UNKNOWN;
+# endif
+ }
+ in_popup_win = FALSE;
+ popup_dragwin = NULL;
+#endif
+#ifdef FEAT_MENU
+ if (row == -1)
+ {
+ // A click in the window toolbar does not enter another window or
+ // change Visual highlighting.
+ winbar_click(wp, col);
+ in_winbar = TRUE;
+ return IN_OTHER_WIN | MOUSE_WINBAR;
+ }
+ in_winbar = FALSE;
+#endif
+
+ // winpos and height may change in win_enter()!
+ if (row >= wp->w_height) // In (or below) status line
+ {
+ on_status_line = row - wp->w_height + 1;
+ dragwin = wp;
+ }
+ else
+ on_status_line = 0;
+ if (col >= wp->w_width) // In separator line
+ {
+ on_sep_line = col - wp->w_width + 1;
+ dragwin = wp;
+ }
+ else
+ on_sep_line = 0;
+
+ // The rightmost character of the status line might be a vertical
+ // separator character if there is no connecting window to the right.
+ if (on_status_line && on_sep_line)
+ {
+ if (stl_connected(wp))
+ on_sep_line = 0;
+ else
+ on_status_line = 0;
+ }
+
+ // Before jumping to another buffer, or moving the cursor for a left
+ // click, stop Visual mode.
+ if (VIsual_active
+ && (wp->w_buffer != curwin->w_buffer
+ || (!on_status_line && !on_sep_line
+#ifdef FEAT_FOLDING
+ && (
+# ifdef FEAT_RIGHTLEFT
+ wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
+# endif
+ col >= wp->w_p_fdc
+# ifdef FEAT_CMDWIN
+ + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
+# endif
+ )
+#endif
+ && (flags & MOUSE_MAY_STOP_VIS))))
+ {
+ end_visual_mode();
+ redraw_curbuf_later(INVERTED); // delete the inversion
+ }
+#ifdef FEAT_CMDWIN
+ if (cmdwin_type != 0 && wp != curwin)
+ {
+ // A click outside the command-line window: Use modeless
+ // selection if possible. Allow dragging the status lines.
+ on_sep_line = 0;
+# ifdef FEAT_CLIPBOARD
+ if (on_status_line)
+ return IN_STATUS_LINE;
+ return IN_OTHER_WIN;
+# else
+ row = 0;
+ col += wp->w_wincol;
+ wp = curwin;
+# endif
+ }
+#endif
+ // Only change window focus when not clicking on or dragging the
+ // status line. Do change focus when releasing the mouse button
+ // (MOUSE_FOCUS was set above if we dragged first).
+ if (dragwin == NULL || (flags & MOUSE_RELEASED))
+ win_enter(wp, TRUE); // can make wp invalid!
+
+ if (curwin != old_curwin)
+ {
+#ifdef CHECK_DOUBLE_CLICK
+ // set topline, to be able to check for double click ourselves
+ set_mouse_topline(curwin);
+#endif
+#ifdef FEAT_TERMINAL
+ // when entering a terminal window may change state
+ term_win_entered();
+#endif
+ }
+ if (on_status_line) // In (or below) status line
+ {
+ // Don't use start_arrow() if we're in the same window
+ if (curwin == old_curwin)
+ return IN_STATUS_LINE;
+ else
+ return IN_STATUS_LINE | CURSOR_MOVED;
+ }
+ if (on_sep_line) // In (or below) status line
+ {
+ // Don't use start_arrow() if we're in the same window
+ if (curwin == old_curwin)
+ return IN_SEP_LINE;
+ else
+ return IN_SEP_LINE | CURSOR_MOVED;
+ }
+
+ curwin->w_cursor.lnum = curwin->w_topline;
+#ifdef FEAT_GUI
+ // remember topline, needed for double click
+ gui_prev_topline = curwin->w_topline;
+# ifdef FEAT_DIFF
+ gui_prev_topfill = curwin->w_topfill;
+# endif
+#endif
+ }
+ else if (on_status_line && which_button == MOUSE_LEFT)
+ {
+ if (dragwin != NULL)
+ {
+ // Drag the status line
+ count = row - dragwin->w_winrow - dragwin->w_height + 1
+ - on_status_line;
+ win_drag_status_line(dragwin, count);
+ did_drag |= count;
+ }
+ return IN_STATUS_LINE; // Cursor didn't move
+ }
+ else if (on_sep_line && which_button == MOUSE_LEFT)
+ {
+ if (dragwin != NULL)
+ {
+ // Drag the separator column
+ count = col - dragwin->w_wincol - dragwin->w_width + 1
+ - on_sep_line;
+ win_drag_vsep_line(dragwin, count);
+ did_drag |= count;
+ }
+ return IN_SEP_LINE; // Cursor didn't move
+ }
+#ifdef FEAT_MENU
+ else if (in_winbar)
+ {
+ // After a click on the window toolbar don't start Visual mode.
+ return IN_OTHER_WIN | MOUSE_WINBAR;
+ }
+#endif
+ else // keep_window_focus must be TRUE
+ {
+ // before moving the cursor for a left click, stop Visual mode
+ if (flags & MOUSE_MAY_STOP_VIS)
+ {
+ end_visual_mode();
+ redraw_curbuf_later(INVERTED); // delete the inversion
+ }
+
+#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
+ // Continue a modeless selection in another window.
+ if (cmdwin_type != 0 && row < curwin->w_winrow)
+ return IN_OTHER_WIN;
+#endif
+#ifdef FEAT_TEXT_PROP
+ if (in_popup_win)
+ {
+ if (popup_dragwin != NULL)
+ {
+ // dragging a popup window
+ popup_drag(popup_dragwin);
+ return IN_UNKNOWN;
+ }
+ // continue a modeless selection in a popup window
+ click_in_popup_win = NULL;
+ return IN_OTHER_WIN;
+ }
+#endif
+
+ row -= W_WINROW(curwin);
+ col -= curwin->w_wincol;
+
+ // When clicking beyond the end of the window, scroll the screen.
+ // Scroll by however many rows outside the window we are.
+ if (row < 0)
+ {
+ count = 0;
+ for (first = TRUE; curwin->w_topline > 1; )
+ {
+#ifdef FEAT_DIFF
+ if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
+ ++count;
+ else
+#endif
+ count += plines(curwin->w_topline - 1);
+ if (!first && count > -row)
+ break;
+ first = FALSE;
+#ifdef FEAT_FOLDING
+ (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
+#endif
+#ifdef FEAT_DIFF
+ if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
+ ++curwin->w_topfill;
+ else
+#endif
+ {
+ --curwin->w_topline;
+#ifdef FEAT_DIFF
+ curwin->w_topfill = 0;
+#endif
+ }
+ }
+#ifdef FEAT_DIFF
+ check_topfill(curwin, FALSE);
+#endif
+ curwin->w_valid &=
+ ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
+ redraw_later(VALID);
+ row = 0;
+ }
+ else if (row >= curwin->w_height)
+ {
+ count = 0;
+ for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
+ {
+#ifdef FEAT_DIFF
+ if (curwin->w_topfill > 0)
+ ++count;
+ else
+#endif
+ count += plines(curwin->w_topline);
+ if (!first && count > row - curwin->w_height + 1)
+ break;
+ first = FALSE;
+#ifdef FEAT_FOLDING
+ if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
+ && curwin->w_topline == curbuf->b_ml.ml_line_count)
+ break;
+#endif
+#ifdef FEAT_DIFF
+ if (curwin->w_topfill > 0)
+ --curwin->w_topfill;
+ else
+#endif
+ {
+ ++curwin->w_topline;
+#ifdef FEAT_DIFF
+ curwin->w_topfill =
+ diff_check_fill(curwin, curwin->w_topline);
+#endif
+ }
+ }
+#ifdef FEAT_DIFF
+ check_topfill(curwin, FALSE);
+#endif
+ redraw_later(VALID);
+ curwin->w_valid &=
+ ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
+ row = curwin->w_height - 1;
+ }
+ else if (row == 0)
+ {
+ // When dragging the mouse, while the text has been scrolled up as
+ // far as it goes, moving the mouse in the top line should scroll
+ // the text down (done later when recomputing w_topline).
+ if (mouse_dragging > 0
+ && curwin->w_cursor.lnum
+ == curwin->w_buffer->b_ml.ml_line_count
+ && curwin->w_cursor.lnum == curwin->w_topline)
+ curwin->w_valid &= ~(VALID_TOPLINE);
+ }
+ }
+
+#ifdef FEAT_FOLDING
+ // Check for position outside of the fold column.
+ if (
+# ifdef FEAT_RIGHTLEFT
+ curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
+# endif
+ col >= curwin->w_p_fdc
+# ifdef FEAT_CMDWIN
+ + (cmdwin_type == 0 ? 0 : 1)
+# endif
+ )
+ mouse_char = ' ';
+#endif
+
+ // compute the position in the buffer line from the posn on the screen
+ if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
+ mouse_past_bottom = TRUE;
+
+ // Start Visual mode before coladvance(), for when 'sel' != "old"
+ if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
+ {
+ check_visual_highlight();
+ VIsual = old_cursor;
+ VIsual_active = TRUE;
+ VIsual_reselect = TRUE;
+ // if 'selectmode' contains "mouse", start Select mode
+ may_start_select('o');
+ setmouse();
+ if (p_smd && msg_silent == 0)
+ redraw_cmdline = TRUE; // show visual mode later
+ }
+
+ curwin->w_curswant = col;
+ curwin->w_set_curswant = FALSE; // May still have been TRUE
+ if (coladvance(col) == FAIL) // Mouse click beyond end of line
+ {
+ if (inclusive != NULL)
+ *inclusive = TRUE;
+ mouse_past_eol = TRUE;
+ }
+ else if (inclusive != NULL)
+ *inclusive = FALSE;
+
+ count = IN_BUFFER;
+ if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
+ || curwin->w_cursor.col != old_cursor.col)
+ count |= CURSOR_MOVED; // Cursor has moved
+
+# ifdef FEAT_FOLDING
+ if (mouse_char == '+')
+ count |= MOUSE_FOLD_OPEN;
+ else if (mouse_char != ' ')
+ count |= MOUSE_FOLD_CLOSE;
+# endif
+
+ return count;
+}
+
+/*
+ * Mouse scroll wheel: Default action is to scroll three lines, or one page
+ * when Shift or Ctrl is used.
+ * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
+ * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
+ */
+ void
+nv_mousescroll(cmdarg_T *cap)
+{
+ win_T *old_curwin = curwin, *wp;
+
+ if (mouse_row >= 0 && mouse_col >= 0)
+ {
+ int row, col;
+
+ row = mouse_row;
+ col = mouse_col;
+
+ // find the window at the pointer coordinates
+ wp = mouse_find_win(&row, &col, FIND_POPUP);
+ if (wp == NULL)
+ return;
+#ifdef FEAT_TEXT_PROP
+ if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
+ return;
+#endif
+ curwin = wp;
+ curbuf = curwin->w_buffer;
+ }
+
+ if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
+ {
+# ifdef FEAT_TERMINAL
+ if (term_use_loop())
+ // This window is a terminal window, send the mouse event there.
+ // Set "typed" to FALSE to avoid an endless loop.
+ send_keys_to_term(curbuf->b_term, cap->cmdchar, FALSE);
+ else
+# endif
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
+ {
+ (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
+ }
+ else
+ {
+ // Don't scroll more than half the window height.
+ if (curwin->w_height < 6)
+ {
+ cap->count1 = curwin->w_height / 2;
+ if (cap->count1 == 0)
+ cap->count1 = 1;
+ }
+ else
+ cap->count1 = 3;
+ cap->count0 = cap->count1;
+ nv_scroll_line(cap);
+ }
+#ifdef FEAT_TEXT_PROP
+ if (WIN_IS_POPUP(curwin))
+ popup_set_firstline(curwin);
+#endif
+ }
+# ifdef FEAT_GUI
+ else
+ {
+ // Horizontal scroll - only allowed when 'wrap' is disabled
+ if (!curwin->w_p_wrap)
+ {
+ int val, step = 6;
+
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
+ step = curwin->w_width;
+ val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
+ if (val < 0)
+ val = 0;
+
+ gui_do_horiz_scroll(val, TRUE);
+ }
+ }
+# endif
+# ifdef FEAT_SYN_HL
+ if (curwin != old_curwin && curwin->w_p_cul)
+ redraw_for_cursorline(curwin);
+# endif
+
+ curwin->w_redr_status = TRUE;
+
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+}
+
+/*
+ * Mouse clicks and drags.
+ */
+ void
+nv_mouse(cmdarg_T *cap)
+{
+ (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
+}
+#endif // FEAT_MOUSE
+
+// Functions also used for popup windows.
+#if defined(FEAT_MOUSE) || defined(FEAT_TEXT_PROP) || defined(PROTO)
+
+/*
+ * Compute the buffer line position from the screen position "rowp" / "colp" in
+ * window "win".
+ * "plines_cache" can be NULL (no cache) or an array with "win->w_height"
+ * entries that caches the plines_win() result from a previous call. Entry is
+ * zero if not computed yet. There must be no text or setting changes since
+ * the entry is put in the cache.
+ * Returns TRUE if the position is below the last line.
+ */
+ int
+mouse_comp_pos(
+ win_T *win,
+ int *rowp,
+ int *colp,
+ linenr_T *lnump,
+ int *plines_cache)
+{
+ int col = *colp;
+ int row = *rowp;
+ linenr_T lnum;
+ int retval = FALSE;
+ int off;
+ int count;
+
+#ifdef FEAT_RIGHTLEFT
+ if (win->w_p_rl)
+ col = win->w_width - 1 - col;
+#endif
+
+ lnum = win->w_topline;
+
+ while (row > 0)
+ {
+ int cache_idx = lnum - win->w_topline;
+
+ if (plines_cache != NULL && plines_cache[cache_idx] > 0)
+ count = plines_cache[cache_idx];
+ else
+ {
+#ifdef FEAT_DIFF
+ // Don't include filler lines in "count"
+ if (win->w_p_diff
+# ifdef FEAT_FOLDING
+ && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
+# endif
+ )
+ {
+ if (lnum == win->w_topline)
+ row -= win->w_topfill;
+ else
+ row -= diff_check_fill(win, lnum);
+ count = plines_win_nofill(win, lnum, TRUE);
+ }
+ else
+#endif
+ count = plines_win(win, lnum, TRUE);
+ if (plines_cache != NULL)
+ plines_cache[cache_idx] = count;
+ }
+ if (count > row)
+ break; // Position is in this buffer line.
+#ifdef FEAT_FOLDING
+ (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
+#endif
+ if (lnum == win->w_buffer->b_ml.ml_line_count)
+ {
+ retval = TRUE;
+ break; // past end of file
+ }
+ row -= count;
+ ++lnum;
+ }
+
+ if (!retval)
+ {
+ // Compute the column without wrapping.
+ off = win_col_off(win) - win_col_off2(win);
+ if (col < off)
+ col = off;
+ col += row * (win->w_width - off);
+ // add skip column (for long wrapping line)
+ col += win->w_skipcol;
+ }
+
+ if (!win->w_p_wrap)
+ col += win->w_leftcol;
+
+ // skip line number and fold column in front of the line
+ col -= win_col_off(win);
+ if (col < 0)
+ {
+#ifdef FEAT_NETBEANS_INTG
+ netbeans_gutter_click(lnum);
+#endif
+ col = 0;
+ }
+
+ *colp = col;
+ *rowp = row;
+ *lnump = lnum;
+ return retval;
+}
+
+/*
+ * Find the window at screen position "*rowp" and "*colp". The positions are
+ * updated to become relative to the top-left of the window.
+ * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
+ * is returned. When "popup" is IGNORE_POPUP then do not even check popup
+ * windows.
+ * Returns NULL when something is wrong.
+ */
+ win_T *
+mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
+{
+ frame_T *fp;
+ win_T *wp;
+
+#ifdef FEAT_TEXT_PROP
+ win_T *pwp = NULL;
+
+ if (popup != IGNORE_POPUP)
+ {
+ popup_reset_handled();
+ while ((wp = find_next_popup(TRUE)) != NULL)
+ {
+ if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
+ && *colp >= wp->w_wincol
+ && *colp < wp->w_wincol + popup_width(wp))
+ pwp = wp;
+ }
+ if (pwp != NULL)
+ {
+ if (popup == FAIL_POPUP)
+ return NULL;
+ *rowp -= pwp->w_winrow;
+ *colp -= pwp->w_wincol;
+ return pwp;
+ }
+ }
+#endif
+
+ fp = topframe;
+ *rowp -= firstwin->w_winrow;
+ for (;;)
+ {
+ if (fp->fr_layout == FR_LEAF)
+ break;
+ if (fp->fr_layout == FR_ROW)
+ {
+ for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
+ {
+ if (*colp < fp->fr_width)
+ break;
+ *colp -= fp->fr_width;
+ }
+ }
+ else // fr_layout == FR_COL
+ {
+ for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
+ {
+ if (*rowp < fp->fr_height)
+ break;
+ *rowp -= fp->fr_height;
+ }
+ }
+ }
+ // When using a timer that closes a window the window might not actually
+ // exist.
+ FOR_ALL_WINDOWS(wp)
+ if (wp == fp->fr_win)
+ {
+#ifdef FEAT_MENU
+ *rowp -= wp->w_winbar_height;
+#endif
+ return wp;
+ }
+ return NULL;
+}
+
+#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MAC) \
+ || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
+ || defined(FEAT_GUI_PHOTON) || defined(FEAT_TERM_POPUP_MENU) \
+ || defined(PROTO)
+# define NEED_VCOL2COL
+
+/*
+ * Translate window coordinates to buffer position without any side effects
+ */
+ static int
+get_fpos_of_mouse(pos_T *mpos)
+{
+ win_T *wp;
+ int row = mouse_row;
+ int col = mouse_col;
+
+ if (row < 0 || col < 0) // check if it makes sense
+ return IN_UNKNOWN;
+
+ // find the window where the row is in
+ wp = mouse_find_win(&row, &col, FAIL_POPUP);
+ if (wp == NULL)
+ return IN_UNKNOWN;
+ // winpos and height may change in win_enter()!
+ if (row >= wp->w_height) // In (or below) status line
+ return IN_STATUS_LINE;
+ if (col >= wp->w_width) // In vertical separator line
+ return IN_SEP_LINE;
+
+ if (wp != curwin)
+ return IN_UNKNOWN;
+
+ // compute the position in the buffer line from the posn on the screen
+ if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
+ return IN_STATUS_LINE; // past bottom
+
+ mpos->col = vcol2col(wp, mpos->lnum, col);
+
+ if (mpos->col > 0)
+ --mpos->col;
+ mpos->coladd = 0;
+ return IN_BUFFER;
+}
+#endif
+
+#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) \
+ || defined(PROTO)
+/*
+ * Convert a virtual (screen) column to a character column.
+ * The first column is one.
+ */
+ int
+vcol2col(win_T *wp, linenr_T lnum, int vcol)
+{
+ // try to advance to the specified column
+ int count = 0;
+ char_u *ptr;
+ char_u *line;
+
+ line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
+ while (count < vcol && *ptr != NUL)
+ {
+ count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
+ MB_PTR_ADV(ptr);
+ }
+ return (int)(ptr - line);
+}
+#endif
+
+#else // FEAT_MOUSE
+
+/*
+ * Dummy implementation of setmouse() to avoid lots of #ifdefs.
+ */
+ void
+setmouse(void)
+{
+}
+
+#endif // FEAT_MOUSE