mirror of
https://github.com/vim/vim
synced 2025-03-31 15:06:43 +02:00
Problem: Divide by zero with getmousepos() and 'smoothscroll'. Solution: Don't compute skip_lines when width1 is zero. (zeertzjq) closes: #14747 Signed-off-by: zeertzjq <zeertzjq@outlook.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
3267 lines
83 KiB
C
3267 lines
83 KiB
C
/* 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"
|
|
|
|
/*
|
|
* Horiziontal and vertical steps used when scrolling.
|
|
* When negative scroll by a whole page.
|
|
*/
|
|
static long mouse_hor_step = 6;
|
|
static long mouse_vert_step = 3;
|
|
|
|
void
|
|
mouse_set_vert_scroll_step(long step)
|
|
{
|
|
mouse_vert_step = step;
|
|
}
|
|
|
|
void
|
|
mouse_set_hor_scroll_step(long step)
|
|
{
|
|
mouse_hor_step = step;
|
|
}
|
|
|
|
#ifdef CHECK_DOUBLE_CLICK
|
|
/*
|
|
* Return the duration from t1 to t2 in milliseconds.
|
|
*/
|
|
static long
|
|
time_diff_ms(struct timeval *t1, struct timeval *t2)
|
|
{
|
|
// This handles wrapping of tv_usec correctly without any special case.
|
|
// Example of 2 pairs (tv_sec, tv_usec) with a duration of 5 ms:
|
|
// t1 = (1, 998000) t2 = (2, 3000) gives:
|
|
// (2 - 1) * 1000 + (3000 - 998000) / 1000 -> 5 ms.
|
|
return (t2->tv_sec - t1->tv_sec) * 1000
|
|
+ (t2->tv_usec - t1->tv_usec) / 1000;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
|
|
|| defined(FEAT_GUI_MSWIN) \
|
|
|| defined(FEAT_GUI_PHOTON) \
|
|
|| defined(FEAT_TERM_POPUP_MENU)
|
|
# define USE_POPUP_SETPOS
|
|
# define NEED_VCOL2COL
|
|
|
|
/*
|
|
* Translate window coordinates to buffer position without any side effects.
|
|
* Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
|
|
* The column is one for the first column.
|
|
*/
|
|
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, &mpos->coladd);
|
|
return IN_BUFFER;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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 == MODE_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.
|
|
// Note: Since characters added to the stuff buffer in the code
|
|
// below need to come before the next character, do not do this
|
|
// when the current character was stuffed.
|
|
if (!KeyStuffed && 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_PROP_POPUP
|
|
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 & MODE_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 == MODE_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 & MODE_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 & MODE_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, NULL, 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;
|
|
|
|
if (TabPageIdxs != NULL) // only when initialized
|
|
{
|
|
// 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 && cmdwin_type == 0 && 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_keep_button();
|
|
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_keep_button();
|
|
}
|
|
}
|
|
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)))
|
|
{
|
|
#ifdef USE_POPUP_SETPOS
|
|
# ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
{
|
|
# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
|
|
|| defined(FEAT_GUI_PHOTON)
|
|
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_MSWIN) || defined(FEAT_GUI_HAIKU)
|
|
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 (VIsual_mode == 'V')
|
|
{
|
|
if ((curwin->w_cursor.lnum <= VIsual.lnum
|
|
&& (m_pos.lnum < curwin->w_cursor.lnum
|
|
|| VIsual.lnum < m_pos.lnum))
|
|
|| (VIsual.lnum < curwin->w_cursor.lnum
|
|
&& (m_pos.lnum < VIsual.lnum
|
|
|| curwin->w_cursor.lnum < m_pos.lnum)))
|
|
{
|
|
jump_flags = MOUSE_MAY_STOP_VIS;
|
|
}
|
|
}
|
|
else if ((LTOREQ_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 ? UPD_INVERTED : UPD_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 & (MODE_NORMAL | MODE_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)
|
|
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 & MODE_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, NULL, 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 & MODE_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 & MODE_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 & (MODE_NORMAL | MODE_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(UPD_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;
|
|
}
|
|
set_can_cindent(TRUE);
|
|
}
|
|
|
|
// redraw status lines (in case another window became active)
|
|
redraw_statuslines();
|
|
}
|
|
|
|
/*
|
|
* Common mouse wheel scrolling, shared between Insert mode and NV modes.
|
|
* Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
|
|
* depending on the scroll direction) or one page when Shift or Ctrl is used.
|
|
* Direction is indicated by "cap->arg":
|
|
* K_MOUSEUP - MSCR_UP
|
|
* K_MOUSEDOWN - MSCR_DOWN
|
|
* K_MOUSELEFT - MSCR_LEFT
|
|
* K_MOUSERIGHT - MSCR_RIGHT
|
|
* "curwin" may have been changed to the window that should be scrolled and
|
|
* differ from the window that actually has focus.
|
|
*/
|
|
static void
|
|
do_mousescroll(cmdarg_T *cap)
|
|
{
|
|
int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
|
|
|
|
#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, mod_mask, FALSE);
|
|
else
|
|
#endif
|
|
if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
|
|
{
|
|
// Vertical scrolling
|
|
if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
|
|
{
|
|
// whole page up or down
|
|
pagescroll(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L, FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (mouse_vert_step < 0 || shift_or_ctrl)
|
|
{
|
|
// whole page up or down
|
|
cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
|
|
}
|
|
// Don't scroll more than half the window height.
|
|
else if (curwin->w_height < mouse_vert_step * 2)
|
|
{
|
|
cap->count1 = curwin->w_height / 2;
|
|
if (cap->count1 == 0)
|
|
cap->count1 = 1;
|
|
}
|
|
else
|
|
{
|
|
cap->count1 = mouse_vert_step;
|
|
}
|
|
cap->count0 = cap->count1;
|
|
nv_scroll_line(cap);
|
|
}
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
if (WIN_IS_POPUP(curwin))
|
|
popup_set_firstline(curwin);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Horizontal scrolling
|
|
long step = (mouse_hor_step < 0 || shift_or_ctrl)
|
|
? curwin->w_width : mouse_hor_step;
|
|
long leftcol = curwin->w_leftcol
|
|
+ (cap->arg == MSCR_RIGHT ? -step : step);
|
|
if (leftcol < 0)
|
|
leftcol = 0;
|
|
do_mousescroll_horiz((long_u)leftcol);
|
|
}
|
|
may_trigger_win_scrolled_resized();
|
|
}
|
|
|
|
/*
|
|
* Insert mode implementation for scrolling in direction "dir", which is
|
|
* one of the MSCR_ values.
|
|
*/
|
|
void
|
|
ins_mousescroll(int dir)
|
|
{
|
|
cmdarg_T cap;
|
|
oparg_T oa;
|
|
CLEAR_FIELD(cap);
|
|
clear_oparg(&oa);
|
|
cap.oap = &oa;
|
|
cap.arg = dir;
|
|
|
|
switch (dir)
|
|
{
|
|
case MSCR_UP:
|
|
cap.cmdchar = K_MOUSEUP;
|
|
break;
|
|
case MSCR_DOWN:
|
|
cap.cmdchar = K_MOUSEDOWN;
|
|
break;
|
|
case MSCR_LEFT:
|
|
cap.cmdchar = K_MOUSELEFT;
|
|
break;
|
|
case MSCR_RIGHT:
|
|
cap.cmdchar = K_MOUSERIGHT;
|
|
break;
|
|
default:
|
|
siemsg("Invalid ins_mousescroll() argument: %d", dir);
|
|
}
|
|
|
|
win_T *old_curwin = curwin;
|
|
if (mouse_row >= 0 && mouse_col >= 0)
|
|
{
|
|
// Find the window at the mouse pointer coordinates.
|
|
// NOTE: Must restore "curwin" to "old_curwin" before returning!
|
|
int row = mouse_row;
|
|
int col = mouse_col;
|
|
curwin = mouse_find_win(&row, &col, FIND_POPUP);
|
|
if (curwin == NULL)
|
|
{
|
|
curwin = old_curwin;
|
|
return;
|
|
}
|
|
curbuf = curwin->w_buffer;
|
|
}
|
|
|
|
if (curwin == old_curwin)
|
|
{
|
|
// Don't scroll the current window if the popup menu is visible.
|
|
if (pum_visible())
|
|
return;
|
|
|
|
undisplay_dollar();
|
|
}
|
|
|
|
linenr_T orig_topline = curwin->w_topline;
|
|
colnr_T orig_leftcol = curwin->w_leftcol;
|
|
pos_T orig_cursor = curwin->w_cursor;
|
|
|
|
// Call the common mouse scroll function shared with other modes.
|
|
do_mousescroll(&cap);
|
|
|
|
int did_scroll = (orig_topline != curwin->w_topline
|
|
|| orig_leftcol != curwin->w_leftcol);
|
|
|
|
curwin->w_redr_status = TRUE;
|
|
curwin = old_curwin;
|
|
curbuf = curwin->w_buffer;
|
|
|
|
// If the window actually scrolled and the popup menu may overlay the
|
|
// window, need to redraw it.
|
|
if (did_scroll && pum_visible())
|
|
{
|
|
// TODO: Would be more efficient to only redraw the windows that are
|
|
// overlapped by the popup menu.
|
|
redraw_all_later(UPD_NOT_VALID);
|
|
ins_compl_show_pum();
|
|
}
|
|
|
|
if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
|
|
{
|
|
start_arrow(&orig_cursor);
|
|
set_can_cindent(TRUE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
}
|
|
|
|
# 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;
|
|
|
|
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_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;
|
|
}
|
|
|
|
#if defined(UNIX) || defined(VMS) || 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_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
|
|
|
|
/*
|
|
* setmouse() - switch mouse on/off depending on current mode and 'mouse'
|
|
*/
|
|
void
|
|
setmouse(void)
|
|
{
|
|
int checkfor;
|
|
|
|
#ifdef FEAT_MOUSESHAPE
|
|
update_mouseshape(-1);
|
|
#endif
|
|
|
|
// 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 == MODE_HITRETURN || State == MODE_ASKMORE
|
|
|| State == MODE_SETWSIZE)
|
|
checkfor = MOUSE_RETURN;
|
|
else if (State & MODE_INSERT)
|
|
checkfor = MOUSE_INSERT;
|
|
else if (State & MODE_CMDLINE)
|
|
checkfor = MOUSE_COMMAND;
|
|
else if (State == MODE_CONFIRM || State == MODE_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);
|
|
}
|
|
|
|
/*
|
|
* 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');
|
|
}
|
|
|
|
static win_T *dragwin = NULL; // window being dragged
|
|
|
|
/*
|
|
* Reset the window being dragged. To be called when switching tab page.
|
|
*/
|
|
void
|
|
reset_dragwin(void)
|
|
{
|
|
dragwin = NULL;
|
|
}
|
|
|
|
/*
|
|
* 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_PROP_POPUP
|
|
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 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;
|
|
colnr_T col_from_screen = -1;
|
|
#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_PROP_POPUP
|
|
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_keep_button();
|
|
redraw_curbuf_later(UPD_INVERTED); // delete the inversion
|
|
}
|
|
#if defined(FEAT_CLIPBOARD)
|
|
// Continue a modeless selection in another window.
|
|
if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
|
|
return IN_OTHER_WIN;
|
|
#endif
|
|
#ifdef FEAT_PROP_POPUP
|
|
// 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...
|
|
|
|
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_PROP_POPUP
|
|
// Click in a popup window may start dragging or modeless selection,
|
|
// but not much else.
|
|
if (WIN_IS_POPUP(wp))
|
|
{
|
|
on_sep_line = 0;
|
|
on_status_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))
|
|
|| (wp->w_popup_flags & POPF_DRAGALL))
|
|
{
|
|
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 + (wp != cmdwin_win ? 0 : 1)
|
|
)
|
|
#endif
|
|
&& (flags & MOUSE_MAY_STOP_VIS))))
|
|
{
|
|
end_visual_mode_keep_button();
|
|
redraw_curbuf_later(UPD_INVERTED); // delete the inversion
|
|
}
|
|
if (cmdwin_type != 0 && wp != cmdwin_win)
|
|
{
|
|
// 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 = cmdwin_win;
|
|
#endif
|
|
}
|
|
#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
|
|
if (popup_is_popup(curwin) && curbuf->b_term != NULL)
|
|
// terminal in popup window: don't jump to another window
|
|
return IN_OTHER_WIN;
|
|
#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 - W_WINROW(dragwin) - 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_keep_button();
|
|
redraw_curbuf_later(UPD_INVERTED); // delete the inversion
|
|
}
|
|
|
|
#if defined(FEAT_CLIPBOARD)
|
|
// Continue a modeless selection in another window.
|
|
if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
|
|
return IN_OTHER_WIN;
|
|
#endif
|
|
#ifdef FEAT_PROP_POPUP
|
|
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(UPD_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(UPD_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);
|
|
}
|
|
}
|
|
|
|
if (prev_row >= W_WINROW(curwin)
|
|
&& prev_row < W_WINROW(curwin) + curwin->w_height
|
|
&& prev_col >= curwin->w_wincol && prev_col < W_ENDCOL(curwin)
|
|
&& ScreenLines != NULL)
|
|
{
|
|
int off = LineOffset[prev_row] + prev_col;
|
|
|
|
// Only use ScreenCols[] after the window was redrawn. Mainly matters
|
|
// for tests, a user would not click before redrawing.
|
|
// Do not use when 'virtualedit' is active.
|
|
if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE)
|
|
col_from_screen = ScreenCols[off];
|
|
#ifdef FEAT_FOLDING
|
|
// Remember the character under the mouse, it might be a '-' or '+' in
|
|
// the fold column.
|
|
mouse_char = ScreenLines[off];
|
|
#endif
|
|
}
|
|
|
|
#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 + (cmdwin_win != curwin ? 0 : 1)
|
|
)
|
|
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
|
|
}
|
|
|
|
if (col_from_screen >= 0)
|
|
{
|
|
// Use the virtual column from ScreenCols[], it is accurate also after
|
|
// concealed characters.
|
|
col = col_from_screen;
|
|
}
|
|
|
|
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 == curwin->w_fill_chars.foldclosed)
|
|
count |= MOUSE_FOLD_OPEN;
|
|
else if (mouse_char != ' ')
|
|
count |= MOUSE_FOLD_CLOSE;
|
|
#endif
|
|
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Make a horizontal scroll to "leftcol".
|
|
* Return TRUE if the cursor moved, FALSE otherwise.
|
|
*/
|
|
int
|
|
do_mousescroll_horiz(long_u leftcol)
|
|
{
|
|
if (curwin->w_p_wrap)
|
|
return FALSE; // no horizontal scrolling when wrapping
|
|
|
|
if (curwin->w_leftcol == (colnr_T)leftcol)
|
|
return FALSE; // already there
|
|
|
|
// When the line of the cursor is too short, move the cursor to the
|
|
// longest visible line.
|
|
if (
|
|
#ifdef FEAT_GUI
|
|
(!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
|
|
#endif
|
|
!virtual_active()
|
|
&& (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
|
|
{
|
|
curwin->w_cursor.lnum = ui_find_longest_lnum();
|
|
curwin->w_cursor.col = 0;
|
|
}
|
|
|
|
return set_leftcol((colnr_T)leftcol);
|
|
}
|
|
|
|
/*
|
|
* Normal and Visual modes implementation for scrolling in direction
|
|
* "cap->arg", which is one of the MSCR_ values.
|
|
*/
|
|
void
|
|
nv_mousescroll(cmdarg_T *cap)
|
|
{
|
|
win_T *old_curwin = curwin;
|
|
|
|
if (mouse_row >= 0 && mouse_col >= 0)
|
|
{
|
|
// Find the window at the mouse pointer coordinates.
|
|
// NOTE: Must restore "curwin" to "old_curwin" before returning!
|
|
int row = mouse_row;
|
|
int col = mouse_col;
|
|
curwin = mouse_find_win(&row, &col, FIND_POPUP);
|
|
if (curwin == NULL)
|
|
{
|
|
curwin = old_curwin;
|
|
return;
|
|
}
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
|
|
{
|
|
// cannot scroll this popup window
|
|
curwin = old_curwin;
|
|
return;
|
|
}
|
|
#endif
|
|
curbuf = curwin->w_buffer;
|
|
}
|
|
|
|
// Call the common mouse scroll function shared with other modes.
|
|
do_mousescroll(cap);
|
|
|
|
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);
|
|
}
|
|
|
|
static int held_button = MOUSE_RELEASE;
|
|
|
|
void
|
|
reset_held_button(void)
|
|
{
|
|
held_button = MOUSE_RELEASE;
|
|
}
|
|
|
|
/*
|
|
* Check if typebuf 'tp' contains a terminal mouse code and returns the
|
|
* modifiers found in typebuf in 'modifiers'.
|
|
*/
|
|
int
|
|
check_termcode_mouse(
|
|
char_u *tp,
|
|
int *slen,
|
|
char_u *key_name,
|
|
char_u *modifiers_start,
|
|
int idx,
|
|
int *modifiers)
|
|
{
|
|
int j;
|
|
char_u *p;
|
|
#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
|
|
|| defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
|
|
char_u bytes[6];
|
|
int num_bytes;
|
|
#endif
|
|
int mouse_code = 0; // init for GCC
|
|
int is_click, is_drag;
|
|
int is_release, release_is_ambiguous;
|
|
int wheel_code = 0;
|
|
int current_button;
|
|
static int orig_num_clicks = 1;
|
|
static int orig_mouse_code = 0x0;
|
|
#ifdef CHECK_DOUBLE_CLICK
|
|
static int orig_mouse_col = 0;
|
|
static int orig_mouse_row = 0;
|
|
static struct timeval orig_mouse_time = {0, 0};
|
|
// time of previous mouse click
|
|
struct timeval mouse_time; // time of current mouse click
|
|
long timediff; // elapsed time in msec
|
|
#endif
|
|
|
|
is_click = is_drag = is_release = release_is_ambiguous = FALSE;
|
|
|
|
#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
|
|
|| defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
|
|
if (key_name[0] == KS_MOUSE
|
|
# ifdef FEAT_MOUSE_GPM
|
|
|| key_name[0] == KS_GPM_MOUSE
|
|
# endif
|
|
)
|
|
{
|
|
/*
|
|
* For xterm we get "<t_mouse>scr", where s == encoded button state:
|
|
* 0x20 = left button down
|
|
* 0x21 = middle button down
|
|
* 0x22 = right button down
|
|
* 0x23 = any button release
|
|
* 0x60 = button 4 down (scroll wheel down)
|
|
* 0x61 = button 5 down (scroll wheel up)
|
|
* add 0x04 for SHIFT
|
|
* add 0x08 for ALT
|
|
* add 0x10 for CTRL
|
|
* add 0x20 for mouse drag (0x40 is drag with left button)
|
|
* add 0x40 for mouse move (0x80 is move, 0x81 too)
|
|
* 0x43 (drag + release) is also move
|
|
* c == column + ' ' + 1 == column + 33
|
|
* r == row + ' ' + 1 == row + 33
|
|
*
|
|
* The coordinates are passed on through global variables. Ugly, but
|
|
* this avoids trouble with mouse clicks at an unexpected moment and
|
|
* allows for mapping them.
|
|
*/
|
|
for (;;)
|
|
{
|
|
// For the GUI and for MS-Windows two bytes each are used for row
|
|
// and column. Allows for more than 223 columns.
|
|
# if defined(FEAT_GUI) || defined(MSWIN)
|
|
if (TRUE
|
|
# if defined(FEAT_GUI) && !defined(MSWIN)
|
|
&& gui.in_use
|
|
# endif
|
|
)
|
|
{
|
|
num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
|
|
if (num_bytes == -1) // not enough coordinates
|
|
return -1;
|
|
mouse_code = bytes[0];
|
|
mouse_col = 128 * (bytes[1] - ' ' - 1)
|
|
+ bytes[2] - ' ' - 1;
|
|
mouse_row = 128 * (bytes[3] - ' ' - 1)
|
|
+ bytes[4] - ' ' - 1;
|
|
}
|
|
else
|
|
# endif
|
|
{
|
|
num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
|
|
if (num_bytes == -1) // not enough coordinates
|
|
return -1;
|
|
mouse_code = bytes[0];
|
|
mouse_col = bytes[1] - ' ' - 1;
|
|
mouse_row = bytes[2] - ' ' - 1;
|
|
}
|
|
*slen += num_bytes;
|
|
|
|
// If the following bytes is also a mouse code and it has the same
|
|
// code, dump this one and get the next. This makes dragging a
|
|
// whole lot faster.
|
|
# ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
j = 3;
|
|
else
|
|
# endif
|
|
j = get_termcode_len(idx);
|
|
if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
|
|
&& tp[*slen + j] == mouse_code
|
|
&& tp[*slen + j + 1] != NUL
|
|
&& tp[*slen + j + 2] != NUL
|
|
# ifdef FEAT_GUI
|
|
&& (!gui.in_use
|
|
|| (tp[*slen + j + 3] != NUL
|
|
&& tp[*slen + j + 4] != NUL))
|
|
# endif
|
|
)
|
|
*slen += j;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (key_name[0] == KS_URXVT_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
{
|
|
// URXVT 1015 mouse reporting mode:
|
|
// Almost identical to xterm mouse mode, except the values are decimal
|
|
// instead of bytes.
|
|
//
|
|
// \033[%d;%d;%dM
|
|
// ^-- row
|
|
// ^----- column
|
|
// ^-------- code
|
|
//
|
|
// SGR 1006 mouse reporting mode:
|
|
// Almost identical to xterm mouse mode, except the values are decimal
|
|
// instead of bytes.
|
|
//
|
|
// \033[<%d;%d;%dM
|
|
// ^-- row
|
|
// ^----- column
|
|
// ^-------- code
|
|
//
|
|
// \033[<%d;%d;%dm : mouse release event
|
|
// ^-- row
|
|
// ^----- column
|
|
// ^-------- code
|
|
p = modifiers_start;
|
|
if (p == NULL)
|
|
return -1;
|
|
|
|
mouse_code = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
// when mouse reporting is SGR, add 32 to mouse code
|
|
if (key_name[0] == KS_SGR_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
mouse_code += 32;
|
|
|
|
mouse_col = getdigits(&p) - 1;
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
mouse_row = getdigits(&p) - 1;
|
|
|
|
// The modifiers were the mouse coordinates, not the modifier keys
|
|
// (alt/shift/ctrl/meta) state.
|
|
*modifiers = 0;
|
|
}
|
|
|
|
if (key_name[0] == KS_SGR_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
{
|
|
if (key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
{
|
|
is_release = TRUE;
|
|
// This is used below to set held_button.
|
|
mouse_code |= MOUSE_RELEASE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
release_is_ambiguous = TRUE;
|
|
if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
|
|
is_release = TRUE;
|
|
}
|
|
|
|
if (key_name[0] == KS_MOUSE
|
|
# ifdef FEAT_MOUSE_GPM
|
|
|| key_name[0] == KS_GPM_MOUSE
|
|
# endif
|
|
# ifdef FEAT_MOUSE_URXVT
|
|
|| key_name[0] == KS_URXVT_MOUSE
|
|
# endif
|
|
|| key_name[0] == KS_SGR_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
{
|
|
# if !defined(MSWIN)
|
|
/*
|
|
* Handle old style mouse events.
|
|
* Recognize the xterm mouse wheel, but not in the GUI, the
|
|
* Linux console with GPM and the MS-DOS or Win32 console
|
|
* (multi-clicks use >= 0x60).
|
|
*/
|
|
if (mouse_code >= MOUSEWHEEL_LOW
|
|
# ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
# endif
|
|
# ifdef FEAT_MOUSE_GPM
|
|
&& key_name[0] != KS_GPM_MOUSE
|
|
# endif
|
|
)
|
|
{
|
|
# if defined(UNIX)
|
|
if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
|
|
// mouse-move event, using MOUSE_DRAG works
|
|
mouse_code = MOUSE_DRAG;
|
|
else
|
|
# endif
|
|
// Keep the mouse_code before it's changed, so that we
|
|
// remember that it was a mouse wheel click.
|
|
wheel_code = mouse_code;
|
|
}
|
|
# ifdef FEAT_MOUSE_XTERM
|
|
else if (held_button == MOUSE_RELEASE
|
|
# ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
# endif
|
|
&& (mouse_code == 0x23 || mouse_code == 0x24
|
|
|| mouse_code == 0x40 || mouse_code == 0x41))
|
|
{
|
|
// Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
|
|
// And 0x40 and 0x41 are used by some xterm emulator.
|
|
wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
|
|
+ MOUSEWHEEL_LOW;
|
|
}
|
|
# endif
|
|
|
|
# if defined(UNIX)
|
|
else if (use_xterm_mouse() > 1)
|
|
{
|
|
if (mouse_code & MOUSE_DRAG_XTERM)
|
|
mouse_code |= MOUSE_DRAG;
|
|
}
|
|
# endif
|
|
# ifdef FEAT_XCLIPBOARD
|
|
else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
|
|
{
|
|
if (is_release)
|
|
stop_xterm_trace();
|
|
else
|
|
start_xterm_trace(mouse_code);
|
|
}
|
|
# endif
|
|
# endif
|
|
}
|
|
#endif // !UNIX || FEAT_MOUSE_XTERM
|
|
#ifdef FEAT_MOUSE_NET
|
|
if (key_name[0] == KS_NETTERM_MOUSE)
|
|
{
|
|
int mc, mr;
|
|
|
|
// expect a rather limited sequence like: balancing {
|
|
// \033}6,45\r
|
|
// '6' is the row, 45 is the column
|
|
p = tp + *slen;
|
|
mr = getdigits(&p);
|
|
if (*p++ != ',')
|
|
return -1;
|
|
mc = getdigits(&p);
|
|
if (*p++ != '\r')
|
|
return -1;
|
|
|
|
mouse_col = mc - 1;
|
|
mouse_row = mr - 1;
|
|
mouse_code = MOUSE_LEFT;
|
|
*slen += (int)(p - (tp + *slen));
|
|
}
|
|
#endif // FEAT_MOUSE_NET
|
|
#ifdef FEAT_MOUSE_JSB
|
|
if (key_name[0] == KS_JSBTERM_MOUSE)
|
|
{
|
|
int mult, val, iter, button, status;
|
|
|
|
/*
|
|
* JSBTERM Input Model
|
|
* \033[0~zw uniq escape sequence
|
|
* (L-x) Left button pressed - not pressed x not reporting
|
|
* (M-x) Middle button pressed - not pressed x not reporting
|
|
* (R-x) Right button pressed - not pressed x not reporting
|
|
* (SDmdu) Single , Double click, m: mouse move, d: button down,
|
|
* u: button up
|
|
* ### X cursor position padded to 3 digits
|
|
* ### Y cursor position padded to 3 digits
|
|
* (s-x) SHIFT key pressed - not pressed x not reporting
|
|
* (c-x) CTRL key pressed - not pressed x not reporting
|
|
* \033\\ terminating sequence
|
|
*/
|
|
p = tp + *slen;
|
|
button = mouse_code = 0;
|
|
switch (*p++)
|
|
{
|
|
case 'L': button = 1; break;
|
|
case '-': break;
|
|
case 'x': break; // ignore sequence
|
|
default: return -1; // Unknown Result
|
|
}
|
|
switch (*p++)
|
|
{
|
|
case 'M': button |= 2; break;
|
|
case '-': break;
|
|
case 'x': break; // ignore sequence
|
|
default: return -1; // Unknown Result
|
|
}
|
|
switch (*p++)
|
|
{
|
|
case 'R': button |= 4; break;
|
|
case '-': break;
|
|
case 'x': break; // ignore sequence
|
|
default: return -1; // Unknown Result
|
|
}
|
|
status = *p++;
|
|
for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
|
|
mult /= 10, p++)
|
|
if (*p >= '0' && *p <= '9')
|
|
val += (*p - '0') * mult;
|
|
else
|
|
return -1;
|
|
mouse_col = val;
|
|
for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
|
|
mult /= 10, p++)
|
|
if (*p >= '0' && *p <= '9')
|
|
val += (*p - '0') * mult;
|
|
else
|
|
return -1;
|
|
mouse_row = val;
|
|
switch (*p++)
|
|
{
|
|
case 's': button |= 8; break; // SHIFT key Pressed
|
|
case '-': break; // Not Pressed
|
|
case 'x': break; // Not Reporting
|
|
default: return -1; // Unknown Result
|
|
}
|
|
switch (*p++)
|
|
{
|
|
case 'c': button |= 16; break; // CTRL key Pressed
|
|
case '-': break; // Not Pressed
|
|
case 'x': break; // Not Reporting
|
|
default: return -1; // Unknown Result
|
|
}
|
|
if (*p++ != '\033')
|
|
return -1;
|
|
if (*p++ != '\\')
|
|
return -1;
|
|
switch (status)
|
|
{
|
|
case 'D': // Double Click
|
|
case 'S': // Single Click
|
|
if (button & 1) mouse_code |= MOUSE_LEFT;
|
|
if (button & 2) mouse_code |= MOUSE_MIDDLE;
|
|
if (button & 4) mouse_code |= MOUSE_RIGHT;
|
|
if (button & 8) mouse_code |= MOUSE_SHIFT;
|
|
if (button & 16) mouse_code |= MOUSE_CTRL;
|
|
break;
|
|
case 'm': // Mouse move
|
|
if (button & 1) mouse_code |= MOUSE_LEFT;
|
|
if (button & 2) mouse_code |= MOUSE_MIDDLE;
|
|
if (button & 4) mouse_code |= MOUSE_RIGHT;
|
|
if (button & 8) mouse_code |= MOUSE_SHIFT;
|
|
if (button & 16) mouse_code |= MOUSE_CTRL;
|
|
if ((button & 7) != 0)
|
|
{
|
|
held_button = mouse_code;
|
|
mouse_code |= MOUSE_DRAG;
|
|
}
|
|
is_drag = TRUE;
|
|
showmode();
|
|
break;
|
|
case 'd': // Button Down
|
|
if (button & 1) mouse_code |= MOUSE_LEFT;
|
|
if (button & 2) mouse_code |= MOUSE_MIDDLE;
|
|
if (button & 4) mouse_code |= MOUSE_RIGHT;
|
|
if (button & 8) mouse_code |= MOUSE_SHIFT;
|
|
if (button & 16) mouse_code |= MOUSE_CTRL;
|
|
break;
|
|
case 'u': // Button Up
|
|
is_release = TRUE;
|
|
if (button & 1)
|
|
mouse_code |= MOUSE_LEFT;
|
|
if (button & 2)
|
|
mouse_code |= MOUSE_MIDDLE;
|
|
if (button & 4)
|
|
mouse_code |= MOUSE_RIGHT;
|
|
if (button & 8)
|
|
mouse_code |= MOUSE_SHIFT;
|
|
if (button & 16)
|
|
mouse_code |= MOUSE_CTRL;
|
|
break;
|
|
default: return -1; // Unknown Result
|
|
}
|
|
|
|
*slen += (p - (tp + *slen));
|
|
}
|
|
#endif // FEAT_MOUSE_JSB
|
|
#ifdef FEAT_MOUSE_DEC
|
|
if (key_name[0] == KS_DEC_MOUSE)
|
|
{
|
|
/*
|
|
* The DEC Locator Input Model
|
|
* Netterm delivers the code sequence:
|
|
* \033[2;4;24;80&w (left button down)
|
|
* \033[3;0;24;80&w (left button up)
|
|
* \033[6;1;24;80&w (right button down)
|
|
* \033[7;0;24;80&w (right button up)
|
|
* CSI Pe ; Pb ; Pr ; Pc ; Pp & w
|
|
* Pe is the event code
|
|
* Pb is the button code
|
|
* Pr is the row coordinate
|
|
* Pc is the column coordinate
|
|
* Pp is the third coordinate (page number)
|
|
* Pe, the event code indicates what event caused this report
|
|
* The following event codes are defined:
|
|
* 0 - request, the terminal received an explicit request for a
|
|
* locator report, but the locator is unavailable
|
|
* 1 - request, the terminal received an explicit request for a
|
|
* locator report
|
|
* 2 - left button down
|
|
* 3 - left button up
|
|
* 4 - middle button down
|
|
* 5 - middle button up
|
|
* 6 - right button down
|
|
* 7 - right button up
|
|
* 8 - fourth button down
|
|
* 9 - fourth button up
|
|
* 10 - locator outside filter rectangle
|
|
* Pb, the button code, ASCII decimal 0-15 indicating which buttons are
|
|
* down if any. The state of the four buttons on the locator
|
|
* correspond to the low four bits of the decimal value, "1" means
|
|
* button depressed
|
|
* 0 - no buttons down,
|
|
* 1 - right,
|
|
* 2 - middle,
|
|
* 4 - left,
|
|
* 8 - fourth
|
|
* Pr is the row coordinate of the locator position in the page,
|
|
* encoded as an ASCII decimal value. If Pr is omitted, the locator
|
|
* position is undefined (outside the terminal window for example).
|
|
* Pc is the column coordinate of the locator position in the page,
|
|
* encoded as an ASCII decimal value. If Pc is omitted, the locator
|
|
* position is undefined (outside the terminal window for example).
|
|
* Pp is the page coordinate of the locator position encoded as an
|
|
* ASCII decimal value. The page coordinate may be omitted if the
|
|
* locator is on page one (the default). We ignore it anyway.
|
|
*/
|
|
int Pe, Pb, Pr, Pc;
|
|
|
|
p = tp + *slen;
|
|
|
|
// get event status
|
|
Pe = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
// get button status
|
|
Pb = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
// get row status
|
|
Pr = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
// get column status
|
|
Pc = getdigits(&p);
|
|
|
|
// the page parameter is optional
|
|
if (*p == ';')
|
|
{
|
|
p++;
|
|
(void)getdigits(&p);
|
|
}
|
|
if (*p++ != '&')
|
|
return -1;
|
|
if (*p++ != 'w')
|
|
return -1;
|
|
|
|
mouse_code = 0;
|
|
switch (Pe)
|
|
{
|
|
case 0: return -1; // position request while unavailable
|
|
case 1: // a response to a locator position request includes
|
|
// the status of all buttons
|
|
Pb &= 7; // mask off and ignore fourth button
|
|
if (Pb & 4)
|
|
mouse_code = MOUSE_LEFT;
|
|
if (Pb & 2)
|
|
mouse_code = MOUSE_MIDDLE;
|
|
if (Pb & 1)
|
|
mouse_code = MOUSE_RIGHT;
|
|
if (Pb)
|
|
{
|
|
held_button = mouse_code;
|
|
mouse_code |= MOUSE_DRAG;
|
|
WantQueryMouse = TRUE;
|
|
}
|
|
is_drag = TRUE;
|
|
showmode();
|
|
break;
|
|
case 2: mouse_code = MOUSE_LEFT;
|
|
WantQueryMouse = TRUE;
|
|
break;
|
|
case 3: mouse_code = MOUSE_LEFT;
|
|
is_release = TRUE;
|
|
break;
|
|
case 4: mouse_code = MOUSE_MIDDLE;
|
|
WantQueryMouse = TRUE;
|
|
break;
|
|
case 5: mouse_code = MOUSE_MIDDLE;
|
|
is_release = TRUE;
|
|
break;
|
|
case 6: mouse_code = MOUSE_RIGHT;
|
|
WantQueryMouse = TRUE;
|
|
break;
|
|
case 7: mouse_code = MOUSE_RIGHT;
|
|
is_release = TRUE;
|
|
break;
|
|
case 8: return -1; // fourth button down
|
|
case 9: return -1; // fourth button up
|
|
case 10: return -1; // mouse outside of filter rectangle
|
|
default: return -1; // should never occur
|
|
}
|
|
|
|
mouse_col = Pc - 1;
|
|
mouse_row = Pr - 1;
|
|
|
|
*slen += (int)(p - (tp + *slen));
|
|
}
|
|
#endif // FEAT_MOUSE_DEC
|
|
#ifdef FEAT_MOUSE_PTERM
|
|
if (key_name[0] == KS_PTERM_MOUSE)
|
|
{
|
|
int button, num_clicks, action;
|
|
|
|
p = tp + *slen;
|
|
|
|
action = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
mouse_row = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
mouse_col = getdigits(&p);
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
button = getdigits(&p);
|
|
mouse_code = 0;
|
|
|
|
switch (button)
|
|
{
|
|
case 4: mouse_code = MOUSE_LEFT; break;
|
|
case 1: mouse_code = MOUSE_RIGHT; break;
|
|
case 2: mouse_code = MOUSE_MIDDLE; break;
|
|
default: return -1;
|
|
}
|
|
|
|
switch (action)
|
|
{
|
|
case 31: // Initial press
|
|
if (*p++ != ';')
|
|
return -1;
|
|
|
|
num_clicks = getdigits(&p); // Not used
|
|
break;
|
|
|
|
case 32: // Release
|
|
is_release = TRUE;
|
|
break;
|
|
|
|
case 33: // Drag
|
|
held_button = mouse_code;
|
|
mouse_code |= MOUSE_DRAG;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (*p++ != 't')
|
|
return -1;
|
|
|
|
*slen += (p - (tp + *slen));
|
|
}
|
|
#endif // FEAT_MOUSE_PTERM
|
|
|
|
// Interpret the mouse code
|
|
current_button = (mouse_code & MOUSE_CLICK_MASK);
|
|
if (is_release)
|
|
current_button |= MOUSE_RELEASE;
|
|
|
|
if (current_button == MOUSE_RELEASE
|
|
#ifdef FEAT_MOUSE_XTERM
|
|
&& wheel_code == 0
|
|
#endif
|
|
)
|
|
{
|
|
/*
|
|
* If we get a mouse drag or release event when there is no mouse
|
|
* button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
|
|
* below.
|
|
* (can happen when you hold down two buttons and then let them go, or
|
|
* click in the menu bar, but not on a menu, and drag into the text).
|
|
*/
|
|
if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
|
|
is_drag = TRUE;
|
|
current_button = held_button;
|
|
}
|
|
else
|
|
{
|
|
if (wheel_code == 0)
|
|
{
|
|
#ifdef CHECK_DOUBLE_CLICK
|
|
# ifdef FEAT_MOUSE_GPM
|
|
/*
|
|
* Only for Unix, when GUI not active, we handle multi-clicks here, but
|
|
* not for GPM mouse events.
|
|
*/
|
|
# ifdef FEAT_GUI
|
|
if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
|
|
# else
|
|
if (key_name[0] != KS_GPM_MOUSE)
|
|
# endif
|
|
# else
|
|
# ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
# endif
|
|
# endif
|
|
{
|
|
/*
|
|
* Compute the time elapsed since the previous mouse click.
|
|
*/
|
|
gettimeofday(&mouse_time, NULL);
|
|
if (orig_mouse_time.tv_sec == 0)
|
|
{
|
|
/*
|
|
* Avoid computing the difference between mouse_time
|
|
* and orig_mouse_time for the first click, as the
|
|
* difference would be huge and would cause
|
|
* multiplication overflow.
|
|
*/
|
|
timediff = p_mouset;
|
|
}
|
|
else
|
|
timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
|
|
orig_mouse_time = mouse_time;
|
|
if (mouse_code == orig_mouse_code
|
|
&& timediff < p_mouset
|
|
&& orig_num_clicks != 4
|
|
&& orig_mouse_col == mouse_col
|
|
&& orig_mouse_row == mouse_row
|
|
&& (is_mouse_topline(curwin)
|
|
// Double click in tab pages line also works
|
|
// when window contents changes.
|
|
|| (mouse_row == 0 && firstwin->w_winrow > 0))
|
|
)
|
|
++orig_num_clicks;
|
|
else
|
|
orig_num_clicks = 1;
|
|
orig_mouse_col = mouse_col;
|
|
orig_mouse_row = mouse_row;
|
|
set_mouse_topline(curwin);
|
|
}
|
|
# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
|
|
else
|
|
orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
|
|
# endif
|
|
#else
|
|
orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
|
|
#endif
|
|
is_click = TRUE;
|
|
}
|
|
orig_mouse_code = mouse_code;
|
|
}
|
|
if (!is_drag)
|
|
held_button = mouse_code & MOUSE_CLICK_MASK;
|
|
|
|
/*
|
|
* Translate the actual mouse event into a pseudo mouse event.
|
|
* First work out what modifiers are to be used.
|
|
*/
|
|
if (orig_mouse_code & MOUSE_SHIFT)
|
|
*modifiers |= MOD_MASK_SHIFT;
|
|
if (orig_mouse_code & MOUSE_CTRL)
|
|
*modifiers |= MOD_MASK_CTRL;
|
|
if (orig_mouse_code & MOUSE_ALT)
|
|
*modifiers |= MOD_MASK_ALT;
|
|
if (orig_num_clicks == 2)
|
|
*modifiers |= MOD_MASK_2CLICK;
|
|
else if (orig_num_clicks == 3)
|
|
*modifiers |= MOD_MASK_3CLICK;
|
|
else if (orig_num_clicks == 4)
|
|
*modifiers |= MOD_MASK_4CLICK;
|
|
|
|
// Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
|
|
// then it's not mouse up/down.
|
|
key_name[0] = KS_EXTRA;
|
|
if (wheel_code != 0 && (!is_release || release_is_ambiguous))
|
|
{
|
|
if (wheel_code & MOUSE_CTRL)
|
|
*modifiers |= MOD_MASK_CTRL;
|
|
if (wheel_code & MOUSE_ALT)
|
|
*modifiers |= MOD_MASK_ALT;
|
|
|
|
if (wheel_code & 1 && wheel_code & 2)
|
|
key_name[1] = (int)KE_MOUSELEFT;
|
|
else if (wheel_code & 2)
|
|
key_name[1] = (int)KE_MOUSERIGHT;
|
|
else if (wheel_code & 1)
|
|
key_name[1] = (int)KE_MOUSEUP;
|
|
else
|
|
key_name[1] = (int)KE_MOUSEDOWN;
|
|
|
|
held_button = MOUSE_RELEASE;
|
|
}
|
|
else
|
|
key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
|
|
|
|
|
|
// Make sure the mouse position is valid. Some terminals may return weird
|
|
// values.
|
|
if (mouse_col >= Columns)
|
|
mouse_col = Columns - 1;
|
|
if (mouse_row >= Rows)
|
|
mouse_row = Rows - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Functions also used for popup windows.
|
|
|
|
/*
|
|
* 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 "Rows" 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;
|
|
|
|
// Only "Rows" lines are cached, with folding we'll run out of entries
|
|
// and use the slow way.
|
|
if (plines_cache != NULL && cache_idx < Rows
|
|
&& 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, FALSE);
|
|
}
|
|
else
|
|
#endif
|
|
count = plines_win(win, lnum, FALSE);
|
|
if (plines_cache != NULL && cache_idx < Rows)
|
|
plines_cache[cache_idx] = count;
|
|
}
|
|
|
|
if (win->w_skipcol > 0 && lnum == win->w_topline)
|
|
{
|
|
int width1 = win->w_width - win_col_off(win);
|
|
|
|
if (width1 > 0)
|
|
{
|
|
int skip_lines = 0;
|
|
|
|
// Adjust for 'smoothscroll' clipping the top screen lines.
|
|
// A similar formula is used in curs_columns().
|
|
if (win->w_skipcol > width1)
|
|
skip_lines = (win->w_skipcol - width1)
|
|
/ (width1 + win_col_off2(win)) + 1;
|
|
else if (win->w_skipcol > 0)
|
|
skip_lines = 1;
|
|
|
|
count -= skip_lines;
|
|
}
|
|
}
|
|
|
|
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 the topline.
|
|
if (lnum == win->w_topline)
|
|
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
|
|
// if mouse is clicked on the gutter, then inform the netbeans server
|
|
if (*colp < win_col_off(win))
|
|
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_PROP_POPUP
|
|
win_T *pwp = NULL;
|
|
|
|
if (popup != IGNORE_POPUP)
|
|
{
|
|
popup_reset_handled(POPUP_HANDLED_1);
|
|
while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != 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(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
|
|
|| defined(FEAT_EVAL) || defined(PROTO)
|
|
/*
|
|
* Convert a virtual (screen) column to a character column.
|
|
* The first column is zero.
|
|
*/
|
|
int
|
|
vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp)
|
|
{
|
|
char_u *line;
|
|
chartabsize_T cts;
|
|
|
|
// try to advance to the specified column
|
|
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
|
|
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
|
|
while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
|
|
{
|
|
int size = win_lbr_chartabsize(&cts, NULL);
|
|
if (cts.cts_vcol + size > vcol)
|
|
break;
|
|
cts.cts_vcol += size;
|
|
MB_PTR_ADV(cts.cts_ptr);
|
|
}
|
|
clear_chartabsize_arg(&cts);
|
|
|
|
if (coladdp != NULL)
|
|
*coladdp = vcol - cts.cts_vcol;
|
|
return (int)(cts.cts_ptr - line);
|
|
}
|
|
#endif
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
/*
|
|
* "getmousepos()" function.
|
|
*/
|
|
void
|
|
f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
|
|
{
|
|
dict_T *d;
|
|
win_T *wp;
|
|
int row = mouse_row;
|
|
int col = mouse_col;
|
|
varnumber_T winid = 0;
|
|
varnumber_T winrow = 0;
|
|
varnumber_T wincol = 0;
|
|
linenr_T lnum = 0;
|
|
varnumber_T column = 0;
|
|
colnr_T coladd = 0;
|
|
|
|
if (rettv_dict_alloc(rettv) == FAIL)
|
|
return;
|
|
d = rettv->vval.v_dict;
|
|
|
|
dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
|
|
dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
|
|
|
|
wp = mouse_find_win(&row, &col, FIND_POPUP);
|
|
if (wp != NULL)
|
|
{
|
|
int top_off = 0;
|
|
int left_off = 0;
|
|
int height = wp->w_height + wp->w_status_height;
|
|
|
|
# ifdef FEAT_PROP_POPUP
|
|
if (WIN_IS_POPUP(wp))
|
|
{
|
|
top_off = popup_top_extra(wp);
|
|
left_off = popup_left_extra(wp);
|
|
height = popup_height(wp);
|
|
}
|
|
# endif
|
|
if (row < height)
|
|
{
|
|
winid = wp->w_id;
|
|
winrow = row + 1;
|
|
wincol = col + 1;
|
|
row -= top_off;
|
|
col -= left_off;
|
|
if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
|
|
{
|
|
(void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
|
|
col = vcol2col(wp, lnum, col, &coladd);
|
|
column = col + 1;
|
|
}
|
|
}
|
|
}
|
|
dict_add_number(d, "winid", winid);
|
|
dict_add_number(d, "winrow", winrow);
|
|
dict_add_number(d, "wincol", wincol);
|
|
dict_add_number(d, "line", (varnumber_T)lnum);
|
|
dict_add_number(d, "column", column);
|
|
dict_add_number(d, "coladd", coladd);
|
|
}
|
|
#endif
|