blob: 4e8dd4d230d7b71459c86985d967bfa6a21c57da [file] [log] [blame]
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * mouse.c: mouse handling functions
12 */
13
14#include "vim.h"
15
LemonBoyc27747e2022-05-07 12:25:40 +010016/*
17 * Horiziontal and vertical steps used when scrolling.
18 * When negative scroll by a whole page.
19 */
20static long mouse_hor_step = 6;
21static long mouse_vert_step = 3;
22
23 void
24mouse_set_vert_scroll_step(long step)
25{
26 mouse_vert_step = step;
27}
28
29 void
30mouse_set_hor_scroll_step(long step)
31{
32 mouse_hor_step = step;
33}
34
Bram Moolenaar85c35022019-11-22 22:21:59 +010035#ifdef CHECK_DOUBLE_CLICK
36/*
37 * Return the duration from t1 to t2 in milliseconds.
38 */
39 static long
40time_diff_ms(struct timeval *t1, struct timeval *t2)
41{
42 // This handles wrapping of tv_usec correctly without any special case.
43 // Example of 2 pairs (tv_sec, tv_usec) with a duration of 5 ms:
44 // t1 = (1, 998000) t2 = (2, 3000) gives:
45 // (2 - 1) * 1000 + (3000 - 998000) / 1000 -> 5 ms.
46 return (t2->tv_sec - t1->tv_sec) * 1000
47 + (t2->tv_usec - t1->tv_usec) / 1000;
48}
49#endif
50
Bram Moolenaarb20b9e12019-09-21 20:48:04 +020051/*
52 * Get class of a character for selection: same class means same word.
53 * 0: blank
54 * 1: punctuation groups
55 * 2: normal word character
56 * >2: multi-byte word character.
57 */
58 static int
59get_mouse_class(char_u *p)
60{
61 int c;
62
63 if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
64 return mb_get_class(p);
65
66 c = *p;
67 if (c == ' ' || c == '\t')
68 return 0;
69
70 if (vim_iswordc(c))
71 return 2;
72
73 // There are a few special cases where we want certain combinations of
74 // characters to be considered as a single word. These are things like
75 // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
76 // character is in its own class.
77 if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
78 return 1;
79 return c;
80}
81
82/*
83 * Move "pos" back to the start of the word it's in.
84 */
85 static void
86find_start_of_word(pos_T *pos)
87{
88 char_u *line;
89 int cclass;
90 int col;
91
92 line = ml_get(pos->lnum);
93 cclass = get_mouse_class(line + pos->col);
94
95 while (pos->col > 0)
96 {
97 col = pos->col - 1;
98 col -= (*mb_head_off)(line, line + col);
99 if (get_mouse_class(line + col) != cclass)
100 break;
101 pos->col = col;
102 }
103}
104
105/*
106 * Move "pos" forward to the end of the word it's in.
107 * When 'selection' is "exclusive", the position is just after the word.
108 */
109 static void
110find_end_of_word(pos_T *pos)
111{
112 char_u *line;
113 int cclass;
114 int col;
115
116 line = ml_get(pos->lnum);
117 if (*p_sel == 'e' && pos->col > 0)
118 {
119 --pos->col;
120 pos->col -= (*mb_head_off)(line, line + pos->col);
121 }
122 cclass = get_mouse_class(line + pos->col);
123 while (line[pos->col] != NUL)
124 {
125 col = pos->col + (*mb_ptr2len)(line + pos->col);
126 if (get_mouse_class(line + col) != cclass)
127 {
128 if (*p_sel == 'e')
129 pos->col = col;
130 break;
131 }
132 pos->col = col;
133 }
134}
135
Bram Moolenaar910c3782019-09-22 14:11:50 +0200136#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100137 || defined(FEAT_GUI_MSWIN) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200138 || defined(FEAT_GUI_PHOTON) \
Bram Moolenaar910c3782019-09-22 14:11:50 +0200139 || defined(FEAT_TERM_POPUP_MENU)
140# define USE_POPUP_SETPOS
141# define NEED_VCOL2COL
142
143/*
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100144 * Translate window coordinates to buffer position without any side effects.
145 * Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
146 * The column is one for the first column.
Bram Moolenaar910c3782019-09-22 14:11:50 +0200147 */
148 static int
149get_fpos_of_mouse(pos_T *mpos)
150{
151 win_T *wp;
152 int row = mouse_row;
153 int col = mouse_col;
154
155 if (row < 0 || col < 0) // check if it makes sense
156 return IN_UNKNOWN;
157
158 // find the window where the row is in
159 wp = mouse_find_win(&row, &col, FAIL_POPUP);
160 if (wp == NULL)
161 return IN_UNKNOWN;
162 // winpos and height may change in win_enter()!
163 if (row >= wp->w_height) // In (or below) status line
164 return IN_STATUS_LINE;
165 if (col >= wp->w_width) // In vertical separator line
166 return IN_SEP_LINE;
167
168 if (wp != curwin)
169 return IN_UNKNOWN;
170
171 // compute the position in the buffer line from the posn on the screen
172 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
173 return IN_STATUS_LINE; // past bottom
174
175 mpos->col = vcol2col(wp, mpos->lnum, col);
176
Bram Moolenaar910c3782019-09-22 14:11:50 +0200177 mpos->coladd = 0;
178 return IN_BUFFER;
179}
180#endif
181
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200182/*
183 * Do the appropriate action for the current mouse click in the current mode.
184 * Not used for Command-line mode.
185 *
186 * Normal and Visual Mode:
187 * event modi- position visual change action
188 * fier cursor window
189 * left press - yes end yes
190 * left press C yes end yes "^]" (2)
191 * left press S yes end (popup: extend) yes "*" (2)
192 * left drag - yes start if moved no
193 * left relse - yes start if moved no
194 * middle press - yes if not active no put register
195 * middle press - yes if active no yank and put
196 * right press - yes start or extend yes
197 * right press S yes no change yes "#" (2)
198 * right drag - yes extend no
199 * right relse - yes extend no
200 *
201 * Insert or Replace Mode:
202 * event modi- position visual change action
203 * fier cursor window
204 * left press - yes (cannot be active) yes
205 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
206 * left press S yes (cannot be active) yes "CTRL-O*" (2)
207 * left drag - yes start or extend (1) no CTRL-O (1)
208 * left relse - yes start or extend (1) no CTRL-O (1)
209 * middle press - no (cannot be active) no put register
210 * right press - yes start or extend yes CTRL-O
211 * right press S yes (cannot be active) yes "CTRL-O#" (2)
212 *
213 * (1) only if mouse pointer moved since press
214 * (2) only if click is in same buffer
215 *
216 * Return TRUE if start_arrow() should be called for edit mode.
217 */
218 int
219do_mouse(
220 oparg_T *oap, // operator argument, can be NULL
221 int c, // K_LEFTMOUSE, etc
222 int dir, // Direction to 'put' if necessary
223 long count,
224 int fixindent) // PUT_FIXINDENT if fixing indent necessary
225{
226 static int do_always = FALSE; // ignore 'mouse' setting next time
zeertzjq8e0ccb62022-11-01 18:35:27 +0000227 static int got_click = FALSE; // got a click some time back
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200228
229 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
230 int is_click = FALSE; // If FALSE it's a drag or release event
231 int is_drag = FALSE; // If TRUE it's a drag event
232 int jump_flags = 0; // flags for jump_to_mouse()
233 pos_T start_visual;
234 int moved; // Has cursor moved?
235 int in_status_line; // mouse in status line
236 static int in_tab_line = FALSE; // mouse clicked in tab line
237 int in_sep_line; // mouse in vertical separator line
238 int c1, c2;
239#if defined(FEAT_FOLDING)
240 pos_T save_cursor;
241#endif
242 win_T *old_curwin = curwin;
243 static pos_T orig_cursor;
244 colnr_T leftcol, rightcol;
245 pos_T end_visual;
246 int diff;
247 int old_active = VIsual_active;
248 int old_mode = VIsual_mode;
249 int regname;
250
251#if defined(FEAT_FOLDING)
252 save_cursor = curwin->w_cursor;
253#endif
254
255 // When GUI is active, always recognize mouse events, otherwise:
256 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
257 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
258 // - For command line and insert mode 'mouse' is checked before calling
259 // do_mouse().
260 if (do_always)
261 do_always = FALSE;
262 else
263#ifdef FEAT_GUI
264 if (!gui.in_use)
265#endif
266 {
267 if (VIsual_active)
268 {
269 if (!mouse_has(MOUSE_VISUAL))
270 return FALSE;
271 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100272 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200273 return FALSE;
274 }
275
276 for (;;)
277 {
278 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
279 if (is_drag)
280 {
281 // If the next character is the same mouse event then use that
282 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100283 // Note: Since characters added to the stuff buffer in the code
284 // below need to come before the next character, do not do this
285 // when the current character was stuffed.
286 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200287 {
288 int nc;
289 int save_mouse_row = mouse_row;
290 int save_mouse_col = mouse_col;
291
292 // Need to get the character, peeking doesn't get the actual
293 // one.
294 nc = safe_vgetc();
295 if (c == nc)
296 continue;
297 vungetc(nc);
298 mouse_row = save_mouse_row;
299 mouse_col = save_mouse_col;
300 }
301 }
302 break;
303 }
304
305 if (c == K_MOUSEMOVE)
306 {
307 // Mouse moved without a button pressed.
308#ifdef FEAT_BEVAL_TERM
309 ui_may_remove_balloon();
310 if (p_bevalterm)
311 {
312 profile_setlimit(p_bdlay, &bevalexpr_due);
313 bevalexpr_due_set = TRUE;
314 }
315#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100316#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200317 popup_handle_mouse_moved();
318#endif
319 return FALSE;
320 }
321
322#ifdef FEAT_MOUSESHAPE
323 // May have stopped dragging the status or separator line. The pointer is
324 // most likely still on the status or separator line.
325 if (!is_drag && drag_status_line)
326 {
327 drag_status_line = FALSE;
328 update_mouseshape(SHAPE_IDX_STATUS);
329 }
330 if (!is_drag && drag_sep_line)
331 {
332 drag_sep_line = FALSE;
333 update_mouseshape(SHAPE_IDX_VSEP);
334 }
335#endif
336
337 // Ignore drag and release events if we didn't get a click.
338 if (is_click)
zeertzjq8e0ccb62022-11-01 18:35:27 +0000339 got_click = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200340 else
341 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000342 if (!got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200343 return FALSE;
zeertzjq8e0ccb62022-11-01 18:35:27 +0000344 if (!is_drag) // release, reset got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200345 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000346 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200347 if (in_tab_line)
348 {
349 in_tab_line = FALSE;
350 return FALSE;
351 }
352 }
353 }
354
355 // CTRL right mouse button does CTRL-T
356 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
357 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100358 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200359 stuffcharReadbuff(Ctrl_O);
360 if (count > 1)
361 stuffnumReadbuff(count);
362 stuffcharReadbuff(Ctrl_T);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000363 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200364 return FALSE;
365 }
366
367 // CTRL only works with left mouse button
368 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
369 return FALSE;
370
371 // When a modifier is down, ignore drag and release events, as well as
372 // multiple clicks and the middle mouse button.
373 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
374 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
375 | MOD_MASK_META))
376 && (!is_click
377 || (mod_mask & MOD_MASK_MULTI_CLICK)
378 || which_button == MOUSE_MIDDLE)
379 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
380 && mouse_model_popup()
381 && which_button == MOUSE_LEFT)
382 && !((mod_mask & MOD_MASK_ALT)
383 && !mouse_model_popup()
384 && which_button == MOUSE_RIGHT)
385 )
386 return FALSE;
387
388 // If the button press was used as the movement command for an operator
389 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
390 // drag/release events.
391 if (!is_click && which_button == MOUSE_MIDDLE)
392 return FALSE;
393
394 if (oap != NULL)
395 regname = oap->regname;
396 else
397 regname = 0;
398
399 // Middle mouse button does a 'put' of the selected text
400 if (which_button == MOUSE_MIDDLE)
401 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100402 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200403 {
404 // If an operator was pending, we don't know what the user wanted
405 // to do. Go back to normal mode: Clear the operator and beep().
406 if (oap != NULL && oap->op_type != OP_NOP)
407 {
408 clearopbeep(oap);
409 return FALSE;
410 }
411
412 // If visual was active, yank the highlighted text and put it
413 // before the mouse pointer position.
414 // In Select mode replace the highlighted text with the clipboard.
415 if (VIsual_active)
416 {
417 if (VIsual_select)
418 {
419 stuffcharReadbuff(Ctrl_G);
420 stuffReadbuff((char_u *)"\"+p");
421 }
422 else
423 {
424 stuffcharReadbuff('y');
425 stuffcharReadbuff(K_MIDDLEMOUSE);
426 }
427 do_always = TRUE; // ignore 'mouse' setting next time
428 return FALSE;
429 }
430 // The rest is below jump_to_mouse()
431 }
432
Bram Moolenaar24959102022-05-07 20:01:16 +0100433 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200434 return FALSE;
435
436 // Middle click in insert mode doesn't move the mouse, just insert the
437 // contents of a register. '.' register is special, can't insert that
438 // with do_put().
439 // Also paste at the cursor if the current mode isn't in 'mouse' (only
440 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100441 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200442 {
443 if (regname == '.')
444 insert_reg(regname, TRUE);
445 else
446 {
447#ifdef FEAT_CLIPBOARD
448 if (clip_star.available && regname == 0)
449 regname = '*';
450#endif
451 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
452 insert_reg(regname, TRUE);
453 else
454 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200455 do_put(regname, NULL, BACKWARD, 1L,
456 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200457
458 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
459 AppendCharToRedobuff(Ctrl_R);
460 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
461 AppendCharToRedobuff(regname == 0 ? '"' : regname);
462 }
463 }
464 return FALSE;
465 }
466 }
467
468 // When dragging or button-up stay in the same window.
469 if (!is_click)
470 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
471
472 start_visual.lnum = 0;
473
Bram Moolenaar80525752022-08-24 19:27:45 +0100474 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200475 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100476 // Check for clicking in the tab page line.
477 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200478 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100479 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200480 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100481 if (in_tab_line)
482 {
483 c1 = TabPageIdxs[mouse_col];
484 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100485 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100486 }
487 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200488 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100489
490 // click in a tab selects that tab page
Martin Tournoij7904fa42022-10-04 16:28:45 +0100491 if (is_click && cmdwin_type == 0 && mouse_col < Columns)
Bram Moolenaar80525752022-08-24 19:27:45 +0100492 {
493 in_tab_line = TRUE;
494 c1 = TabPageIdxs[mouse_col];
495 if (c1 >= 0)
496 {
497 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
498 {
499 // double click opens new page
500 end_visual_mode_keep_button();
501 tabpage_new();
502 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
503 }
504 else
505 {
506 // Go to specified tab page, or next one if not clicking
507 // on a label.
508 goto_tabpage(c1);
509
510 // It's like clicking on the status line of a window.
511 if (curwin != old_curwin)
512 end_visual_mode_keep_button();
513 }
514 }
515 else
516 {
517 tabpage_T *tp;
518
519 // Close the current or specified tab page.
520 if (c1 == -999)
521 tp = curtab;
522 else
523 tp = find_tabpage(-c1);
524 if (tp == curtab)
525 {
526 if (first_tabpage->tp_next != NULL)
527 tabpage_close(FALSE);
528 }
529 else if (tp != NULL)
530 tabpage_close_other(tp, FALSE);
531 }
532 }
533 return TRUE;
534 }
535 else if (is_drag && in_tab_line)
536 {
537 c1 = TabPageIdxs[mouse_col];
538 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200539 return FALSE;
540 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200541 }
542
543 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
544 // right button up -> pop-up menu
545 // shift-left button -> right button
546 // alt-left button -> alt-right button
547 if (mouse_model_popup())
548 {
549 if (which_button == MOUSE_RIGHT
550 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
551 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200552#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200553# ifdef FEAT_GUI
554 if (gui.in_use)
555 {
556# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200557 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200558 if (!is_click)
559 // Ignore right button release events, only shows the popup
560 // menu on the button down event.
561 return FALSE;
562# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100563# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200564 if (is_click || is_drag)
565 // Ignore right button down and drag mouse events. Windows
566 // only shows the popup menu on the button up event.
567 return FALSE;
568# endif
569 }
570# endif
571# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
572 else
573# endif
574# if defined(FEAT_TERM_POPUP_MENU)
575 if (!is_click)
576 // Ignore right button release events, only shows the popup
577 // menu on the button down event.
578 return FALSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +0000579# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200580
581 jump_flags = 0;
582 if (STRCMP(p_mousem, "popup_setpos") == 0)
583 {
584 // First set the cursor position before showing the popup
585 // menu.
586 if (VIsual_active)
587 {
588 pos_T m_pos;
589
590 // set MOUSE_MAY_STOP_VIS if we are outside the
591 // selection or the current window (might have false
592 // negative here)
593 if (mouse_row < curwin->w_winrow
594 || mouse_row
595 > (curwin->w_winrow + curwin->w_height))
596 jump_flags = MOUSE_MAY_STOP_VIS;
597 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
598 jump_flags = MOUSE_MAY_STOP_VIS;
599 else
600 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100601 if (VIsual_mode == 'V')
602 {
603 if ((curwin->w_cursor.lnum <= VIsual.lnum
604 && (m_pos.lnum < curwin->w_cursor.lnum
605 || VIsual.lnum < m_pos.lnum))
606 || (VIsual.lnum < curwin->w_cursor.lnum
607 && (m_pos.lnum < VIsual.lnum
608 || curwin->w_cursor.lnum < m_pos.lnum)))
609 {
610 jump_flags = MOUSE_MAY_STOP_VIS;
611 }
612 }
613 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200614 && (LT_POS(m_pos, curwin->w_cursor)
615 || LT_POS(VIsual, m_pos)))
616 || (LT_POS(VIsual, curwin->w_cursor)
617 && (LT_POS(m_pos, VIsual)
618 || LT_POS(curwin->w_cursor, m_pos))))
619 {
620 jump_flags = MOUSE_MAY_STOP_VIS;
621 }
622 else if (VIsual_mode == Ctrl_V)
623 {
624 getvcols(curwin, &curwin->w_cursor, &VIsual,
625 &leftcol, &rightcol);
626 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
627 if (m_pos.col < leftcol || m_pos.col > rightcol)
628 jump_flags = MOUSE_MAY_STOP_VIS;
629 }
630 }
631 }
632 else
633 jump_flags = MOUSE_MAY_STOP_VIS;
634 }
635 if (jump_flags)
636 {
637 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100638 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200639 setcursor();
640 out_flush(); // Update before showing popup menu
641 }
642# ifdef FEAT_MENU
643 show_popupmenu();
zeertzjq8e0ccb62022-11-01 18:35:27 +0000644 got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200645# endif
646 return (jump_flags & CURSOR_MOVED) != 0;
647#else
648 return FALSE;
649#endif
650 }
651 if (which_button == MOUSE_LEFT
652 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
653 {
654 which_button = MOUSE_RIGHT;
655 mod_mask &= ~MOD_MASK_SHIFT;
656 }
657 }
658
Bram Moolenaar24959102022-05-07 20:01:16 +0100659 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200660 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
661 {
662 if (which_button == MOUSE_LEFT)
663 {
664 if (is_click)
665 {
666 // stop Visual mode for a left click in a window, but not when
667 // on a status line
668 if (VIsual_active)
669 jump_flags |= MOUSE_MAY_STOP_VIS;
670 }
671 else if (mouse_has(MOUSE_VISUAL))
672 jump_flags |= MOUSE_MAY_VIS;
673 }
674 else if (which_button == MOUSE_RIGHT)
675 {
676 if (is_click && VIsual_active)
677 {
678 // Remember the start and end of visual before moving the
679 // cursor.
680 if (LT_POS(curwin->w_cursor, VIsual))
681 {
682 start_visual = curwin->w_cursor;
683 end_visual = VIsual;
684 }
685 else
686 {
687 start_visual = VIsual;
688 end_visual = curwin->w_cursor;
689 }
690 }
691 jump_flags |= MOUSE_FOCUS;
692 if (mouse_has(MOUSE_VISUAL))
693 jump_flags |= MOUSE_MAY_VIS;
694 }
695 }
696
697 // If an operator is pending, ignore all drags and releases until the
698 // next mouse click.
699 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
700 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000701 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200702 oap->motion_type = MCHAR;
703 }
704
705 // When releasing the button let jump_to_mouse() know.
706 if (!is_click && !is_drag)
707 jump_flags |= MOUSE_RELEASED;
708
709 // JUMP!
710 jump_flags = jump_to_mouse(jump_flags,
711 oap == NULL ? NULL : &(oap->inclusive), which_button);
712
713#ifdef FEAT_MENU
714 // A click in the window toolbar has no side effects.
715 if (jump_flags & MOUSE_WINBAR)
716 return FALSE;
717#endif
718 moved = (jump_flags & CURSOR_MOVED);
719 in_status_line = (jump_flags & IN_STATUS_LINE);
720 in_sep_line = (jump_flags & IN_SEP_LINE);
721
722#ifdef FEAT_NETBEANS_INTG
723 if (isNetbeansBuffer(curbuf)
724 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
725 {
726 int key = KEY2TERMCAP1(c);
727
728 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
729 || key == (int)KE_RIGHTRELEASE)
730 netbeans_button_release(which_button);
731 }
732#endif
733
734 // When jumping to another window, clear a pending operator. That's a bit
735 // friendlier than beeping and not jumping to that window.
736 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
737 clearop(oap);
738
739#ifdef FEAT_FOLDING
740 if (mod_mask == 0
741 && !is_drag
742 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
743 && which_button == MOUSE_LEFT)
744 {
745 // open or close a fold at this line
746 if (jump_flags & MOUSE_FOLD_OPEN)
747 openFold(curwin->w_cursor.lnum, 1L);
748 else
749 closeFold(curwin->w_cursor.lnum, 1L);
750 // don't move the cursor if still in the same window
751 if (curwin == old_curwin)
752 curwin->w_cursor = save_cursor;
753 }
754#endif
755
Martin Tournoij7904fa42022-10-04 16:28:45 +0100756#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200757 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
758 {
759 clip_modeless(which_button, is_click, is_drag);
760 return FALSE;
761 }
762#endif
763
764 // Set global flag that we are extending the Visual area with mouse
765 // dragging; temporarily minimize 'scrolloff'.
766 if (VIsual_active && is_drag && get_scrolloff_value())
767 {
768 // In the very first line, allow scrolling one line
769 if (mouse_row == 0)
770 mouse_dragging = 2;
771 else
772 mouse_dragging = 1;
773 }
774
775 // When dragging the mouse above the window, scroll down.
776 if (is_drag && mouse_row < 0 && !in_status_line)
777 {
778 scroll_redraw(FALSE, 1L);
779 mouse_row = 0;
780 }
781
782 if (start_visual.lnum) // right click in visual mode
783 {
784 // When ALT is pressed make Visual mode blockwise.
785 if (mod_mask & MOD_MASK_ALT)
786 VIsual_mode = Ctrl_V;
787
788 // In Visual-block mode, divide the area in four, pick up the corner
789 // that is in the quarter that the cursor is in.
790 if (VIsual_mode == Ctrl_V)
791 {
792 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
793 if (curwin->w_curswant > (leftcol + rightcol) / 2)
794 end_visual.col = leftcol;
795 else
796 end_visual.col = rightcol;
797 if (curwin->w_cursor.lnum >=
798 (start_visual.lnum + end_visual.lnum) / 2)
799 end_visual.lnum = start_visual.lnum;
800
801 // move VIsual to the right column
802 start_visual = curwin->w_cursor; // save the cursor pos
803 curwin->w_cursor = end_visual;
804 coladvance(end_visual.col);
805 VIsual = curwin->w_cursor;
806 curwin->w_cursor = start_visual; // restore the cursor
807 }
808 else
809 {
810 // If the click is before the start of visual, change the start.
811 // If the click is after the end of visual, change the end. If
812 // the click is inside the visual, change the closest side.
813 if (LT_POS(curwin->w_cursor, start_visual))
814 VIsual = end_visual;
815 else if (LT_POS(end_visual, curwin->w_cursor))
816 VIsual = start_visual;
817 else
818 {
819 // In the same line, compare column number
820 if (end_visual.lnum == start_visual.lnum)
821 {
822 if (curwin->w_cursor.col - start_visual.col >
823 end_visual.col - curwin->w_cursor.col)
824 VIsual = start_visual;
825 else
826 VIsual = end_visual;
827 }
828
829 // In different lines, compare line number
830 else
831 {
832 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
833 (end_visual.lnum - curwin->w_cursor.lnum);
834
835 if (diff > 0) // closest to end
836 VIsual = start_visual;
837 else if (diff < 0) // closest to start
838 VIsual = end_visual;
839 else // in the middle line
840 {
841 if (curwin->w_cursor.col <
842 (start_visual.col + end_visual.col) / 2)
843 VIsual = end_visual;
844 else
845 VIsual = start_visual;
846 }
847 }
848 }
849 }
850 }
851 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100852 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200853 stuffcharReadbuff(Ctrl_O);
854
855 // Middle mouse click: Put text before cursor.
856 if (which_button == MOUSE_MIDDLE)
857 {
858#ifdef FEAT_CLIPBOARD
859 if (clip_star.available && regname == 0)
860 regname = '*';
861#endif
862 if (yank_register_mline(regname))
863 {
864 if (mouse_past_bottom)
865 dir = FORWARD;
866 }
867 else if (mouse_past_eol)
868 dir = FORWARD;
869
870 if (fixindent)
871 {
872 c1 = (dir == BACKWARD) ? '[' : ']';
873 c2 = 'p';
874 }
875 else
876 {
877 c1 = (dir == FORWARD) ? 'p' : 'P';
878 c2 = NUL;
879 }
880 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
881
882 // Remember where the paste started, so in edit() Insstart can be set
883 // to this position
884 if (restart_edit != 0)
885 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200886 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200887 }
888
889#if defined(FEAT_QUICKFIX)
890 // Ctrl-Mouse click or double click in a quickfix window jumps to the
891 // error under the mouse pointer.
892 else if (((mod_mask & MOD_MASK_CTRL)
893 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
894 && bt_quickfix(curbuf))
895 {
896 if (curwin->w_llist_ref == NULL) // quickfix window
897 do_cmdline_cmd((char_u *)".cc");
898 else // location list window
899 do_cmdline_cmd((char_u *)".ll");
zeertzjq8e0ccb62022-11-01 18:35:27 +0000900 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200901 }
902#endif
903
904 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
905 // under the mouse pointer.
906 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
907 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
908 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100909 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200910 stuffcharReadbuff(Ctrl_O);
911 stuffcharReadbuff(Ctrl_RSB);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000912 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200913 }
914
915 // Shift-Mouse click searches for the next occurrence of the word under
916 // the mouse pointer
917 else if ((mod_mask & MOD_MASK_SHIFT))
918 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100919 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200920 stuffcharReadbuff(Ctrl_O);
921 if (which_button == MOUSE_LEFT)
922 stuffcharReadbuff('*');
923 else // MOUSE_RIGHT
924 stuffcharReadbuff('#');
925 }
926
927 // Handle double clicks, unless on status line
928 else if (in_status_line)
929 {
930#ifdef FEAT_MOUSESHAPE
931 if ((is_drag || is_click) && !drag_status_line)
932 {
933 drag_status_line = TRUE;
934 update_mouseshape(-1);
935 }
936#endif
937 }
938 else if (in_sep_line)
939 {
940#ifdef FEAT_MOUSESHAPE
941 if ((is_drag || is_click) && !drag_sep_line)
942 {
943 drag_sep_line = TRUE;
944 update_mouseshape(-1);
945 }
946#endif
947 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100948 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
949 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200950 && mouse_has(MOUSE_VISUAL))
951 {
952 if (is_click || !VIsual_active)
953 {
954 if (VIsual_active)
955 orig_cursor = VIsual;
956 else
957 {
958 check_visual_highlight();
959 VIsual = curwin->w_cursor;
960 orig_cursor = VIsual;
961 VIsual_active = TRUE;
962 VIsual_reselect = TRUE;
963 // start Select mode if 'selectmode' contains "mouse"
964 may_start_select('o');
965 setmouse();
966 }
967 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
968 {
969 // Double click with ALT pressed makes it blockwise.
970 if (mod_mask & MOD_MASK_ALT)
971 VIsual_mode = Ctrl_V;
972 else
973 VIsual_mode = 'v';
974 }
975 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
976 VIsual_mode = 'V';
977 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
978 VIsual_mode = Ctrl_V;
979#ifdef FEAT_CLIPBOARD
980 // Make sure the clipboard gets updated. Needed because start and
981 // end may still be the same, and the selection needs to be owned
982 clip_star.vmode = NUL;
983#endif
984 }
985 // A double click selects a word or a block.
986 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
987 {
988 pos_T *pos = NULL;
989 int gc;
990
991 if (is_click)
992 {
993 // If the character under the cursor (skipping white space) is
994 // not a word character, try finding a match and select a (),
995 // {}, [], #if/#endif, etc. block.
996 end_visual = curwin->w_cursor;
997 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
998 inc(&end_visual);
999 if (oap != NULL)
1000 oap->motion_type = MCHAR;
1001 if (oap != NULL
1002 && VIsual_mode == 'v'
1003 && !vim_iswordc(gchar_pos(&end_visual))
1004 && EQUAL_POS(curwin->w_cursor, VIsual)
1005 && (pos = findmatch(oap, NUL)) != NULL)
1006 {
1007 curwin->w_cursor = *pos;
1008 if (oap->motion_type == MLINE)
1009 VIsual_mode = 'V';
1010 else if (*p_sel == 'e')
1011 {
1012 if (LT_POS(curwin->w_cursor, VIsual))
1013 ++VIsual.col;
1014 else
1015 ++curwin->w_cursor.col;
1016 }
1017 }
1018 }
1019
1020 if (pos == NULL && (is_click || is_drag))
1021 {
1022 // When not found a match or when dragging: extend to include
1023 // a word.
1024 if (LT_POS(curwin->w_cursor, orig_cursor))
1025 {
1026 find_start_of_word(&curwin->w_cursor);
1027 find_end_of_word(&VIsual);
1028 }
1029 else
1030 {
1031 find_start_of_word(&VIsual);
1032 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1033 curwin->w_cursor.col +=
1034 (*mb_ptr2len)(ml_get_cursor());
1035 find_end_of_word(&curwin->w_cursor);
1036 }
1037 }
1038 curwin->w_set_curswant = TRUE;
1039 }
1040 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001041 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001042 }
1043 else if (VIsual_active && !old_active)
1044 {
1045 if (mod_mask & MOD_MASK_ALT)
1046 VIsual_mode = Ctrl_V;
1047 else
1048 VIsual_mode = 'v';
1049 }
1050
1051 // If Visual mode changed show it later.
1052 if ((!VIsual_active && old_active && mode_displayed)
1053 || (VIsual_active && p_smd && msg_silent == 0
1054 && (!old_active || VIsual_mode != old_mode)))
1055 redraw_cmdline = TRUE;
1056
1057 return moved;
1058}
1059
1060 void
1061ins_mouse(int c)
1062{
1063 pos_T tpos;
1064 win_T *old_curwin = curwin;
1065
Christopher Plewright696d0a82022-11-18 17:53:34 +00001066#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001067 // When GUI is active, also move/paste when 'mouse' is empty
1068 if (!gui.in_use)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001069#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001070 if (!mouse_has(MOUSE_INSERT))
1071 return;
1072
1073 undisplay_dollar();
1074 tpos = curwin->w_cursor;
1075 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1076 {
1077 win_T *new_curwin = curwin;
1078
1079 if (curwin != old_curwin && win_valid(old_curwin))
1080 {
1081 // Mouse took us to another window. We need to go back to the
1082 // previous one to stop insert there properly.
1083 curwin = old_curwin;
1084 curbuf = curwin->w_buffer;
1085#ifdef FEAT_JOB_CHANNEL
1086 if (bt_prompt(curbuf))
1087 // Restart Insert mode when re-entering the prompt buffer.
1088 curbuf->b_prompt_insert = 'A';
1089#endif
1090 }
1091 start_arrow(curwin == old_curwin ? &tpos : NULL);
1092 if (curwin != new_curwin && win_valid(new_curwin))
1093 {
1094 curwin = new_curwin;
1095 curbuf = curwin->w_buffer;
1096 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001097 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001098 }
1099
1100 // redraw status lines (in case another window became active)
1101 redraw_statuslines();
1102}
1103
Christopher Plewright44c22092022-11-15 17:43:36 +00001104/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001105 * Common mouse wheel scrolling, shared between Insert mode and NV modes.
1106 * Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
1107 * depending on the scroll direction) or one page when Shift or Ctrl is used.
1108 * Direction is indicated by "cap->arg":
1109 * K_MOUSEUP - MSCR_UP
1110 * K_MOUSEDOWN - MSCR_DOWN
1111 * K_MOUSELEFT - MSCR_LEFT
1112 * K_MOUSERIGHT - MSCR_RIGHT
1113 * "curwin" may have been changed to the window that should be scrolled and
1114 * differ from the window that actually has focus.
1115 */
1116 static void
1117do_mousescroll(cmdarg_T *cap)
1118{
1119 int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
1120
1121#ifdef FEAT_TERMINAL
1122 if (term_use_loop())
1123 // This window is a terminal window, send the mouse event there.
1124 // Set "typed" to FALSE to avoid an endless loop.
1125 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
1126 else
1127#endif
1128 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1129 {
1130 // Vertical scrolling
1131 if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
1132 {
1133 // whole page up or down
1134 onepage(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L);
1135 }
1136 else
1137 {
1138 if (mouse_vert_step < 0 || shift_or_ctrl)
1139 {
1140 // whole page up or down
1141 cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
1142 }
1143 // Don't scroll more than half the window height.
1144 else if (curwin->w_height < mouse_vert_step * 2)
1145 {
1146 cap->count1 = curwin->w_height / 2;
1147 if (cap->count1 == 0)
1148 cap->count1 = 1;
1149 }
1150 else
1151 {
1152 cap->count1 = mouse_vert_step;
1153 }
1154 cap->count0 = cap->count1;
1155 nv_scroll_line(cap);
1156 }
1157
1158#ifdef FEAT_PROP_POPUP
1159 if (WIN_IS_POPUP(curwin))
1160 popup_set_firstline(curwin);
1161#endif
1162 }
1163 else
1164 {
1165 // Horizontal scrolling
1166 long step = (mouse_hor_step < 0 || shift_or_ctrl)
1167 ? curwin->w_width : mouse_hor_step;
1168 long leftcol = curwin->w_leftcol
1169 + (cap->arg == MSCR_RIGHT ? -step : step);
1170 if (leftcol < 0)
1171 leftcol = 0;
1172 do_mousescroll_horiz((long_u)leftcol);
1173 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001174 may_trigger_win_scrolled_resized();
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001175}
1176
1177/*
1178 * Insert mode implementation for scrolling in direction "dir", which is
1179 * one of the MSCR_ values.
Christopher Plewright44c22092022-11-15 17:43:36 +00001180 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001181 void
1182ins_mousescroll(int dir)
1183{
Christopher Plewright696d0a82022-11-18 17:53:34 +00001184 cmdarg_T cap;
1185 oparg_T oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001186 CLEAR_FIELD(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00001187 clear_oparg(&oa);
1188 cap.oap = &oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001189 cap.arg = dir;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001190
Christopher Plewright44c22092022-11-15 17:43:36 +00001191 switch (dir)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001192 {
Christopher Plewright44c22092022-11-15 17:43:36 +00001193 case MSCR_UP:
1194 cap.cmdchar = K_MOUSEUP;
1195 break;
1196 case MSCR_DOWN:
1197 cap.cmdchar = K_MOUSEDOWN;
1198 break;
1199 case MSCR_LEFT:
1200 cap.cmdchar = K_MOUSELEFT;
1201 break;
1202 case MSCR_RIGHT:
1203 cap.cmdchar = K_MOUSERIGHT;
1204 break;
1205 default:
1206 siemsg("Invalid ins_mousescroll() argument: %d", dir);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001207 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00001208
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001209 win_T *old_curwin = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001210 if (mouse_row >= 0 && mouse_col >= 0)
1211 {
1212 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001213 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00001214 int row = mouse_row;
1215 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001216 curwin = mouse_find_win(&row, &col, FIND_POPUP);
1217 if (curwin == NULL)
1218 {
1219 curwin = old_curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001220 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001221 }
1222 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001223 }
1224
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001225 if (curwin == old_curwin)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001226 {
1227 // Don't scroll the current window if the popup menu is visible.
1228 if (pum_visible())
1229 return;
1230
1231 undisplay_dollar();
1232 }
1233
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001234 linenr_T orig_topline = curwin->w_topline;
1235 colnr_T orig_leftcol = curwin->w_leftcol;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001236 pos_T orig_cursor = curwin->w_cursor;
1237
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001238 // Call the common mouse scroll function shared with other modes.
1239 do_mousescroll(&cap);
1240
1241 int did_scroll = (orig_topline != curwin->w_topline
1242 || orig_leftcol != curwin->w_leftcol);
1243
1244 curwin->w_redr_status = TRUE;
1245 curwin = old_curwin;
1246 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001247
1248 // If the window actually scrolled and the popup menu may overlay the
1249 // window, need to redraw it.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001250 if (did_scroll && pum_visible())
Christopher Plewright696d0a82022-11-18 17:53:34 +00001251 {
1252 // TODO: Would be more efficient to only redraw the windows that are
1253 // overlapped by the popup menu.
1254 redraw_all_later(UPD_NOT_VALID);
1255 ins_compl_show_pum();
1256 }
1257
1258 if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
1259 {
1260 start_arrow(&orig_cursor);
1261 set_can_cindent(TRUE);
1262 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001263}
1264
1265/*
1266 * Return TRUE if "c" is a mouse key.
1267 */
1268 int
1269is_mouse_key(int c)
1270{
1271 return c == K_LEFTMOUSE
1272 || c == K_LEFTMOUSE_NM
1273 || c == K_LEFTDRAG
1274 || c == K_LEFTRELEASE
1275 || c == K_LEFTRELEASE_NM
1276 || c == K_MOUSEMOVE
1277 || c == K_MIDDLEMOUSE
1278 || c == K_MIDDLEDRAG
1279 || c == K_MIDDLERELEASE
1280 || c == K_RIGHTMOUSE
1281 || c == K_RIGHTDRAG
1282 || c == K_RIGHTRELEASE
1283 || c == K_MOUSEDOWN
1284 || c == K_MOUSEUP
1285 || c == K_MOUSELEFT
1286 || c == K_MOUSERIGHT
1287 || c == K_X1MOUSE
1288 || c == K_X1DRAG
1289 || c == K_X1RELEASE
1290 || c == K_X2MOUSE
1291 || c == K_X2DRAG
1292 || c == K_X2RELEASE;
1293}
1294
1295static struct mousetable
1296{
1297 int pseudo_code; // Code for pseudo mouse event
1298 int button; // Which mouse button is it?
1299 int is_click; // Is it a mouse button click event?
1300 int is_drag; // Is it a mouse drag event?
1301} mouse_table[] =
1302{
1303 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1304#ifdef FEAT_GUI
1305 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1306#endif
1307 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1308 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1309#ifdef FEAT_GUI
1310 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1311#endif
1312 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1313 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1314 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1315 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1316 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1317 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1318 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1319 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1320 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1321 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1322 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1323 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1324 // DRAG without CLICK
1325 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1326 // RELEASE without CLICK
1327 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1328 {0, 0, 0, 0},
1329};
1330
1331/*
1332 * Look up the given mouse code to return the relevant information in the other
1333 * arguments. Return which button is down or was released.
1334 */
1335 int
1336get_mouse_button(int code, int *is_click, int *is_drag)
1337{
1338 int i;
1339
1340 for (i = 0; mouse_table[i].pseudo_code; i++)
1341 if (code == mouse_table[i].pseudo_code)
1342 {
1343 *is_click = mouse_table[i].is_click;
1344 *is_drag = mouse_table[i].is_drag;
1345 return mouse_table[i].button;
1346 }
1347 return 0; // Shouldn't get here
1348}
1349
1350/*
1351 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1352 * the given information about which mouse button is down, and whether the
1353 * mouse was clicked, dragged or released.
1354 */
1355 int
1356get_pseudo_mouse_code(
1357 int button, // eg MOUSE_LEFT
1358 int is_click,
1359 int is_drag)
1360{
1361 int i;
1362
1363 for (i = 0; mouse_table[i].pseudo_code; i++)
1364 if (button == mouse_table[i].button
1365 && is_click == mouse_table[i].is_click
1366 && is_drag == mouse_table[i].is_drag)
1367 {
1368#ifdef FEAT_GUI
1369 // Trick: a non mappable left click and release has mouse_col -1
1370 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1371 // gui_mouse_moved()
1372 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1373 {
1374 if (mouse_col < 0)
1375 mouse_col = 0;
1376 else
1377 mouse_col -= MOUSE_COLOFF;
1378 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1379 return (int)KE_LEFTMOUSE_NM;
1380 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1381 return (int)KE_LEFTRELEASE_NM;
1382 }
1383#endif
1384 return mouse_table[i].pseudo_code;
1385 }
1386 return (int)KE_IGNORE; // not recognized, ignore it
1387}
1388
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001389# define HMT_NORMAL 1
1390# define HMT_NETTERM 2
1391# define HMT_DEC 4
1392# define HMT_JSBTERM 8
1393# define HMT_PTERM 16
1394# define HMT_URXVT 32
1395# define HMT_GPM 64
1396# define HMT_SGR 128
1397# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001398static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001399
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001400 void
1401set_mouse_termcode(
1402 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1403 char_u *s)
1404{
1405 char_u name[2];
1406
1407 name[0] = n;
1408 name[1] = KE_FILLER;
1409 add_termcode(name, s, FALSE);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001410#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001411 if (n == KS_JSBTERM_MOUSE)
1412 has_mouse_termcode |= HMT_JSBTERM;
1413 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001414#endif
1415#ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001416 if (n == KS_NETTERM_MOUSE)
1417 has_mouse_termcode |= HMT_NETTERM;
1418 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001419#endif
1420#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001421 if (n == KS_DEC_MOUSE)
1422 has_mouse_termcode |= HMT_DEC;
1423 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001424#endif
1425#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001426 if (n == KS_PTERM_MOUSE)
1427 has_mouse_termcode |= HMT_PTERM;
1428 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001429#endif
1430#ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001431 if (n == KS_URXVT_MOUSE)
1432 has_mouse_termcode |= HMT_URXVT;
1433 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001434#endif
1435#ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001436 if (n == KS_GPM_MOUSE)
1437 has_mouse_termcode |= HMT_GPM;
1438 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001439#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001440 if (n == KS_SGR_MOUSE)
1441 has_mouse_termcode |= HMT_SGR;
1442 else if (n == KS_SGR_MOUSE_RELEASE)
1443 has_mouse_termcode |= HMT_SGR_REL;
1444 else
1445 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001446}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001447
Christopher Plewright696d0a82022-11-18 17:53:34 +00001448#if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001449 void
1450del_mouse_termcode(
1451 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1452{
1453 char_u name[2];
1454
1455 name[0] = n;
1456 name[1] = KE_FILLER;
1457 del_termcode(name);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001458# ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001459 if (n == KS_JSBTERM_MOUSE)
1460 has_mouse_termcode &= ~HMT_JSBTERM;
1461 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001462# endif
1463# ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001464 if (n == KS_NETTERM_MOUSE)
1465 has_mouse_termcode &= ~HMT_NETTERM;
1466 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001467# endif
1468# ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001469 if (n == KS_DEC_MOUSE)
1470 has_mouse_termcode &= ~HMT_DEC;
1471 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001472# endif
1473# ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001474 if (n == KS_PTERM_MOUSE)
1475 has_mouse_termcode &= ~HMT_PTERM;
1476 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001477# endif
1478# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001479 if (n == KS_URXVT_MOUSE)
1480 has_mouse_termcode &= ~HMT_URXVT;
1481 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001482# endif
1483# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001484 if (n == KS_GPM_MOUSE)
1485 has_mouse_termcode &= ~HMT_GPM;
1486 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001487# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001488 if (n == KS_SGR_MOUSE)
1489 has_mouse_termcode &= ~HMT_SGR;
1490 else if (n == KS_SGR_MOUSE_RELEASE)
1491 has_mouse_termcode &= ~HMT_SGR_REL;
1492 else
1493 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001494}
Christopher Plewright696d0a82022-11-18 17:53:34 +00001495#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001496
1497/*
1498 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1499 */
1500 void
1501setmouse(void)
1502{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001503 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001504
Christopher Plewright696d0a82022-11-18 17:53:34 +00001505#ifdef FEAT_MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001506 update_mouseshape(-1);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001507#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001508
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001509 // Should be outside proc, but may break MOUSESHAPE
Christopher Plewright696d0a82022-11-18 17:53:34 +00001510#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001511 // In the GUI the mouse is always enabled.
1512 if (gui.in_use)
1513 return;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001514#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001515 // be quick when mouse is off
1516 if (*p_mouse == NUL || has_mouse_termcode == 0)
1517 return;
1518
1519 // don't switch mouse on when not in raw mode (Ex mode)
1520 if (cur_tmode != TMODE_RAW)
1521 {
1522 mch_setmouse(FALSE);
1523 return;
1524 }
1525
1526 if (VIsual_active)
1527 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001528 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1529 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001530 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001531 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001532 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001533 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001534 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001535 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001536 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1537 else
1538 checkfor = MOUSE_NORMAL; // assume normal mode
1539
1540 if (mouse_has(checkfor))
1541 mch_setmouse(TRUE);
1542 else
1543 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001544}
1545
1546/*
1547 * Return TRUE if
1548 * - "c" is in 'mouse', or
1549 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1550 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1551 * normal editing mode (not at hit-return message).
1552 */
1553 int
1554mouse_has(int c)
1555{
1556 char_u *p;
1557
1558 for (p = p_mouse; *p; ++p)
1559 switch (*p)
1560 {
1561 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1562 return TRUE;
1563 break;
1564 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1565 return TRUE;
1566 break;
1567 default: if (c == *p) return TRUE; break;
1568 }
1569 return FALSE;
1570}
1571
1572/*
1573 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1574 */
1575 int
1576mouse_model_popup(void)
1577{
1578 return (p_mousem[0] == 'p');
1579}
1580
zeertzjq8e0ccb62022-11-01 18:35:27 +00001581static win_T *dragwin = NULL; // window being dragged
1582
1583/*
1584 * Reset the window being dragged. To be called when switching tab page.
1585 */
1586 void
1587reset_dragwin(void)
1588{
1589 dragwin = NULL;
1590}
1591
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001592/*
1593 * Move the cursor to the specified row and column on the screen.
1594 * Change current window if necessary. Returns an integer with the
1595 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1596 *
1597 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1598 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1599 *
1600 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1601 * if the mouse is outside the window then the text will scroll, or if the
1602 * mouse was previously on a status line, then the status line may be dragged.
1603 *
1604 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1605 * cursor is moved unless the cursor was on a status line.
1606 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1607 * IN_SEP_LINE depending on where the cursor was clicked.
1608 *
1609 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1610 * the mouse is on the status line of the same window.
1611 *
1612 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1613 * the last call.
1614 *
1615 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1616 * remembered.
1617 */
1618 int
1619jump_to_mouse(
1620 int flags,
1621 int *inclusive, // used for inclusive operator, can be NULL
1622 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1623{
1624 static int on_status_line = 0; // #lines below bottom of window
1625 static int on_sep_line = 0; // on separator right of window
1626#ifdef FEAT_MENU
1627 static int in_winbar = FALSE;
1628#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001629#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001630 static int in_popup_win = FALSE;
1631 static win_T *click_in_popup_win = NULL;
1632#endif
1633 static int prev_row = -1;
1634 static int prev_col = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001635 static int did_drag = FALSE; // drag was noticed
1636
1637 win_T *wp, *old_curwin;
1638 pos_T old_cursor;
1639 int count;
1640 int first;
1641 int row = mouse_row;
1642 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001643 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001644#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001645 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001646#endif
1647
1648 mouse_past_bottom = FALSE;
1649 mouse_past_eol = FALSE;
1650
1651 if (flags & MOUSE_RELEASED)
1652 {
1653 // On button release we may change window focus if positioned on a
1654 // status line and no dragging happened.
1655 if (dragwin != NULL && !did_drag)
1656 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1657 dragwin = NULL;
1658 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001659#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001660 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1661 popup_close_for_mouse_click(click_in_popup_win);
1662
1663 popup_dragwin = NULL;
1664 click_in_popup_win = NULL;
1665#endif
1666 }
1667
1668 if ((flags & MOUSE_DID_MOVE)
1669 && prev_row == mouse_row
1670 && prev_col == mouse_col)
1671 {
1672retnomove:
1673 // before moving the cursor for a left click which is NOT in a status
1674 // line, stop Visual mode
1675 if (on_status_line)
1676 return IN_STATUS_LINE;
1677 if (on_sep_line)
1678 return IN_SEP_LINE;
1679#ifdef FEAT_MENU
1680 if (in_winbar)
1681 {
1682 // A quick second click may arrive as a double-click, but we use it
1683 // as a second click in the WinBar.
1684 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1685 {
1686 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1687 if (wp == NULL)
1688 return IN_UNKNOWN;
1689 winbar_click(wp, col);
1690 }
1691 return IN_OTHER_WIN | MOUSE_WINBAR;
1692 }
1693#endif
1694 if (flags & MOUSE_MAY_STOP_VIS)
1695 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001696 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001697 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001698 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001699#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001700 // Continue a modeless selection in another window.
1701 if (cmdwin_type != 0 && row < curwin->w_winrow)
1702 return IN_OTHER_WIN;
1703#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001704#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001705 // Continue a modeless selection in a popup window or dragging it.
1706 if (in_popup_win)
1707 {
1708 click_in_popup_win = NULL; // don't close it on release
1709 if (popup_dragwin != NULL)
1710 {
1711 // dragging a popup window
1712 popup_drag(popup_dragwin);
1713 return IN_UNKNOWN;
1714 }
1715 return IN_OTHER_WIN;
1716 }
1717#endif
1718 return IN_BUFFER;
1719 }
1720
1721 prev_row = mouse_row;
1722 prev_col = mouse_col;
1723
1724 if (flags & MOUSE_SETPOS)
1725 goto retnomove; // ugly goto...
1726
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001727 old_curwin = curwin;
1728 old_cursor = curwin->w_cursor;
1729
1730 if (!(flags & MOUSE_FOCUS))
1731 {
1732 if (row < 0 || col < 0) // check if it makes sense
1733 return IN_UNKNOWN;
1734
1735 // find the window where the row is in and adjust "row" and "col" to be
1736 // relative to top-left of the window
1737 wp = mouse_find_win(&row, &col, FIND_POPUP);
1738 if (wp == NULL)
1739 return IN_UNKNOWN;
1740 dragwin = NULL;
1741
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001742#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001743 // Click in a popup window may start dragging or modeless selection,
1744 // but not much else.
1745 if (WIN_IS_POPUP(wp))
1746 {
1747 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001748 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001749 in_popup_win = TRUE;
1750 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1751 {
1752 return IN_UNKNOWN;
1753 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001754 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001755 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001756 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001757 {
1758 popup_dragwin = wp;
1759 popup_start_drag(wp, row, col);
1760 return IN_UNKNOWN;
1761 }
1762 // Only close on release, otherwise it's not possible to drag or do
1763 // modeless selection.
1764 else if (wp->w_popup_close == POPCLOSE_CLICK
1765 && which_button == MOUSE_LEFT)
1766 {
1767 click_in_popup_win = wp;
1768 }
1769 else if (which_button == MOUSE_LEFT)
1770 // If the click is in the scrollbar, may scroll up/down.
1771 popup_handle_scrollbar_click(wp, row, col);
1772# ifdef FEAT_CLIPBOARD
1773 return IN_OTHER_WIN;
1774# else
1775 return IN_UNKNOWN;
1776# endif
1777 }
1778 in_popup_win = FALSE;
1779 popup_dragwin = NULL;
1780#endif
1781#ifdef FEAT_MENU
1782 if (row == -1)
1783 {
1784 // A click in the window toolbar does not enter another window or
1785 // change Visual highlighting.
1786 winbar_click(wp, col);
1787 in_winbar = TRUE;
1788 return IN_OTHER_WIN | MOUSE_WINBAR;
1789 }
1790 in_winbar = FALSE;
1791#endif
1792
1793 // winpos and height may change in win_enter()!
1794 if (row >= wp->w_height) // In (or below) status line
1795 {
1796 on_status_line = row - wp->w_height + 1;
1797 dragwin = wp;
1798 }
1799 else
1800 on_status_line = 0;
1801 if (col >= wp->w_width) // In separator line
1802 {
1803 on_sep_line = col - wp->w_width + 1;
1804 dragwin = wp;
1805 }
1806 else
1807 on_sep_line = 0;
1808
1809 // The rightmost character of the status line might be a vertical
1810 // separator character if there is no connecting window to the right.
1811 if (on_status_line && on_sep_line)
1812 {
1813 if (stl_connected(wp))
1814 on_sep_line = 0;
1815 else
1816 on_status_line = 0;
1817 }
1818
1819 // Before jumping to another buffer, or moving the cursor for a left
1820 // click, stop Visual mode.
1821 if (VIsual_active
1822 && (wp->w_buffer != curwin->w_buffer
1823 || (!on_status_line && !on_sep_line
1824#ifdef FEAT_FOLDING
1825 && (
1826# ifdef FEAT_RIGHTLEFT
1827 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1828# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01001829 col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001830 )
1831#endif
1832 && (flags & MOUSE_MAY_STOP_VIS))))
1833 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001834 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001835 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001836 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001837 if (cmdwin_type != 0 && wp != curwin)
1838 {
1839 // A click outside the command-line window: Use modeless
1840 // selection if possible. Allow dragging the status lines.
1841 on_sep_line = 0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001842#ifdef FEAT_CLIPBOARD
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001843 if (on_status_line)
1844 return IN_STATUS_LINE;
1845 return IN_OTHER_WIN;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001846#else
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001847 row = 0;
1848 col += wp->w_wincol;
1849 wp = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001850#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001851 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001852#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1853 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1854 // terminal in popup window: don't jump to another window
1855 return IN_OTHER_WIN;
1856#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001857 // Only change window focus when not clicking on or dragging the
1858 // status line. Do change focus when releasing the mouse button
1859 // (MOUSE_FOCUS was set above if we dragged first).
1860 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1861 win_enter(wp, TRUE); // can make wp invalid!
1862
1863 if (curwin != old_curwin)
1864 {
1865#ifdef CHECK_DOUBLE_CLICK
1866 // set topline, to be able to check for double click ourselves
1867 set_mouse_topline(curwin);
1868#endif
1869#ifdef FEAT_TERMINAL
1870 // when entering a terminal window may change state
1871 term_win_entered();
1872#endif
1873 }
1874 if (on_status_line) // In (or below) status line
1875 {
1876 // Don't use start_arrow() if we're in the same window
1877 if (curwin == old_curwin)
1878 return IN_STATUS_LINE;
1879 else
1880 return IN_STATUS_LINE | CURSOR_MOVED;
1881 }
1882 if (on_sep_line) // In (or below) status line
1883 {
1884 // Don't use start_arrow() if we're in the same window
1885 if (curwin == old_curwin)
1886 return IN_SEP_LINE;
1887 else
1888 return IN_SEP_LINE | CURSOR_MOVED;
1889 }
1890
1891 curwin->w_cursor.lnum = curwin->w_topline;
1892#ifdef FEAT_GUI
1893 // remember topline, needed for double click
1894 gui_prev_topline = curwin->w_topline;
1895# ifdef FEAT_DIFF
1896 gui_prev_topfill = curwin->w_topfill;
1897# endif
1898#endif
1899 }
1900 else if (on_status_line && which_button == MOUSE_LEFT)
1901 {
1902 if (dragwin != NULL)
1903 {
1904 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001905 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001906 - on_status_line;
1907 win_drag_status_line(dragwin, count);
1908 did_drag |= count;
1909 }
1910 return IN_STATUS_LINE; // Cursor didn't move
1911 }
1912 else if (on_sep_line && which_button == MOUSE_LEFT)
1913 {
1914 if (dragwin != NULL)
1915 {
1916 // Drag the separator column
1917 count = col - dragwin->w_wincol - dragwin->w_width + 1
1918 - on_sep_line;
1919 win_drag_vsep_line(dragwin, count);
1920 did_drag |= count;
1921 }
1922 return IN_SEP_LINE; // Cursor didn't move
1923 }
1924#ifdef FEAT_MENU
1925 else if (in_winbar)
1926 {
1927 // After a click on the window toolbar don't start Visual mode.
1928 return IN_OTHER_WIN | MOUSE_WINBAR;
1929 }
1930#endif
1931 else // keep_window_focus must be TRUE
1932 {
1933 // before moving the cursor for a left click, stop Visual mode
1934 if (flags & MOUSE_MAY_STOP_VIS)
1935 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001936 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001937 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001938 }
1939
Martin Tournoij7904fa42022-10-04 16:28:45 +01001940#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001941 // Continue a modeless selection in another window.
1942 if (cmdwin_type != 0 && row < curwin->w_winrow)
1943 return IN_OTHER_WIN;
1944#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001945#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001946 if (in_popup_win)
1947 {
1948 if (popup_dragwin != NULL)
1949 {
1950 // dragging a popup window
1951 popup_drag(popup_dragwin);
1952 return IN_UNKNOWN;
1953 }
1954 // continue a modeless selection in a popup window
1955 click_in_popup_win = NULL;
1956 return IN_OTHER_WIN;
1957 }
1958#endif
1959
1960 row -= W_WINROW(curwin);
1961 col -= curwin->w_wincol;
1962
1963 // When clicking beyond the end of the window, scroll the screen.
1964 // Scroll by however many rows outside the window we are.
1965 if (row < 0)
1966 {
1967 count = 0;
1968 for (first = TRUE; curwin->w_topline > 1; )
1969 {
1970#ifdef FEAT_DIFF
1971 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1972 ++count;
1973 else
1974#endif
1975 count += plines(curwin->w_topline - 1);
1976 if (!first && count > -row)
1977 break;
1978 first = FALSE;
1979#ifdef FEAT_FOLDING
1980 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1981#endif
1982#ifdef FEAT_DIFF
1983 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1984 ++curwin->w_topfill;
1985 else
1986#endif
1987 {
1988 --curwin->w_topline;
1989#ifdef FEAT_DIFF
1990 curwin->w_topfill = 0;
1991#endif
1992 }
1993 }
1994#ifdef FEAT_DIFF
1995 check_topfill(curwin, FALSE);
1996#endif
1997 curwin->w_valid &=
1998 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001999 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002000 row = 0;
2001 }
2002 else if (row >= curwin->w_height)
2003 {
2004 count = 0;
2005 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
2006 {
2007#ifdef FEAT_DIFF
2008 if (curwin->w_topfill > 0)
2009 ++count;
2010 else
2011#endif
2012 count += plines(curwin->w_topline);
2013 if (!first && count > row - curwin->w_height + 1)
2014 break;
2015 first = FALSE;
2016#ifdef FEAT_FOLDING
2017 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
2018 && curwin->w_topline == curbuf->b_ml.ml_line_count)
2019 break;
2020#endif
2021#ifdef FEAT_DIFF
2022 if (curwin->w_topfill > 0)
2023 --curwin->w_topfill;
2024 else
2025#endif
2026 {
2027 ++curwin->w_topline;
2028#ifdef FEAT_DIFF
2029 curwin->w_topfill =
2030 diff_check_fill(curwin, curwin->w_topline);
2031#endif
2032 }
2033 }
2034#ifdef FEAT_DIFF
2035 check_topfill(curwin, FALSE);
2036#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002037 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002038 curwin->w_valid &=
2039 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2040 row = curwin->w_height - 1;
2041 }
2042 else if (row == 0)
2043 {
2044 // When dragging the mouse, while the text has been scrolled up as
2045 // far as it goes, moving the mouse in the top line should scroll
2046 // the text down (done later when recomputing w_topline).
2047 if (mouse_dragging > 0
2048 && curwin->w_cursor.lnum
2049 == curwin->w_buffer->b_ml.ml_line_count
2050 && curwin->w_cursor.lnum == curwin->w_topline)
2051 curwin->w_valid &= ~(VALID_TOPLINE);
2052 }
2053 }
2054
Bram Moolenaarb9081882022-07-09 04:56:24 +01002055 if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns
2056 && ScreenLines != NULL)
2057 {
2058 int off = LineOffset[prev_row] + prev_col;
2059
2060 // Only use ScreenCols[] after the window was redrawn. Mainly matters
2061 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01002062 // Do not use when 'virtualedit' is active.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002063 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE && !virtual_active())
Bram Moolenaarb9081882022-07-09 04:56:24 +01002064 col_from_screen = ScreenCols[off];
2065#ifdef FEAT_FOLDING
2066 // Remember the character under the mouse, it might be a '-' or '+' in
2067 // the fold column.
2068 mouse_char = ScreenLines[off];
2069#endif
2070 }
2071
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002072#ifdef FEAT_FOLDING
2073 // Check for position outside of the fold column.
2074 if (
2075# ifdef FEAT_RIGHTLEFT
2076 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
2077# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01002078 col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002079 )
2080 mouse_char = ' ';
2081#endif
2082
2083 // compute the position in the buffer line from the posn on the screen
2084 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
2085 mouse_past_bottom = TRUE;
2086
2087 // Start Visual mode before coladvance(), for when 'sel' != "old"
2088 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2089 {
2090 check_visual_highlight();
2091 VIsual = old_cursor;
2092 VIsual_active = TRUE;
2093 VIsual_reselect = TRUE;
2094 // if 'selectmode' contains "mouse", start Select mode
2095 may_start_select('o');
2096 setmouse();
2097 if (p_smd && msg_silent == 0)
2098 redraw_cmdline = TRUE; // show visual mode later
2099 }
2100
Bram Moolenaarb9081882022-07-09 04:56:24 +01002101 if (col_from_screen >= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002102 {
Bram Moolenaarb9081882022-07-09 04:56:24 +01002103 // Use the column from ScreenCols[], it is accurate also after
2104 // concealed characters.
2105 curwin->w_cursor.col = col_from_screen;
2106 if (col_from_screen == MAXCOL)
2107 {
2108 curwin->w_curswant = col_from_screen;
2109 curwin->w_set_curswant = FALSE; // May still have been TRUE
2110 mouse_past_eol = TRUE;
2111 if (inclusive != NULL)
2112 *inclusive = TRUE;
2113 }
2114 else
2115 {
2116 curwin->w_set_curswant = TRUE;
2117 if (inclusive != NULL)
2118 *inclusive = FALSE;
2119 }
2120 check_cursor_col();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002121 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002122 else
2123 {
2124 curwin->w_curswant = col;
2125 curwin->w_set_curswant = FALSE; // May still have been TRUE
2126 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2127 {
2128 if (inclusive != NULL)
2129 *inclusive = TRUE;
2130 mouse_past_eol = TRUE;
2131 }
2132 else if (inclusive != NULL)
2133 *inclusive = FALSE;
2134 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002135
2136 count = IN_BUFFER;
2137 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2138 || curwin->w_cursor.col != old_cursor.col)
2139 count |= CURSOR_MOVED; // Cursor has moved
2140
Christopher Plewright696d0a82022-11-18 17:53:34 +00002141#ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002142 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002143 count |= MOUSE_FOLD_OPEN;
2144 else if (mouse_char != ' ')
2145 count |= MOUSE_FOLD_CLOSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002146#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002147
2148 return count;
2149}
2150
2151/*
Christopher Plewright44c22092022-11-15 17:43:36 +00002152 * Make a horizontal scroll to "leftcol".
2153 * Return TRUE if the cursor moved, FALSE otherwise.
2154 */
2155 int
2156do_mousescroll_horiz(long_u leftcol)
2157{
2158 if (curwin->w_p_wrap)
2159 return FALSE; // no wrapping, no scrolling
2160
2161 if (curwin->w_leftcol == (colnr_T)leftcol)
2162 return FALSE; // already there
2163
Christopher Plewright44c22092022-11-15 17:43:36 +00002164 // When the line of the cursor is too short, move the cursor to the
2165 // longest visible line.
2166 if (
2167#ifdef FEAT_GUI
2168 (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
2169#endif
2170 !virtual_active()
2171 && (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
2172 {
2173 curwin->w_cursor.lnum = ui_find_longest_lnum();
2174 curwin->w_cursor.col = 0;
2175 }
2176
Bram Moolenaar0c34d562022-11-18 14:07:20 +00002177 return set_leftcol((colnr_T)leftcol);
Christopher Plewright44c22092022-11-15 17:43:36 +00002178}
2179
2180/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002181 * Normal and Visual modes implementation for scrolling in direction
2182 * "cap->arg", which is one of the MSCR_ values.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002183 */
2184 void
Christopher Plewright696d0a82022-11-18 17:53:34 +00002185nv_mousescroll(cmdarg_T *cap)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002186{
Christopher Plewright696d0a82022-11-18 17:53:34 +00002187 win_T *old_curwin = curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002188
2189 if (mouse_row >= 0 && mouse_col >= 0)
2190 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002191 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002192 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00002193 int row = mouse_row;
2194 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002195 curwin = mouse_find_win(&row, &col, FIND_POPUP);
2196 if (curwin == NULL)
2197 {
2198 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002199 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002200 }
2201
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002202#ifdef FEAT_PROP_POPUP
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002203 if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
2204 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002205 // cannot scroll this popup window
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002206 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002207 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002208 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002209#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002210 curbuf = curwin->w_buffer;
2211 }
Christopher Plewright44c22092022-11-15 17:43:36 +00002212
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002213 // Call the common mouse scroll function shared with other modes.
2214 do_mousescroll(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00002215
Christopher Plewright696d0a82022-11-18 17:53:34 +00002216#ifdef FEAT_SYN_HL
2217 if (curwin != old_curwin && curwin->w_p_cul)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002218 redraw_for_cursorline(curwin);
Christopher Plewright696d0a82022-11-18 17:53:34 +00002219#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002220 curwin->w_redr_status = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002221 curwin = old_curwin;
2222 curbuf = curwin->w_buffer;
2223}
2224
2225/*
2226 * Mouse clicks and drags.
2227 */
2228 void
2229nv_mouse(cmdarg_T *cap)
2230{
2231 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2232}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002233
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002234static int held_button = MOUSE_RELEASE;
2235
2236 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00002237reset_held_button(void)
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002238{
2239 held_button = MOUSE_RELEASE;
2240}
2241
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002242/*
2243 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2244 * modifiers found in typebuf in 'modifiers'.
2245 */
2246 int
2247check_termcode_mouse(
2248 char_u *tp,
2249 int *slen,
2250 char_u *key_name,
2251 char_u *modifiers_start,
2252 int idx,
2253 int *modifiers)
2254{
2255 int j;
2256 char_u *p;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002257#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002258 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2259 char_u bytes[6];
2260 int num_bytes;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002261#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002262 int mouse_code = 0; // init for GCC
2263 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002264 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002265 int wheel_code = 0;
2266 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002267 static int orig_num_clicks = 1;
2268 static int orig_mouse_code = 0x0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002269#ifdef CHECK_DOUBLE_CLICK
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002270 static int orig_mouse_col = 0;
2271 static int orig_mouse_row = 0;
2272 static struct timeval orig_mouse_time = {0, 0};
2273 // time of previous mouse click
2274 struct timeval mouse_time; // time of current mouse click
2275 long timediff; // elapsed time in msec
Christopher Plewright696d0a82022-11-18 17:53:34 +00002276#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002277
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002278 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002279
Christopher Plewright696d0a82022-11-18 17:53:34 +00002280#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002281 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2282 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002283# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002284 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002285# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002286 )
2287 {
2288 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002289 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002290 * 0x20 = left button down
2291 * 0x21 = middle button down
2292 * 0x22 = right button down
2293 * 0x23 = any button release
2294 * 0x60 = button 4 down (scroll wheel down)
2295 * 0x61 = button 5 down (scroll wheel up)
2296 * add 0x04 for SHIFT
2297 * add 0x08 for ALT
2298 * add 0x10 for CTRL
2299 * add 0x20 for mouse drag (0x40 is drag with left button)
2300 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2301 * 0x43 (drag + release) is also move
2302 * c == column + ' ' + 1 == column + 33
2303 * r == row + ' ' + 1 == row + 33
2304 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002305 * The coordinates are passed on through global variables. Ugly, but
2306 * this avoids trouble with mouse clicks at an unexpected moment and
2307 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002308 */
2309 for (;;)
2310 {
Christopher Plewright36446bb2022-11-23 22:28:08 +00002311 // For the GUI and for MS-Windows two bytes each are used for row
2312 // and column. Allows for more than 223 columns.
2313# if defined(FEAT_GUI) || defined(MSWIN)
2314 if (TRUE
2315# if defined(FEAT_GUI) && !defined(MSWIN)
2316 && gui.in_use
2317# endif
2318 )
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002319 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002320 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2321 if (num_bytes == -1) // not enough coordinates
2322 return -1;
2323 mouse_code = bytes[0];
2324 mouse_col = 128 * (bytes[1] - ' ' - 1)
2325 + bytes[2] - ' ' - 1;
2326 mouse_row = 128 * (bytes[3] - ' ' - 1)
2327 + bytes[4] - ' ' - 1;
2328 }
2329 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002330# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002331 {
2332 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2333 if (num_bytes == -1) // not enough coordinates
2334 return -1;
2335 mouse_code = bytes[0];
2336 mouse_col = bytes[1] - ' ' - 1;
2337 mouse_row = bytes[2] - ' ' - 1;
2338 }
2339 *slen += num_bytes;
2340
Bram Moolenaar13c04632020-07-12 13:47:42 +02002341 // If the following bytes is also a mouse code and it has the same
2342 // code, dump this one and get the next. This makes dragging a
2343 // whole lot faster.
Christopher Plewright696d0a82022-11-18 17:53:34 +00002344# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002345 if (gui.in_use)
2346 j = 3;
2347 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002348# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002349 j = get_termcode_len(idx);
2350 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2351 && tp[*slen + j] == mouse_code
2352 && tp[*slen + j + 1] != NUL
2353 && tp[*slen + j + 2] != NUL
Christopher Plewright696d0a82022-11-18 17:53:34 +00002354# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002355 && (!gui.in_use
2356 || (tp[*slen + j + 3] != NUL
2357 && tp[*slen + j + 4] != NUL))
Christopher Plewright696d0a82022-11-18 17:53:34 +00002358# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002359 )
2360 *slen += j;
2361 else
2362 break;
2363 }
2364 }
2365
2366 if (key_name[0] == KS_URXVT_MOUSE
2367 || key_name[0] == KS_SGR_MOUSE
2368 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2369 {
2370 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002371 // Almost identical to xterm mouse mode, except the values are decimal
2372 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002373 //
2374 // \033[%d;%d;%dM
2375 // ^-- row
2376 // ^----- column
2377 // ^-------- code
2378 //
2379 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002380 // Almost identical to xterm mouse mode, except the values are decimal
2381 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002382 //
2383 // \033[<%d;%d;%dM
2384 // ^-- row
2385 // ^----- column
2386 // ^-------- code
2387 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002388 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002389 // ^-- row
2390 // ^----- column
2391 // ^-------- code
2392 p = modifiers_start;
2393 if (p == NULL)
2394 return -1;
2395
2396 mouse_code = getdigits(&p);
2397 if (*p++ != ';')
2398 return -1;
2399
2400 // when mouse reporting is SGR, add 32 to mouse code
2401 if (key_name[0] == KS_SGR_MOUSE
2402 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2403 mouse_code += 32;
2404
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002405 mouse_col = getdigits(&p) - 1;
2406 if (*p++ != ';')
2407 return -1;
2408
2409 mouse_row = getdigits(&p) - 1;
2410
Bram Moolenaar13c04632020-07-12 13:47:42 +02002411 // The modifiers were the mouse coordinates, not the modifier keys
2412 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002413 *modifiers = 0;
2414 }
2415
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002416 if (key_name[0] == KS_SGR_MOUSE
2417 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2418 {
2419 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002420 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002421 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002422 // This is used below to set held_button.
2423 mouse_code |= MOUSE_RELEASE;
2424 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002425 }
2426 else
2427 {
2428 release_is_ambiguous = TRUE;
2429 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2430 is_release = TRUE;
2431 }
2432
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002433 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002434# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002435 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002436# endif
2437# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002438 || key_name[0] == KS_URXVT_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002439# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002440 || key_name[0] == KS_SGR_MOUSE
2441 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2442 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002443# if !defined(MSWIN)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002444 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002445 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002446 * Recognize the xterm mouse wheel, but not in the GUI, the
2447 * Linux console with GPM and the MS-DOS or Win32 console
2448 * (multi-clicks use >= 0x60).
2449 */
2450 if (mouse_code >= MOUSEWHEEL_LOW
Christopher Plewright696d0a82022-11-18 17:53:34 +00002451# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002452 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002453# endif
2454# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002455 && key_name[0] != KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002456# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002457 )
2458 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002459# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002460 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2461 // mouse-move event, using MOUSE_DRAG works
2462 mouse_code = MOUSE_DRAG;
2463 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002464# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002465 // Keep the mouse_code before it's changed, so that we
2466 // remember that it was a mouse wheel click.
2467 wheel_code = mouse_code;
2468 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002469# ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002470 else if (held_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002471# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002472 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002473# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002474 && (mouse_code == 0x23 || mouse_code == 0x24
2475 || mouse_code == 0x40 || mouse_code == 0x41))
2476 {
2477 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2478 // And 0x40 and 0x41 are used by some xterm emulator.
2479 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002480 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002481 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002482# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002483
Christopher Plewright696d0a82022-11-18 17:53:34 +00002484# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002485 else if (use_xterm_mouse() > 1)
2486 {
2487 if (mouse_code & MOUSE_DRAG_XTERM)
2488 mouse_code |= MOUSE_DRAG;
2489 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002490# endif
2491# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002492 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2493 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002494 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002495 stop_xterm_trace();
2496 else
2497 start_xterm_trace(mouse_code);
2498 }
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002499# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002500# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002501 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002502#endif // !UNIX || FEAT_MOUSE_XTERM
2503#ifdef FEAT_MOUSE_NET
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002504 if (key_name[0] == KS_NETTERM_MOUSE)
2505 {
2506 int mc, mr;
2507
2508 // expect a rather limited sequence like: balancing {
2509 // \033}6,45\r
2510 // '6' is the row, 45 is the column
2511 p = tp + *slen;
2512 mr = getdigits(&p);
2513 if (*p++ != ',')
2514 return -1;
2515 mc = getdigits(&p);
2516 if (*p++ != '\r')
2517 return -1;
2518
2519 mouse_col = mc - 1;
2520 mouse_row = mr - 1;
2521 mouse_code = MOUSE_LEFT;
2522 *slen += (int)(p - (tp + *slen));
2523 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002524#endif // FEAT_MOUSE_NET
2525#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002526 if (key_name[0] == KS_JSBTERM_MOUSE)
2527 {
2528 int mult, val, iter, button, status;
2529
2530 /*
2531 * JSBTERM Input Model
2532 * \033[0~zw uniq escape sequence
2533 * (L-x) Left button pressed - not pressed x not reporting
2534 * (M-x) Middle button pressed - not pressed x not reporting
2535 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002536 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2537 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002538 * ### X cursor position padded to 3 digits
2539 * ### Y cursor position padded to 3 digits
2540 * (s-x) SHIFT key pressed - not pressed x not reporting
2541 * (c-x) CTRL key pressed - not pressed x not reporting
2542 * \033\\ terminating sequence
2543 */
2544 p = tp + *slen;
2545 button = mouse_code = 0;
2546 switch (*p++)
2547 {
2548 case 'L': button = 1; break;
2549 case '-': break;
2550 case 'x': break; // ignore sequence
2551 default: return -1; // Unknown Result
2552 }
2553 switch (*p++)
2554 {
2555 case 'M': button |= 2; break;
2556 case '-': break;
2557 case 'x': break; // ignore sequence
2558 default: return -1; // Unknown Result
2559 }
2560 switch (*p++)
2561 {
2562 case 'R': button |= 4; break;
2563 case '-': break;
2564 case 'x': break; // ignore sequence
2565 default: return -1; // Unknown Result
2566 }
2567 status = *p++;
2568 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2569 mult /= 10, p++)
2570 if (*p >= '0' && *p <= '9')
2571 val += (*p - '0') * mult;
2572 else
2573 return -1;
2574 mouse_col = val;
2575 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2576 mult /= 10, p++)
2577 if (*p >= '0' && *p <= '9')
2578 val += (*p - '0') * mult;
2579 else
2580 return -1;
2581 mouse_row = val;
2582 switch (*p++)
2583 {
2584 case 's': button |= 8; break; // SHIFT key Pressed
2585 case '-': break; // Not Pressed
2586 case 'x': break; // Not Reporting
2587 default: return -1; // Unknown Result
2588 }
2589 switch (*p++)
2590 {
2591 case 'c': button |= 16; break; // CTRL key Pressed
2592 case '-': break; // Not Pressed
2593 case 'x': break; // Not Reporting
2594 default: return -1; // Unknown Result
2595 }
2596 if (*p++ != '\033')
2597 return -1;
2598 if (*p++ != '\\')
2599 return -1;
2600 switch (status)
2601 {
2602 case 'D': // Double Click
2603 case 'S': // Single Click
2604 if (button & 1) mouse_code |= MOUSE_LEFT;
2605 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2606 if (button & 4) mouse_code |= MOUSE_RIGHT;
2607 if (button & 8) mouse_code |= MOUSE_SHIFT;
2608 if (button & 16) mouse_code |= MOUSE_CTRL;
2609 break;
2610 case 'm': // Mouse move
2611 if (button & 1) mouse_code |= MOUSE_LEFT;
2612 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2613 if (button & 4) mouse_code |= MOUSE_RIGHT;
2614 if (button & 8) mouse_code |= MOUSE_SHIFT;
2615 if (button & 16) mouse_code |= MOUSE_CTRL;
2616 if ((button & 7) != 0)
2617 {
2618 held_button = mouse_code;
2619 mouse_code |= MOUSE_DRAG;
2620 }
2621 is_drag = TRUE;
2622 showmode();
2623 break;
2624 case 'd': // Button Down
2625 if (button & 1) mouse_code |= MOUSE_LEFT;
2626 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2627 if (button & 4) mouse_code |= MOUSE_RIGHT;
2628 if (button & 8) mouse_code |= MOUSE_SHIFT;
2629 if (button & 16) mouse_code |= MOUSE_CTRL;
2630 break;
2631 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002632 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002633 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002634 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002635 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002636 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002637 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002638 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002639 if (button & 8)
2640 mouse_code |= MOUSE_SHIFT;
2641 if (button & 16)
2642 mouse_code |= MOUSE_CTRL;
2643 break;
2644 default: return -1; // Unknown Result
2645 }
2646
2647 *slen += (p - (tp + *slen));
2648 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002649#endif // FEAT_MOUSE_JSB
2650#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002651 if (key_name[0] == KS_DEC_MOUSE)
2652 {
2653 /*
2654 * The DEC Locator Input Model
2655 * Netterm delivers the code sequence:
2656 * \033[2;4;24;80&w (left button down)
2657 * \033[3;0;24;80&w (left button up)
2658 * \033[6;1;24;80&w (right button down)
2659 * \033[7;0;24;80&w (right button up)
2660 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2661 * Pe is the event code
2662 * Pb is the button code
2663 * Pr is the row coordinate
2664 * Pc is the column coordinate
2665 * Pp is the third coordinate (page number)
2666 * Pe, the event code indicates what event caused this report
2667 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002668 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002669 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002670 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002671 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002672 * 2 - left button down
2673 * 3 - left button up
2674 * 4 - middle button down
2675 * 5 - middle button up
2676 * 6 - right button down
2677 * 7 - right button up
2678 * 8 - fourth button down
2679 * 9 - fourth button up
2680 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002681 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2682 * down if any. The state of the four buttons on the locator
2683 * correspond to the low four bits of the decimal value, "1" means
2684 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002685 * 0 - no buttons down,
2686 * 1 - right,
2687 * 2 - middle,
2688 * 4 - left,
2689 * 8 - fourth
2690 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002691 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2692 * position is undefined (outside the terminal window for example).
2693 * Pc is the column coordinate of the locator position in the page,
2694 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2695 * position is undefined (outside the terminal window for example).
2696 * Pp is the page coordinate of the locator position encoded as an
2697 * ASCII decimal value. The page coordinate may be omitted if the
2698 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002699 */
2700 int Pe, Pb, Pr, Pc;
2701
2702 p = tp + *slen;
2703
2704 // get event status
2705 Pe = getdigits(&p);
2706 if (*p++ != ';')
2707 return -1;
2708
2709 // get button status
2710 Pb = getdigits(&p);
2711 if (*p++ != ';')
2712 return -1;
2713
2714 // get row status
2715 Pr = getdigits(&p);
2716 if (*p++ != ';')
2717 return -1;
2718
2719 // get column status
2720 Pc = getdigits(&p);
2721
2722 // the page parameter is optional
2723 if (*p == ';')
2724 {
2725 p++;
2726 (void)getdigits(&p);
2727 }
2728 if (*p++ != '&')
2729 return -1;
2730 if (*p++ != 'w')
2731 return -1;
2732
2733 mouse_code = 0;
2734 switch (Pe)
2735 {
2736 case 0: return -1; // position request while unavailable
2737 case 1: // a response to a locator position request includes
2738 // the status of all buttons
2739 Pb &= 7; // mask off and ignore fourth button
2740 if (Pb & 4)
2741 mouse_code = MOUSE_LEFT;
2742 if (Pb & 2)
2743 mouse_code = MOUSE_MIDDLE;
2744 if (Pb & 1)
2745 mouse_code = MOUSE_RIGHT;
2746 if (Pb)
2747 {
2748 held_button = mouse_code;
2749 mouse_code |= MOUSE_DRAG;
2750 WantQueryMouse = TRUE;
2751 }
2752 is_drag = TRUE;
2753 showmode();
2754 break;
2755 case 2: mouse_code = MOUSE_LEFT;
2756 WantQueryMouse = TRUE;
2757 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002758 case 3: mouse_code = MOUSE_LEFT;
2759 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002760 break;
2761 case 4: mouse_code = MOUSE_MIDDLE;
2762 WantQueryMouse = TRUE;
2763 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002764 case 5: mouse_code = MOUSE_MIDDLE;
2765 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002766 break;
2767 case 6: mouse_code = MOUSE_RIGHT;
2768 WantQueryMouse = TRUE;
2769 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002770 case 7: mouse_code = MOUSE_RIGHT;
2771 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002772 break;
2773 case 8: return -1; // fourth button down
2774 case 9: return -1; // fourth button up
2775 case 10: return -1; // mouse outside of filter rectangle
2776 default: return -1; // should never occur
2777 }
2778
2779 mouse_col = Pc - 1;
2780 mouse_row = Pr - 1;
2781
2782 *slen += (int)(p - (tp + *slen));
2783 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002784#endif // FEAT_MOUSE_DEC
2785#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002786 if (key_name[0] == KS_PTERM_MOUSE)
2787 {
2788 int button, num_clicks, action;
2789
2790 p = tp + *slen;
2791
2792 action = getdigits(&p);
2793 if (*p++ != ';')
2794 return -1;
2795
2796 mouse_row = getdigits(&p);
2797 if (*p++ != ';')
2798 return -1;
2799 mouse_col = getdigits(&p);
2800 if (*p++ != ';')
2801 return -1;
2802
2803 button = getdigits(&p);
2804 mouse_code = 0;
2805
2806 switch (button)
2807 {
2808 case 4: mouse_code = MOUSE_LEFT; break;
2809 case 1: mouse_code = MOUSE_RIGHT; break;
2810 case 2: mouse_code = MOUSE_MIDDLE; break;
2811 default: return -1;
2812 }
2813
2814 switch (action)
2815 {
2816 case 31: // Initial press
2817 if (*p++ != ';')
2818 return -1;
2819
2820 num_clicks = getdigits(&p); // Not used
2821 break;
2822
2823 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002824 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002825 break;
2826
2827 case 33: // Drag
2828 held_button = mouse_code;
2829 mouse_code |= MOUSE_DRAG;
2830 break;
2831
2832 default:
2833 return -1;
2834 }
2835
2836 if (*p++ != 't')
2837 return -1;
2838
2839 *slen += (p - (tp + *slen));
2840 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002841#endif // FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002842
2843 // Interpret the mouse code
2844 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002845 if (is_release)
2846 current_button |= MOUSE_RELEASE;
2847
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002848 if (current_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002849#ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002850 && wheel_code == 0
Christopher Plewright696d0a82022-11-18 17:53:34 +00002851#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002852 )
2853 {
2854 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002855 * If we get a mouse drag or release event when there is no mouse
2856 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2857 * below.
2858 * (can happen when you hold down two buttons and then let them go, or
2859 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002860 */
2861 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2862 is_drag = TRUE;
2863 current_button = held_button;
2864 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002865 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002866 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002867 if (wheel_code == 0)
2868 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002869#ifdef CHECK_DOUBLE_CLICK
2870# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002871 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002872 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2873 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002874 */
Christopher Plewright696d0a82022-11-18 17:53:34 +00002875# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002876 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002877# else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002878 if (key_name[0] != KS_GPM_MOUSE)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002879# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002880# else
2881# ifdef FEAT_GUI
2882 if (!gui.in_use)
2883# endif
2884# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002885 {
2886 /*
2887 * Compute the time elapsed since the previous mouse click.
2888 */
2889 gettimeofday(&mouse_time, NULL);
2890 if (orig_mouse_time.tv_sec == 0)
2891 {
2892 /*
2893 * Avoid computing the difference between mouse_time
2894 * and orig_mouse_time for the first click, as the
2895 * difference would be huge and would cause
2896 * multiplication overflow.
2897 */
2898 timediff = p_mouset;
2899 }
2900 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002901 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002902 orig_mouse_time = mouse_time;
2903 if (mouse_code == orig_mouse_code
2904 && timediff < p_mouset
2905 && orig_num_clicks != 4
2906 && orig_mouse_col == mouse_col
2907 && orig_mouse_row == mouse_row
2908 && (is_mouse_topline(curwin)
2909 // Double click in tab pages line also works
2910 // when window contents changes.
2911 || (mouse_row == 0 && firstwin->w_winrow > 0))
2912 )
2913 ++orig_num_clicks;
2914 else
2915 orig_num_clicks = 1;
2916 orig_mouse_col = mouse_col;
2917 orig_mouse_row = mouse_row;
2918 set_mouse_topline(curwin);
2919 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002920# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002921 else
2922 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002923# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002924#else
2925 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2926#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002927 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002928 }
2929 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002930 }
2931 if (!is_drag)
2932 held_button = mouse_code & MOUSE_CLICK_MASK;
2933
2934 /*
2935 * Translate the actual mouse event into a pseudo mouse event.
2936 * First work out what modifiers are to be used.
2937 */
2938 if (orig_mouse_code & MOUSE_SHIFT)
2939 *modifiers |= MOD_MASK_SHIFT;
2940 if (orig_mouse_code & MOUSE_CTRL)
2941 *modifiers |= MOD_MASK_CTRL;
2942 if (orig_mouse_code & MOUSE_ALT)
2943 *modifiers |= MOD_MASK_ALT;
2944 if (orig_num_clicks == 2)
2945 *modifiers |= MOD_MASK_2CLICK;
2946 else if (orig_num_clicks == 3)
2947 *modifiers |= MOD_MASK_3CLICK;
2948 else if (orig_num_clicks == 4)
2949 *modifiers |= MOD_MASK_4CLICK;
2950
Bram Moolenaar13c04632020-07-12 13:47:42 +02002951 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2952 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002953 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002954 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002955 {
2956 if (wheel_code & MOUSE_CTRL)
2957 *modifiers |= MOD_MASK_CTRL;
2958 if (wheel_code & MOUSE_ALT)
2959 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002960
2961 if (wheel_code & 1 && wheel_code & 2)
2962 key_name[1] = (int)KE_MOUSELEFT;
2963 else if (wheel_code & 2)
2964 key_name[1] = (int)KE_MOUSERIGHT;
2965 else if (wheel_code & 1)
2966 key_name[1] = (int)KE_MOUSEUP;
2967 else
2968 key_name[1] = (int)KE_MOUSEDOWN;
2969
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002970 held_button = MOUSE_RELEASE;
2971 }
2972 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002973 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002974
Bram Moolenaar13c04632020-07-12 13:47:42 +02002975
2976 // Make sure the mouse position is valid. Some terminals may return weird
2977 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002978 if (mouse_col >= Columns)
2979 mouse_col = Columns - 1;
2980 if (mouse_row >= Rows)
2981 mouse_row = Rows - 1;
2982
2983 return 0;
2984}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002985
2986// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002987
2988/*
2989 * Compute the buffer line position from the screen position "rowp" / "colp" in
2990 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02002991 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
2992 * caches the plines_win() result from a previous call. Entry is zero if not
2993 * computed yet. There must be no text or setting changes since the entry is
2994 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002995 * Returns TRUE if the position is below the last line.
2996 */
2997 int
2998mouse_comp_pos(
2999 win_T *win,
3000 int *rowp,
3001 int *colp,
3002 linenr_T *lnump,
3003 int *plines_cache)
3004{
3005 int col = *colp;
3006 int row = *rowp;
3007 linenr_T lnum;
3008 int retval = FALSE;
3009 int off;
3010 int count;
3011
3012#ifdef FEAT_RIGHTLEFT
3013 if (win->w_p_rl)
3014 col = win->w_width - 1 - col;
3015#endif
3016
3017 lnum = win->w_topline;
3018
3019 while (row > 0)
3020 {
3021 int cache_idx = lnum - win->w_topline;
3022
Bram Moolenaar452143c2020-07-15 17:38:21 +02003023 // Only "Rows" lines are cached, with folding we'll run out of entries
3024 // and use the slow way.
3025 if (plines_cache != NULL && cache_idx < Rows
3026 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003027 count = plines_cache[cache_idx];
3028 else
3029 {
3030#ifdef FEAT_DIFF
3031 // Don't include filler lines in "count"
3032 if (win->w_p_diff
3033# ifdef FEAT_FOLDING
3034 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3035# endif
3036 )
3037 {
3038 if (lnum == win->w_topline)
3039 row -= win->w_topfill;
3040 else
3041 row -= diff_check_fill(win, lnum);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003042 count = plines_win_nofill(win, lnum, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003043 }
3044 else
3045#endif
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003046 count = plines_win(win, lnum, FALSE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02003047 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003048 plines_cache[cache_idx] = count;
3049 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003050
3051 if (win->w_skipcol > 0 && lnum == win->w_topline)
3052 {
3053 // Adjust for 'smoothscroll' clipping the top screen lines.
3054 // A similar formula is used in curs_columns().
3055 int width1 = win->w_width - win_col_off(win);
3056 int skip_lines = 0;
3057 if (win->w_skipcol > width1)
3058 skip_lines = (win->w_skipcol - width1)
3059 / (width1 + win_col_off2(win)) + 1;
3060 else if (win->w_skipcol > 0)
3061 skip_lines = 1;
3062 count -= skip_lines;
3063 }
3064
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003065 if (count > row)
3066 break; // Position is in this buffer line.
3067#ifdef FEAT_FOLDING
3068 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3069#endif
3070 if (lnum == win->w_buffer->b_ml.ml_line_count)
3071 {
3072 retval = TRUE;
3073 break; // past end of file
3074 }
3075 row -= count;
3076 ++lnum;
3077 }
3078
3079 if (!retval)
3080 {
3081 // Compute the column without wrapping.
3082 off = win_col_off(win) - win_col_off2(win);
3083 if (col < off)
3084 col = off;
3085 col += row * (win->w_width - off);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003086
3087 // Add skip column for the topline.
3088 if (lnum == win->w_topline)
3089 col += win->w_skipcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003090 }
3091
3092 if (!win->w_p_wrap)
3093 col += win->w_leftcol;
3094
3095 // skip line number and fold column in front of the line
3096 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003097 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003098 {
3099#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003100 // if mouse is clicked on the gutter, then inform the netbeans server
3101 if (*colp < win_col_off(win))
3102 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003103#endif
3104 col = 0;
3105 }
3106
3107 *colp = col;
3108 *rowp = row;
3109 *lnump = lnum;
3110 return retval;
3111}
3112
3113/*
3114 * Find the window at screen position "*rowp" and "*colp". The positions are
3115 * updated to become relative to the top-left of the window.
3116 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3117 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3118 * windows.
3119 * Returns NULL when something is wrong.
3120 */
3121 win_T *
3122mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3123{
3124 frame_T *fp;
3125 win_T *wp;
3126
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003127#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003128 win_T *pwp = NULL;
3129
3130 if (popup != IGNORE_POPUP)
3131 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003132 popup_reset_handled(POPUP_HANDLED_1);
3133 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003134 {
3135 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3136 && *colp >= wp->w_wincol
3137 && *colp < wp->w_wincol + popup_width(wp))
3138 pwp = wp;
3139 }
3140 if (pwp != NULL)
3141 {
3142 if (popup == FAIL_POPUP)
3143 return NULL;
3144 *rowp -= pwp->w_winrow;
3145 *colp -= pwp->w_wincol;
3146 return pwp;
3147 }
3148 }
3149#endif
3150
3151 fp = topframe;
3152 *rowp -= firstwin->w_winrow;
3153 for (;;)
3154 {
3155 if (fp->fr_layout == FR_LEAF)
3156 break;
3157 if (fp->fr_layout == FR_ROW)
3158 {
3159 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3160 {
3161 if (*colp < fp->fr_width)
3162 break;
3163 *colp -= fp->fr_width;
3164 }
3165 }
3166 else // fr_layout == FR_COL
3167 {
3168 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3169 {
3170 if (*rowp < fp->fr_height)
3171 break;
3172 *rowp -= fp->fr_height;
3173 }
3174 }
3175 }
3176 // When using a timer that closes a window the window might not actually
3177 // exist.
3178 FOR_ALL_WINDOWS(wp)
3179 if (wp == fp->fr_win)
3180 {
3181#ifdef FEAT_MENU
3182 *rowp -= wp->w_winbar_height;
3183#endif
3184 return wp;
3185 }
3186 return NULL;
3187}
3188
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003189#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003190 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003191/*
3192 * Convert a virtual (screen) column to a character column.
3193 * The first column is one.
3194 */
3195 int
3196vcol2col(win_T *wp, linenr_T lnum, int vcol)
3197{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003198 char_u *line;
3199 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003200
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003201 // try to advance to the specified column
3202 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3203 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3204 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003205 {
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003206 cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
3207 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003208 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003209 clear_chartabsize_arg(&cts);
3210
3211 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003212}
3213#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003214
3215#if defined(FEAT_EVAL) || defined(PROTO)
Christopher Plewright696d0a82022-11-18 17:53:34 +00003216/*
3217 * "getmousepos()" function.
3218 */
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003219 void
3220f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3221{
3222 dict_T *d;
3223 win_T *wp;
3224 int row = mouse_row;
3225 int col = mouse_col;
3226 varnumber_T winid = 0;
3227 varnumber_T winrow = 0;
3228 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003229 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003230 varnumber_T column = 0;
3231
Bram Moolenaar93a10962022-06-16 11:42:09 +01003232 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003233 return;
3234 d = rettv->vval.v_dict;
3235
3236 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3237 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3238
3239 wp = mouse_find_win(&row, &col, FIND_POPUP);
3240 if (wp != NULL)
3241 {
3242 int top_off = 0;
3243 int left_off = 0;
3244 int height = wp->w_height + wp->w_status_height;
3245
Christopher Plewright696d0a82022-11-18 17:53:34 +00003246# ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003247 if (WIN_IS_POPUP(wp))
3248 {
3249 top_off = popup_top_extra(wp);
3250 left_off = popup_left_extra(wp);
3251 height = popup_height(wp);
3252 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00003253# endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003254 if (row < height)
3255 {
3256 winid = wp->w_id;
3257 winrow = row + 1;
3258 wincol = col + 1;
3259 row -= top_off;
3260 col -= left_off;
3261 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3262 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003263 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
3264 col = vcol2col(wp, lnum, col);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003265 column = col + 1;
3266 }
3267 }
3268 }
3269 dict_add_number(d, "winid", winid);
3270 dict_add_number(d, "winrow", winrow);
3271 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003272 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003273 dict_add_number(d, "column", column);
3274}
3275#endif