blob: b283c6479a5d4c8acdca2ace417809c457ce66c1 [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
zeertzjqf5a94d52023-10-15 10:03:30 +0200175 mpos->col = vcol2col(wp, mpos->lnum, col, &mpos->coladd);
Bram Moolenaar910c3782019-09-22 14:11:50 +0200176 return IN_BUFFER;
177}
178#endif
179
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200180/*
181 * Do the appropriate action for the current mouse click in the current mode.
182 * Not used for Command-line mode.
183 *
184 * Normal and Visual Mode:
185 * event modi- position visual change action
186 * fier cursor window
187 * left press - yes end yes
188 * left press C yes end yes "^]" (2)
189 * left press S yes end (popup: extend) yes "*" (2)
190 * left drag - yes start if moved no
191 * left relse - yes start if moved no
192 * middle press - yes if not active no put register
193 * middle press - yes if active no yank and put
194 * right press - yes start or extend yes
195 * right press S yes no change yes "#" (2)
196 * right drag - yes extend no
197 * right relse - yes extend no
198 *
199 * Insert or Replace Mode:
200 * event modi- position visual change action
201 * fier cursor window
202 * left press - yes (cannot be active) yes
203 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
204 * left press S yes (cannot be active) yes "CTRL-O*" (2)
205 * left drag - yes start or extend (1) no CTRL-O (1)
206 * left relse - yes start or extend (1) no CTRL-O (1)
207 * middle press - no (cannot be active) no put register
208 * right press - yes start or extend yes CTRL-O
209 * right press S yes (cannot be active) yes "CTRL-O#" (2)
210 *
211 * (1) only if mouse pointer moved since press
212 * (2) only if click is in same buffer
213 *
214 * Return TRUE if start_arrow() should be called for edit mode.
215 */
216 int
217do_mouse(
218 oparg_T *oap, // operator argument, can be NULL
219 int c, // K_LEFTMOUSE, etc
220 int dir, // Direction to 'put' if necessary
221 long count,
222 int fixindent) // PUT_FIXINDENT if fixing indent necessary
223{
224 static int do_always = FALSE; // ignore 'mouse' setting next time
zeertzjq8e0ccb62022-11-01 18:35:27 +0000225 static int got_click = FALSE; // got a click some time back
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200226
227 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
228 int is_click = FALSE; // If FALSE it's a drag or release event
229 int is_drag = FALSE; // If TRUE it's a drag event
230 int jump_flags = 0; // flags for jump_to_mouse()
231 pos_T start_visual;
232 int moved; // Has cursor moved?
233 int in_status_line; // mouse in status line
234 static int in_tab_line = FALSE; // mouse clicked in tab line
235 int in_sep_line; // mouse in vertical separator line
236 int c1, c2;
237#if defined(FEAT_FOLDING)
238 pos_T save_cursor;
239#endif
240 win_T *old_curwin = curwin;
241 static pos_T orig_cursor;
242 colnr_T leftcol, rightcol;
243 pos_T end_visual;
244 int diff;
245 int old_active = VIsual_active;
246 int old_mode = VIsual_mode;
247 int regname;
248
249#if defined(FEAT_FOLDING)
250 save_cursor = curwin->w_cursor;
251#endif
252
253 // When GUI is active, always recognize mouse events, otherwise:
254 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
255 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
256 // - For command line and insert mode 'mouse' is checked before calling
257 // do_mouse().
258 if (do_always)
259 do_always = FALSE;
260 else
261#ifdef FEAT_GUI
262 if (!gui.in_use)
263#endif
264 {
265 if (VIsual_active)
266 {
267 if (!mouse_has(MOUSE_VISUAL))
268 return FALSE;
269 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100270 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200271 return FALSE;
272 }
273
274 for (;;)
275 {
276 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
277 if (is_drag)
278 {
279 // If the next character is the same mouse event then use that
280 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100281 // Note: Since characters added to the stuff buffer in the code
282 // below need to come before the next character, do not do this
283 // when the current character was stuffed.
284 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200285 {
286 int nc;
287 int save_mouse_row = mouse_row;
288 int save_mouse_col = mouse_col;
289
290 // Need to get the character, peeking doesn't get the actual
291 // one.
292 nc = safe_vgetc();
293 if (c == nc)
294 continue;
295 vungetc(nc);
296 mouse_row = save_mouse_row;
297 mouse_col = save_mouse_col;
298 }
299 }
300 break;
301 }
302
303 if (c == K_MOUSEMOVE)
304 {
305 // Mouse moved without a button pressed.
306#ifdef FEAT_BEVAL_TERM
307 ui_may_remove_balloon();
308 if (p_bevalterm)
309 {
310 profile_setlimit(p_bdlay, &bevalexpr_due);
311 bevalexpr_due_set = TRUE;
312 }
313#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100314#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200315 popup_handle_mouse_moved();
316#endif
317 return FALSE;
318 }
319
320#ifdef FEAT_MOUSESHAPE
321 // May have stopped dragging the status or separator line. The pointer is
322 // most likely still on the status or separator line.
323 if (!is_drag && drag_status_line)
324 {
325 drag_status_line = FALSE;
326 update_mouseshape(SHAPE_IDX_STATUS);
327 }
328 if (!is_drag && drag_sep_line)
329 {
330 drag_sep_line = FALSE;
331 update_mouseshape(SHAPE_IDX_VSEP);
332 }
333#endif
334
335 // Ignore drag and release events if we didn't get a click.
336 if (is_click)
zeertzjq8e0ccb62022-11-01 18:35:27 +0000337 got_click = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200338 else
339 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000340 if (!got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200341 return FALSE;
zeertzjq8e0ccb62022-11-01 18:35:27 +0000342 if (!is_drag) // release, reset got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200343 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000344 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200345 if (in_tab_line)
346 {
347 in_tab_line = FALSE;
348 return FALSE;
349 }
350 }
351 }
352
353 // CTRL right mouse button does CTRL-T
354 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
355 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100356 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200357 stuffcharReadbuff(Ctrl_O);
358 if (count > 1)
359 stuffnumReadbuff(count);
360 stuffcharReadbuff(Ctrl_T);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000361 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200362 return FALSE;
363 }
364
365 // CTRL only works with left mouse button
366 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
367 return FALSE;
368
369 // When a modifier is down, ignore drag and release events, as well as
370 // multiple clicks and the middle mouse button.
371 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
372 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
373 | MOD_MASK_META))
374 && (!is_click
375 || (mod_mask & MOD_MASK_MULTI_CLICK)
376 || which_button == MOUSE_MIDDLE)
377 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
378 && mouse_model_popup()
379 && which_button == MOUSE_LEFT)
380 && !((mod_mask & MOD_MASK_ALT)
381 && !mouse_model_popup()
382 && which_button == MOUSE_RIGHT)
383 )
384 return FALSE;
385
386 // If the button press was used as the movement command for an operator
387 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
388 // drag/release events.
389 if (!is_click && which_button == MOUSE_MIDDLE)
390 return FALSE;
391
392 if (oap != NULL)
393 regname = oap->regname;
394 else
395 regname = 0;
396
397 // Middle mouse button does a 'put' of the selected text
398 if (which_button == MOUSE_MIDDLE)
399 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100400 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200401 {
402 // If an operator was pending, we don't know what the user wanted
403 // to do. Go back to normal mode: Clear the operator and beep().
404 if (oap != NULL && oap->op_type != OP_NOP)
405 {
406 clearopbeep(oap);
407 return FALSE;
408 }
409
410 // If visual was active, yank the highlighted text and put it
411 // before the mouse pointer position.
412 // In Select mode replace the highlighted text with the clipboard.
413 if (VIsual_active)
414 {
415 if (VIsual_select)
416 {
417 stuffcharReadbuff(Ctrl_G);
418 stuffReadbuff((char_u *)"\"+p");
419 }
420 else
421 {
422 stuffcharReadbuff('y');
423 stuffcharReadbuff(K_MIDDLEMOUSE);
424 }
425 do_always = TRUE; // ignore 'mouse' setting next time
426 return FALSE;
427 }
428 // The rest is below jump_to_mouse()
429 }
430
Bram Moolenaar24959102022-05-07 20:01:16 +0100431 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200432 return FALSE;
433
434 // Middle click in insert mode doesn't move the mouse, just insert the
435 // contents of a register. '.' register is special, can't insert that
436 // with do_put().
437 // Also paste at the cursor if the current mode isn't in 'mouse' (only
438 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100439 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200440 {
441 if (regname == '.')
442 insert_reg(regname, TRUE);
443 else
444 {
445#ifdef FEAT_CLIPBOARD
446 if (clip_star.available && regname == 0)
447 regname = '*';
448#endif
449 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
450 insert_reg(regname, TRUE);
451 else
452 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200453 do_put(regname, NULL, BACKWARD, 1L,
454 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200455
456 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
457 AppendCharToRedobuff(Ctrl_R);
458 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
459 AppendCharToRedobuff(regname == 0 ? '"' : regname);
460 }
461 }
462 return FALSE;
463 }
464 }
465
466 // When dragging or button-up stay in the same window.
467 if (!is_click)
468 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
469
470 start_visual.lnum = 0;
471
Bram Moolenaar80525752022-08-24 19:27:45 +0100472 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200473 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100474 // Check for clicking in the tab page line.
475 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200476 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100477 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200478 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100479 if (in_tab_line)
480 {
481 c1 = TabPageIdxs[mouse_col];
482 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100483 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100484 }
485 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200486 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100487
488 // click in a tab selects that tab page
Martin Tournoij7904fa42022-10-04 16:28:45 +0100489 if (is_click && cmdwin_type == 0 && mouse_col < Columns)
Bram Moolenaar80525752022-08-24 19:27:45 +0100490 {
491 in_tab_line = TRUE;
492 c1 = TabPageIdxs[mouse_col];
493 if (c1 >= 0)
494 {
495 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
496 {
497 // double click opens new page
498 end_visual_mode_keep_button();
499 tabpage_new();
500 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
501 }
502 else
503 {
504 // Go to specified tab page, or next one if not clicking
505 // on a label.
506 goto_tabpage(c1);
507
508 // It's like clicking on the status line of a window.
509 if (curwin != old_curwin)
510 end_visual_mode_keep_button();
511 }
512 }
513 else
514 {
515 tabpage_T *tp;
516
517 // Close the current or specified tab page.
518 if (c1 == -999)
519 tp = curtab;
520 else
521 tp = find_tabpage(-c1);
522 if (tp == curtab)
523 {
524 if (first_tabpage->tp_next != NULL)
525 tabpage_close(FALSE);
526 }
527 else if (tp != NULL)
528 tabpage_close_other(tp, FALSE);
529 }
530 }
531 return TRUE;
532 }
533 else if (is_drag && in_tab_line)
534 {
535 c1 = TabPageIdxs[mouse_col];
536 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200537 return FALSE;
538 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200539 }
540
541 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
542 // right button up -> pop-up menu
543 // shift-left button -> right button
544 // alt-left button -> alt-right button
545 if (mouse_model_popup())
546 {
547 if (which_button == MOUSE_RIGHT
548 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
549 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200550#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200551# ifdef FEAT_GUI
552 if (gui.in_use)
553 {
554# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200555 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200556 if (!is_click)
557 // Ignore right button release events, only shows the popup
558 // menu on the button down event.
559 return FALSE;
560# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100561# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200562 if (is_click || is_drag)
563 // Ignore right button down and drag mouse events. Windows
564 // only shows the popup menu on the button up event.
565 return FALSE;
566# endif
567 }
568# endif
569# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
570 else
571# endif
572# if defined(FEAT_TERM_POPUP_MENU)
573 if (!is_click)
574 // Ignore right button release events, only shows the popup
575 // menu on the button down event.
576 return FALSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +0000577# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200578
579 jump_flags = 0;
580 if (STRCMP(p_mousem, "popup_setpos") == 0)
581 {
582 // First set the cursor position before showing the popup
583 // menu.
584 if (VIsual_active)
585 {
586 pos_T m_pos;
587
588 // set MOUSE_MAY_STOP_VIS if we are outside the
589 // selection or the current window (might have false
590 // negative here)
591 if (mouse_row < curwin->w_winrow
592 || mouse_row
593 > (curwin->w_winrow + curwin->w_height))
594 jump_flags = MOUSE_MAY_STOP_VIS;
595 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
596 jump_flags = MOUSE_MAY_STOP_VIS;
597 else
598 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100599 if (VIsual_mode == 'V')
600 {
601 if ((curwin->w_cursor.lnum <= VIsual.lnum
602 && (m_pos.lnum < curwin->w_cursor.lnum
603 || VIsual.lnum < m_pos.lnum))
604 || (VIsual.lnum < curwin->w_cursor.lnum
605 && (m_pos.lnum < VIsual.lnum
606 || curwin->w_cursor.lnum < m_pos.lnum)))
607 {
608 jump_flags = MOUSE_MAY_STOP_VIS;
609 }
610 }
611 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200612 && (LT_POS(m_pos, curwin->w_cursor)
613 || LT_POS(VIsual, m_pos)))
614 || (LT_POS(VIsual, curwin->w_cursor)
615 && (LT_POS(m_pos, VIsual)
616 || LT_POS(curwin->w_cursor, m_pos))))
617 {
618 jump_flags = MOUSE_MAY_STOP_VIS;
619 }
620 else if (VIsual_mode == Ctrl_V)
621 {
622 getvcols(curwin, &curwin->w_cursor, &VIsual,
623 &leftcol, &rightcol);
624 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
625 if (m_pos.col < leftcol || m_pos.col > rightcol)
626 jump_flags = MOUSE_MAY_STOP_VIS;
627 }
628 }
629 }
630 else
631 jump_flags = MOUSE_MAY_STOP_VIS;
632 }
633 if (jump_flags)
634 {
635 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100636 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200637 setcursor();
638 out_flush(); // Update before showing popup menu
639 }
640# ifdef FEAT_MENU
641 show_popupmenu();
zeertzjq8e0ccb62022-11-01 18:35:27 +0000642 got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200643# endif
644 return (jump_flags & CURSOR_MOVED) != 0;
645#else
646 return FALSE;
647#endif
648 }
649 if (which_button == MOUSE_LEFT
650 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
651 {
652 which_button = MOUSE_RIGHT;
653 mod_mask &= ~MOD_MASK_SHIFT;
654 }
655 }
656
Bram Moolenaar24959102022-05-07 20:01:16 +0100657 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200658 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
659 {
660 if (which_button == MOUSE_LEFT)
661 {
662 if (is_click)
663 {
664 // stop Visual mode for a left click in a window, but not when
665 // on a status line
666 if (VIsual_active)
667 jump_flags |= MOUSE_MAY_STOP_VIS;
668 }
669 else if (mouse_has(MOUSE_VISUAL))
670 jump_flags |= MOUSE_MAY_VIS;
671 }
672 else if (which_button == MOUSE_RIGHT)
673 {
674 if (is_click && VIsual_active)
675 {
676 // Remember the start and end of visual before moving the
677 // cursor.
678 if (LT_POS(curwin->w_cursor, VIsual))
679 {
680 start_visual = curwin->w_cursor;
681 end_visual = VIsual;
682 }
683 else
684 {
685 start_visual = VIsual;
686 end_visual = curwin->w_cursor;
687 }
688 }
689 jump_flags |= MOUSE_FOCUS;
690 if (mouse_has(MOUSE_VISUAL))
691 jump_flags |= MOUSE_MAY_VIS;
692 }
693 }
694
695 // If an operator is pending, ignore all drags and releases until the
696 // next mouse click.
697 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
698 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000699 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200700 oap->motion_type = MCHAR;
701 }
702
703 // When releasing the button let jump_to_mouse() know.
704 if (!is_click && !is_drag)
705 jump_flags |= MOUSE_RELEASED;
706
707 // JUMP!
708 jump_flags = jump_to_mouse(jump_flags,
709 oap == NULL ? NULL : &(oap->inclusive), which_button);
710
711#ifdef FEAT_MENU
712 // A click in the window toolbar has no side effects.
713 if (jump_flags & MOUSE_WINBAR)
714 return FALSE;
715#endif
716 moved = (jump_flags & CURSOR_MOVED);
717 in_status_line = (jump_flags & IN_STATUS_LINE);
718 in_sep_line = (jump_flags & IN_SEP_LINE);
719
720#ifdef FEAT_NETBEANS_INTG
721 if (isNetbeansBuffer(curbuf)
722 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
723 {
724 int key = KEY2TERMCAP1(c);
725
726 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
727 || key == (int)KE_RIGHTRELEASE)
728 netbeans_button_release(which_button);
729 }
730#endif
731
732 // When jumping to another window, clear a pending operator. That's a bit
733 // friendlier than beeping and not jumping to that window.
734 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
735 clearop(oap);
736
737#ifdef FEAT_FOLDING
738 if (mod_mask == 0
739 && !is_drag
740 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
741 && which_button == MOUSE_LEFT)
742 {
743 // open or close a fold at this line
744 if (jump_flags & MOUSE_FOLD_OPEN)
745 openFold(curwin->w_cursor.lnum, 1L);
746 else
747 closeFold(curwin->w_cursor.lnum, 1L);
748 // don't move the cursor if still in the same window
749 if (curwin == old_curwin)
750 curwin->w_cursor = save_cursor;
751 }
752#endif
753
Martin Tournoij7904fa42022-10-04 16:28:45 +0100754#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200755 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
756 {
757 clip_modeless(which_button, is_click, is_drag);
758 return FALSE;
759 }
760#endif
761
762 // Set global flag that we are extending the Visual area with mouse
763 // dragging; temporarily minimize 'scrolloff'.
764 if (VIsual_active && is_drag && get_scrolloff_value())
765 {
766 // In the very first line, allow scrolling one line
767 if (mouse_row == 0)
768 mouse_dragging = 2;
769 else
770 mouse_dragging = 1;
771 }
772
773 // When dragging the mouse above the window, scroll down.
774 if (is_drag && mouse_row < 0 && !in_status_line)
775 {
776 scroll_redraw(FALSE, 1L);
777 mouse_row = 0;
778 }
779
780 if (start_visual.lnum) // right click in visual mode
781 {
782 // When ALT is pressed make Visual mode blockwise.
783 if (mod_mask & MOD_MASK_ALT)
784 VIsual_mode = Ctrl_V;
785
786 // In Visual-block mode, divide the area in four, pick up the corner
787 // that is in the quarter that the cursor is in.
788 if (VIsual_mode == Ctrl_V)
789 {
790 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
791 if (curwin->w_curswant > (leftcol + rightcol) / 2)
792 end_visual.col = leftcol;
793 else
794 end_visual.col = rightcol;
795 if (curwin->w_cursor.lnum >=
796 (start_visual.lnum + end_visual.lnum) / 2)
797 end_visual.lnum = start_visual.lnum;
798
799 // move VIsual to the right column
800 start_visual = curwin->w_cursor; // save the cursor pos
801 curwin->w_cursor = end_visual;
802 coladvance(end_visual.col);
803 VIsual = curwin->w_cursor;
804 curwin->w_cursor = start_visual; // restore the cursor
805 }
806 else
807 {
808 // If the click is before the start of visual, change the start.
809 // If the click is after the end of visual, change the end. If
810 // the click is inside the visual, change the closest side.
811 if (LT_POS(curwin->w_cursor, start_visual))
812 VIsual = end_visual;
813 else if (LT_POS(end_visual, curwin->w_cursor))
814 VIsual = start_visual;
815 else
816 {
817 // In the same line, compare column number
818 if (end_visual.lnum == start_visual.lnum)
819 {
820 if (curwin->w_cursor.col - start_visual.col >
821 end_visual.col - curwin->w_cursor.col)
822 VIsual = start_visual;
823 else
824 VIsual = end_visual;
825 }
826
827 // In different lines, compare line number
828 else
829 {
830 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
831 (end_visual.lnum - curwin->w_cursor.lnum);
832
833 if (diff > 0) // closest to end
834 VIsual = start_visual;
835 else if (diff < 0) // closest to start
836 VIsual = end_visual;
837 else // in the middle line
838 {
839 if (curwin->w_cursor.col <
840 (start_visual.col + end_visual.col) / 2)
841 VIsual = end_visual;
842 else
843 VIsual = start_visual;
844 }
845 }
846 }
847 }
848 }
849 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100850 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200851 stuffcharReadbuff(Ctrl_O);
852
853 // Middle mouse click: Put text before cursor.
854 if (which_button == MOUSE_MIDDLE)
855 {
856#ifdef FEAT_CLIPBOARD
857 if (clip_star.available && regname == 0)
858 regname = '*';
859#endif
860 if (yank_register_mline(regname))
861 {
862 if (mouse_past_bottom)
863 dir = FORWARD;
864 }
865 else if (mouse_past_eol)
866 dir = FORWARD;
867
868 if (fixindent)
869 {
870 c1 = (dir == BACKWARD) ? '[' : ']';
871 c2 = 'p';
872 }
873 else
874 {
875 c1 = (dir == FORWARD) ? 'p' : 'P';
876 c2 = NUL;
877 }
878 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
879
880 // Remember where the paste started, so in edit() Insstart can be set
881 // to this position
882 if (restart_edit != 0)
883 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200884 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200885 }
886
887#if defined(FEAT_QUICKFIX)
888 // Ctrl-Mouse click or double click in a quickfix window jumps to the
889 // error under the mouse pointer.
890 else if (((mod_mask & MOD_MASK_CTRL)
891 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
892 && bt_quickfix(curbuf))
893 {
894 if (curwin->w_llist_ref == NULL) // quickfix window
895 do_cmdline_cmd((char_u *)".cc");
896 else // location list window
897 do_cmdline_cmd((char_u *)".ll");
zeertzjq8e0ccb62022-11-01 18:35:27 +0000898 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200899 }
900#endif
901
902 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
903 // under the mouse pointer.
904 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
905 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
906 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100907 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200908 stuffcharReadbuff(Ctrl_O);
909 stuffcharReadbuff(Ctrl_RSB);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000910 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200911 }
912
913 // Shift-Mouse click searches for the next occurrence of the word under
914 // the mouse pointer
915 else if ((mod_mask & MOD_MASK_SHIFT))
916 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100917 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200918 stuffcharReadbuff(Ctrl_O);
919 if (which_button == MOUSE_LEFT)
920 stuffcharReadbuff('*');
921 else // MOUSE_RIGHT
922 stuffcharReadbuff('#');
923 }
924
925 // Handle double clicks, unless on status line
926 else if (in_status_line)
927 {
928#ifdef FEAT_MOUSESHAPE
929 if ((is_drag || is_click) && !drag_status_line)
930 {
931 drag_status_line = TRUE;
932 update_mouseshape(-1);
933 }
934#endif
935 }
936 else if (in_sep_line)
937 {
938#ifdef FEAT_MOUSESHAPE
939 if ((is_drag || is_click) && !drag_sep_line)
940 {
941 drag_sep_line = TRUE;
942 update_mouseshape(-1);
943 }
944#endif
945 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100946 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
947 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200948 && mouse_has(MOUSE_VISUAL))
949 {
950 if (is_click || !VIsual_active)
951 {
952 if (VIsual_active)
953 orig_cursor = VIsual;
954 else
955 {
956 check_visual_highlight();
957 VIsual = curwin->w_cursor;
958 orig_cursor = VIsual;
959 VIsual_active = TRUE;
960 VIsual_reselect = TRUE;
961 // start Select mode if 'selectmode' contains "mouse"
962 may_start_select('o');
963 setmouse();
964 }
965 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
966 {
967 // Double click with ALT pressed makes it blockwise.
968 if (mod_mask & MOD_MASK_ALT)
969 VIsual_mode = Ctrl_V;
970 else
971 VIsual_mode = 'v';
972 }
973 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
974 VIsual_mode = 'V';
975 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
976 VIsual_mode = Ctrl_V;
977#ifdef FEAT_CLIPBOARD
978 // Make sure the clipboard gets updated. Needed because start and
979 // end may still be the same, and the selection needs to be owned
980 clip_star.vmode = NUL;
981#endif
982 }
983 // A double click selects a word or a block.
984 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
985 {
986 pos_T *pos = NULL;
987 int gc;
988
989 if (is_click)
990 {
991 // If the character under the cursor (skipping white space) is
992 // not a word character, try finding a match and select a (),
993 // {}, [], #if/#endif, etc. block.
994 end_visual = curwin->w_cursor;
995 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
996 inc(&end_visual);
997 if (oap != NULL)
998 oap->motion_type = MCHAR;
999 if (oap != NULL
1000 && VIsual_mode == 'v'
1001 && !vim_iswordc(gchar_pos(&end_visual))
1002 && EQUAL_POS(curwin->w_cursor, VIsual)
1003 && (pos = findmatch(oap, NUL)) != NULL)
1004 {
1005 curwin->w_cursor = *pos;
1006 if (oap->motion_type == MLINE)
1007 VIsual_mode = 'V';
1008 else if (*p_sel == 'e')
1009 {
1010 if (LT_POS(curwin->w_cursor, VIsual))
1011 ++VIsual.col;
1012 else
1013 ++curwin->w_cursor.col;
1014 }
1015 }
1016 }
1017
1018 if (pos == NULL && (is_click || is_drag))
1019 {
1020 // When not found a match or when dragging: extend to include
1021 // a word.
1022 if (LT_POS(curwin->w_cursor, orig_cursor))
1023 {
1024 find_start_of_word(&curwin->w_cursor);
1025 find_end_of_word(&VIsual);
1026 }
1027 else
1028 {
1029 find_start_of_word(&VIsual);
1030 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1031 curwin->w_cursor.col +=
1032 (*mb_ptr2len)(ml_get_cursor());
1033 find_end_of_word(&curwin->w_cursor);
1034 }
1035 }
1036 curwin->w_set_curswant = TRUE;
1037 }
1038 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001039 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001040 }
1041 else if (VIsual_active && !old_active)
1042 {
1043 if (mod_mask & MOD_MASK_ALT)
1044 VIsual_mode = Ctrl_V;
1045 else
1046 VIsual_mode = 'v';
1047 }
1048
1049 // If Visual mode changed show it later.
1050 if ((!VIsual_active && old_active && mode_displayed)
1051 || (VIsual_active && p_smd && msg_silent == 0
1052 && (!old_active || VIsual_mode != old_mode)))
1053 redraw_cmdline = TRUE;
1054
1055 return moved;
1056}
1057
1058 void
1059ins_mouse(int c)
1060{
1061 pos_T tpos;
1062 win_T *old_curwin = curwin;
1063
Christopher Plewright696d0a82022-11-18 17:53:34 +00001064#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001065 // When GUI is active, also move/paste when 'mouse' is empty
1066 if (!gui.in_use)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001067#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001068 if (!mouse_has(MOUSE_INSERT))
1069 return;
1070
1071 undisplay_dollar();
1072 tpos = curwin->w_cursor;
1073 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1074 {
1075 win_T *new_curwin = curwin;
1076
1077 if (curwin != old_curwin && win_valid(old_curwin))
1078 {
1079 // Mouse took us to another window. We need to go back to the
1080 // previous one to stop insert there properly.
1081 curwin = old_curwin;
1082 curbuf = curwin->w_buffer;
1083#ifdef FEAT_JOB_CHANNEL
1084 if (bt_prompt(curbuf))
1085 // Restart Insert mode when re-entering the prompt buffer.
1086 curbuf->b_prompt_insert = 'A';
1087#endif
1088 }
1089 start_arrow(curwin == old_curwin ? &tpos : NULL);
1090 if (curwin != new_curwin && win_valid(new_curwin))
1091 {
1092 curwin = new_curwin;
1093 curbuf = curwin->w_buffer;
1094 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001095 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001096 }
1097
1098 // redraw status lines (in case another window became active)
1099 redraw_statuslines();
1100}
1101
Christopher Plewright44c22092022-11-15 17:43:36 +00001102/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001103 * Common mouse wheel scrolling, shared between Insert mode and NV modes.
1104 * Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
1105 * depending on the scroll direction) or one page when Shift or Ctrl is used.
1106 * Direction is indicated by "cap->arg":
1107 * K_MOUSEUP - MSCR_UP
1108 * K_MOUSEDOWN - MSCR_DOWN
1109 * K_MOUSELEFT - MSCR_LEFT
1110 * K_MOUSERIGHT - MSCR_RIGHT
1111 * "curwin" may have been changed to the window that should be scrolled and
1112 * differ from the window that actually has focus.
1113 */
1114 static void
1115do_mousescroll(cmdarg_T *cap)
1116{
1117 int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
1118
1119#ifdef FEAT_TERMINAL
1120 if (term_use_loop())
1121 // This window is a terminal window, send the mouse event there.
1122 // Set "typed" to FALSE to avoid an endless loop.
1123 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
1124 else
1125#endif
1126 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1127 {
1128 // Vertical scrolling
1129 if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
1130 {
1131 // whole page up or down
1132 onepage(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L);
1133 }
1134 else
1135 {
1136 if (mouse_vert_step < 0 || shift_or_ctrl)
1137 {
1138 // whole page up or down
1139 cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
1140 }
1141 // Don't scroll more than half the window height.
1142 else if (curwin->w_height < mouse_vert_step * 2)
1143 {
1144 cap->count1 = curwin->w_height / 2;
1145 if (cap->count1 == 0)
1146 cap->count1 = 1;
1147 }
1148 else
1149 {
1150 cap->count1 = mouse_vert_step;
1151 }
1152 cap->count0 = cap->count1;
1153 nv_scroll_line(cap);
1154 }
1155
1156#ifdef FEAT_PROP_POPUP
1157 if (WIN_IS_POPUP(curwin))
1158 popup_set_firstline(curwin);
1159#endif
1160 }
1161 else
1162 {
1163 // Horizontal scrolling
1164 long step = (mouse_hor_step < 0 || shift_or_ctrl)
1165 ? curwin->w_width : mouse_hor_step;
1166 long leftcol = curwin->w_leftcol
1167 + (cap->arg == MSCR_RIGHT ? -step : step);
1168 if (leftcol < 0)
1169 leftcol = 0;
1170 do_mousescroll_horiz((long_u)leftcol);
1171 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001172 may_trigger_win_scrolled_resized();
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001173}
1174
1175/*
1176 * Insert mode implementation for scrolling in direction "dir", which is
1177 * one of the MSCR_ values.
Christopher Plewright44c22092022-11-15 17:43:36 +00001178 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001179 void
1180ins_mousescroll(int dir)
1181{
Christopher Plewright696d0a82022-11-18 17:53:34 +00001182 cmdarg_T cap;
1183 oparg_T oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001184 CLEAR_FIELD(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00001185 clear_oparg(&oa);
1186 cap.oap = &oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001187 cap.arg = dir;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001188
Christopher Plewright44c22092022-11-15 17:43:36 +00001189 switch (dir)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001190 {
Christopher Plewright44c22092022-11-15 17:43:36 +00001191 case MSCR_UP:
1192 cap.cmdchar = K_MOUSEUP;
1193 break;
1194 case MSCR_DOWN:
1195 cap.cmdchar = K_MOUSEDOWN;
1196 break;
1197 case MSCR_LEFT:
1198 cap.cmdchar = K_MOUSELEFT;
1199 break;
1200 case MSCR_RIGHT:
1201 cap.cmdchar = K_MOUSERIGHT;
1202 break;
1203 default:
1204 siemsg("Invalid ins_mousescroll() argument: %d", dir);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001205 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00001206
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001207 win_T *old_curwin = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001208 if (mouse_row >= 0 && mouse_col >= 0)
1209 {
1210 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001211 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00001212 int row = mouse_row;
1213 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001214 curwin = mouse_find_win(&row, &col, FIND_POPUP);
1215 if (curwin == NULL)
1216 {
1217 curwin = old_curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001218 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001219 }
1220 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001221 }
1222
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001223 if (curwin == old_curwin)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001224 {
1225 // Don't scroll the current window if the popup menu is visible.
1226 if (pum_visible())
1227 return;
1228
1229 undisplay_dollar();
1230 }
1231
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001232 linenr_T orig_topline = curwin->w_topline;
1233 colnr_T orig_leftcol = curwin->w_leftcol;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001234 pos_T orig_cursor = curwin->w_cursor;
1235
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001236 // Call the common mouse scroll function shared with other modes.
1237 do_mousescroll(&cap);
1238
1239 int did_scroll = (orig_topline != curwin->w_topline
1240 || orig_leftcol != curwin->w_leftcol);
1241
1242 curwin->w_redr_status = TRUE;
1243 curwin = old_curwin;
1244 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001245
1246 // If the window actually scrolled and the popup menu may overlay the
1247 // window, need to redraw it.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001248 if (did_scroll && pum_visible())
Christopher Plewright696d0a82022-11-18 17:53:34 +00001249 {
1250 // TODO: Would be more efficient to only redraw the windows that are
1251 // overlapped by the popup menu.
1252 redraw_all_later(UPD_NOT_VALID);
1253 ins_compl_show_pum();
1254 }
1255
1256 if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
1257 {
1258 start_arrow(&orig_cursor);
1259 set_can_cindent(TRUE);
1260 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001261}
1262
1263/*
1264 * Return TRUE if "c" is a mouse key.
1265 */
1266 int
1267is_mouse_key(int c)
1268{
1269 return c == K_LEFTMOUSE
1270 || c == K_LEFTMOUSE_NM
1271 || c == K_LEFTDRAG
1272 || c == K_LEFTRELEASE
1273 || c == K_LEFTRELEASE_NM
1274 || c == K_MOUSEMOVE
1275 || c == K_MIDDLEMOUSE
1276 || c == K_MIDDLEDRAG
1277 || c == K_MIDDLERELEASE
1278 || c == K_RIGHTMOUSE
1279 || c == K_RIGHTDRAG
1280 || c == K_RIGHTRELEASE
1281 || c == K_MOUSEDOWN
1282 || c == K_MOUSEUP
1283 || c == K_MOUSELEFT
1284 || c == K_MOUSERIGHT
1285 || c == K_X1MOUSE
1286 || c == K_X1DRAG
1287 || c == K_X1RELEASE
1288 || c == K_X2MOUSE
1289 || c == K_X2DRAG
1290 || c == K_X2RELEASE;
1291}
1292
1293static struct mousetable
1294{
1295 int pseudo_code; // Code for pseudo mouse event
1296 int button; // Which mouse button is it?
1297 int is_click; // Is it a mouse button click event?
1298 int is_drag; // Is it a mouse drag event?
1299} mouse_table[] =
1300{
1301 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1302#ifdef FEAT_GUI
1303 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1304#endif
1305 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1306 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1307#ifdef FEAT_GUI
1308 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1309#endif
1310 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1311 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1312 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1313 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1314 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1315 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1316 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1317 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1318 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1319 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1320 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1321 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1322 // DRAG without CLICK
1323 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1324 // RELEASE without CLICK
1325 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1326 {0, 0, 0, 0},
1327};
1328
1329/*
1330 * Look up the given mouse code to return the relevant information in the other
1331 * arguments. Return which button is down or was released.
1332 */
1333 int
1334get_mouse_button(int code, int *is_click, int *is_drag)
1335{
1336 int i;
1337
1338 for (i = 0; mouse_table[i].pseudo_code; i++)
1339 if (code == mouse_table[i].pseudo_code)
1340 {
1341 *is_click = mouse_table[i].is_click;
1342 *is_drag = mouse_table[i].is_drag;
1343 return mouse_table[i].button;
1344 }
1345 return 0; // Shouldn't get here
1346}
1347
1348/*
1349 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1350 * the given information about which mouse button is down, and whether the
1351 * mouse was clicked, dragged or released.
1352 */
1353 int
1354get_pseudo_mouse_code(
1355 int button, // eg MOUSE_LEFT
1356 int is_click,
1357 int is_drag)
1358{
1359 int i;
1360
1361 for (i = 0; mouse_table[i].pseudo_code; i++)
1362 if (button == mouse_table[i].button
1363 && is_click == mouse_table[i].is_click
1364 && is_drag == mouse_table[i].is_drag)
1365 {
1366#ifdef FEAT_GUI
1367 // Trick: a non mappable left click and release has mouse_col -1
1368 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1369 // gui_mouse_moved()
1370 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1371 {
1372 if (mouse_col < 0)
1373 mouse_col = 0;
1374 else
1375 mouse_col -= MOUSE_COLOFF;
1376 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1377 return (int)KE_LEFTMOUSE_NM;
1378 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1379 return (int)KE_LEFTRELEASE_NM;
1380 }
1381#endif
1382 return mouse_table[i].pseudo_code;
1383 }
1384 return (int)KE_IGNORE; // not recognized, ignore it
1385}
1386
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001387# define HMT_NORMAL 1
1388# define HMT_NETTERM 2
1389# define HMT_DEC 4
1390# define HMT_JSBTERM 8
1391# define HMT_PTERM 16
1392# define HMT_URXVT 32
1393# define HMT_GPM 64
1394# define HMT_SGR 128
1395# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001396static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001397
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001398 void
1399set_mouse_termcode(
1400 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1401 char_u *s)
1402{
1403 char_u name[2];
1404
1405 name[0] = n;
1406 name[1] = KE_FILLER;
1407 add_termcode(name, s, FALSE);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001408#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001409 if (n == KS_JSBTERM_MOUSE)
1410 has_mouse_termcode |= HMT_JSBTERM;
1411 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001412#endif
1413#ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001414 if (n == KS_NETTERM_MOUSE)
1415 has_mouse_termcode |= HMT_NETTERM;
1416 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001417#endif
1418#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001419 if (n == KS_DEC_MOUSE)
1420 has_mouse_termcode |= HMT_DEC;
1421 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001422#endif
1423#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001424 if (n == KS_PTERM_MOUSE)
1425 has_mouse_termcode |= HMT_PTERM;
1426 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001427#endif
1428#ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001429 if (n == KS_URXVT_MOUSE)
1430 has_mouse_termcode |= HMT_URXVT;
1431 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001432#endif
1433#ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001434 if (n == KS_GPM_MOUSE)
1435 has_mouse_termcode |= HMT_GPM;
1436 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001437#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001438 if (n == KS_SGR_MOUSE)
1439 has_mouse_termcode |= HMT_SGR;
1440 else if (n == KS_SGR_MOUSE_RELEASE)
1441 has_mouse_termcode |= HMT_SGR_REL;
1442 else
1443 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001444}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001445
Christopher Plewright696d0a82022-11-18 17:53:34 +00001446#if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001447 void
1448del_mouse_termcode(
1449 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1450{
1451 char_u name[2];
1452
1453 name[0] = n;
1454 name[1] = KE_FILLER;
1455 del_termcode(name);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001456# ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001457 if (n == KS_JSBTERM_MOUSE)
1458 has_mouse_termcode &= ~HMT_JSBTERM;
1459 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001460# endif
1461# ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001462 if (n == KS_NETTERM_MOUSE)
1463 has_mouse_termcode &= ~HMT_NETTERM;
1464 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001465# endif
1466# ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001467 if (n == KS_DEC_MOUSE)
1468 has_mouse_termcode &= ~HMT_DEC;
1469 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001470# endif
1471# ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001472 if (n == KS_PTERM_MOUSE)
1473 has_mouse_termcode &= ~HMT_PTERM;
1474 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001475# endif
1476# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001477 if (n == KS_URXVT_MOUSE)
1478 has_mouse_termcode &= ~HMT_URXVT;
1479 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001480# endif
1481# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001482 if (n == KS_GPM_MOUSE)
1483 has_mouse_termcode &= ~HMT_GPM;
1484 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001485# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001486 if (n == KS_SGR_MOUSE)
1487 has_mouse_termcode &= ~HMT_SGR;
1488 else if (n == KS_SGR_MOUSE_RELEASE)
1489 has_mouse_termcode &= ~HMT_SGR_REL;
1490 else
1491 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001492}
Christopher Plewright696d0a82022-11-18 17:53:34 +00001493#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001494
1495/*
1496 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1497 */
1498 void
1499setmouse(void)
1500{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001501 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001502
Christopher Plewright696d0a82022-11-18 17:53:34 +00001503#ifdef FEAT_MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001504 update_mouseshape(-1);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001505#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001506
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001507 // Should be outside proc, but may break MOUSESHAPE
Christopher Plewright696d0a82022-11-18 17:53:34 +00001508#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001509 // In the GUI the mouse is always enabled.
1510 if (gui.in_use)
1511 return;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001512#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001513 // be quick when mouse is off
1514 if (*p_mouse == NUL || has_mouse_termcode == 0)
1515 return;
1516
1517 // don't switch mouse on when not in raw mode (Ex mode)
1518 if (cur_tmode != TMODE_RAW)
1519 {
1520 mch_setmouse(FALSE);
1521 return;
1522 }
1523
1524 if (VIsual_active)
1525 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001526 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1527 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001528 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001529 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001530 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001531 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001532 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001533 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001534 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1535 else
1536 checkfor = MOUSE_NORMAL; // assume normal mode
1537
1538 if (mouse_has(checkfor))
1539 mch_setmouse(TRUE);
1540 else
1541 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001542}
1543
1544/*
1545 * Return TRUE if
1546 * - "c" is in 'mouse', or
1547 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1548 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1549 * normal editing mode (not at hit-return message).
1550 */
1551 int
1552mouse_has(int c)
1553{
1554 char_u *p;
1555
1556 for (p = p_mouse; *p; ++p)
1557 switch (*p)
1558 {
1559 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1560 return TRUE;
1561 break;
1562 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1563 return TRUE;
1564 break;
1565 default: if (c == *p) return TRUE; break;
1566 }
1567 return FALSE;
1568}
1569
1570/*
1571 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1572 */
1573 int
1574mouse_model_popup(void)
1575{
1576 return (p_mousem[0] == 'p');
1577}
1578
zeertzjq8e0ccb62022-11-01 18:35:27 +00001579static win_T *dragwin = NULL; // window being dragged
1580
1581/*
1582 * Reset the window being dragged. To be called when switching tab page.
1583 */
1584 void
1585reset_dragwin(void)
1586{
1587 dragwin = NULL;
1588}
1589
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001590/*
1591 * Move the cursor to the specified row and column on the screen.
1592 * Change current window if necessary. Returns an integer with the
1593 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1594 *
1595 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1596 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1597 *
1598 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1599 * if the mouse is outside the window then the text will scroll, or if the
1600 * mouse was previously on a status line, then the status line may be dragged.
1601 *
1602 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1603 * cursor is moved unless the cursor was on a status line.
1604 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1605 * IN_SEP_LINE depending on where the cursor was clicked.
1606 *
1607 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1608 * the mouse is on the status line of the same window.
1609 *
1610 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1611 * the last call.
1612 *
1613 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1614 * remembered.
1615 */
1616 int
1617jump_to_mouse(
1618 int flags,
1619 int *inclusive, // used for inclusive operator, can be NULL
1620 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1621{
1622 static int on_status_line = 0; // #lines below bottom of window
1623 static int on_sep_line = 0; // on separator right of window
1624#ifdef FEAT_MENU
1625 static int in_winbar = FALSE;
1626#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001627#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001628 static int in_popup_win = FALSE;
1629 static win_T *click_in_popup_win = NULL;
1630#endif
1631 static int prev_row = -1;
1632 static int prev_col = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001633 static int did_drag = FALSE; // drag was noticed
1634
1635 win_T *wp, *old_curwin;
1636 pos_T old_cursor;
1637 int count;
1638 int first;
1639 int row = mouse_row;
1640 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001641 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001642#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001643 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001644#endif
1645
1646 mouse_past_bottom = FALSE;
1647 mouse_past_eol = FALSE;
1648
1649 if (flags & MOUSE_RELEASED)
1650 {
1651 // On button release we may change window focus if positioned on a
1652 // status line and no dragging happened.
1653 if (dragwin != NULL && !did_drag)
1654 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1655 dragwin = NULL;
1656 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001657#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001658 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1659 popup_close_for_mouse_click(click_in_popup_win);
1660
1661 popup_dragwin = NULL;
1662 click_in_popup_win = NULL;
1663#endif
1664 }
1665
1666 if ((flags & MOUSE_DID_MOVE)
1667 && prev_row == mouse_row
1668 && prev_col == mouse_col)
1669 {
1670retnomove:
1671 // before moving the cursor for a left click which is NOT in a status
1672 // line, stop Visual mode
1673 if (on_status_line)
1674 return IN_STATUS_LINE;
1675 if (on_sep_line)
1676 return IN_SEP_LINE;
1677#ifdef FEAT_MENU
1678 if (in_winbar)
1679 {
1680 // A quick second click may arrive as a double-click, but we use it
1681 // as a second click in the WinBar.
1682 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1683 {
1684 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1685 if (wp == NULL)
1686 return IN_UNKNOWN;
1687 winbar_click(wp, col);
1688 }
1689 return IN_OTHER_WIN | MOUSE_WINBAR;
1690 }
1691#endif
1692 if (flags & MOUSE_MAY_STOP_VIS)
1693 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001694 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001695 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001696 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001697#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001698 // Continue a modeless selection in another window.
1699 if (cmdwin_type != 0 && row < curwin->w_winrow)
1700 return IN_OTHER_WIN;
1701#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001702#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001703 // Continue a modeless selection in a popup window or dragging it.
1704 if (in_popup_win)
1705 {
1706 click_in_popup_win = NULL; // don't close it on release
1707 if (popup_dragwin != NULL)
1708 {
1709 // dragging a popup window
1710 popup_drag(popup_dragwin);
1711 return IN_UNKNOWN;
1712 }
1713 return IN_OTHER_WIN;
1714 }
1715#endif
1716 return IN_BUFFER;
1717 }
1718
1719 prev_row = mouse_row;
1720 prev_col = mouse_col;
1721
1722 if (flags & MOUSE_SETPOS)
1723 goto retnomove; // ugly goto...
1724
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001725 old_curwin = curwin;
1726 old_cursor = curwin->w_cursor;
1727
1728 if (!(flags & MOUSE_FOCUS))
1729 {
1730 if (row < 0 || col < 0) // check if it makes sense
1731 return IN_UNKNOWN;
1732
1733 // find the window where the row is in and adjust "row" and "col" to be
1734 // relative to top-left of the window
1735 wp = mouse_find_win(&row, &col, FIND_POPUP);
1736 if (wp == NULL)
1737 return IN_UNKNOWN;
1738 dragwin = NULL;
1739
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001740#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001741 // Click in a popup window may start dragging or modeless selection,
1742 // but not much else.
1743 if (WIN_IS_POPUP(wp))
1744 {
1745 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001746 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001747 in_popup_win = TRUE;
1748 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1749 {
1750 return IN_UNKNOWN;
1751 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001752 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001753 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001754 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001755 {
1756 popup_dragwin = wp;
1757 popup_start_drag(wp, row, col);
1758 return IN_UNKNOWN;
1759 }
1760 // Only close on release, otherwise it's not possible to drag or do
1761 // modeless selection.
1762 else if (wp->w_popup_close == POPCLOSE_CLICK
1763 && which_button == MOUSE_LEFT)
1764 {
1765 click_in_popup_win = wp;
1766 }
1767 else if (which_button == MOUSE_LEFT)
1768 // If the click is in the scrollbar, may scroll up/down.
1769 popup_handle_scrollbar_click(wp, row, col);
1770# ifdef FEAT_CLIPBOARD
1771 return IN_OTHER_WIN;
1772# else
1773 return IN_UNKNOWN;
1774# endif
1775 }
1776 in_popup_win = FALSE;
1777 popup_dragwin = NULL;
1778#endif
1779#ifdef FEAT_MENU
1780 if (row == -1)
1781 {
1782 // A click in the window toolbar does not enter another window or
1783 // change Visual highlighting.
1784 winbar_click(wp, col);
1785 in_winbar = TRUE;
1786 return IN_OTHER_WIN | MOUSE_WINBAR;
1787 }
1788 in_winbar = FALSE;
1789#endif
1790
1791 // winpos and height may change in win_enter()!
1792 if (row >= wp->w_height) // In (or below) status line
1793 {
1794 on_status_line = row - wp->w_height + 1;
1795 dragwin = wp;
1796 }
1797 else
1798 on_status_line = 0;
1799 if (col >= wp->w_width) // In separator line
1800 {
1801 on_sep_line = col - wp->w_width + 1;
1802 dragwin = wp;
1803 }
1804 else
1805 on_sep_line = 0;
1806
1807 // The rightmost character of the status line might be a vertical
1808 // separator character if there is no connecting window to the right.
1809 if (on_status_line && on_sep_line)
1810 {
1811 if (stl_connected(wp))
1812 on_sep_line = 0;
1813 else
1814 on_status_line = 0;
1815 }
1816
1817 // Before jumping to another buffer, or moving the cursor for a left
1818 // click, stop Visual mode.
1819 if (VIsual_active
1820 && (wp->w_buffer != curwin->w_buffer
1821 || (!on_status_line && !on_sep_line
1822#ifdef FEAT_FOLDING
1823 && (
1824# ifdef FEAT_RIGHTLEFT
1825 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1826# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01001827 col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001828 )
1829#endif
1830 && (flags & MOUSE_MAY_STOP_VIS))))
1831 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001832 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001833 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001834 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001835 if (cmdwin_type != 0 && wp != curwin)
1836 {
1837 // A click outside the command-line window: Use modeless
1838 // selection if possible. Allow dragging the status lines.
1839 on_sep_line = 0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001840#ifdef FEAT_CLIPBOARD
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001841 if (on_status_line)
1842 return IN_STATUS_LINE;
1843 return IN_OTHER_WIN;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001844#else
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001845 row = 0;
1846 col += wp->w_wincol;
1847 wp = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001848#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001849 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001850#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1851 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1852 // terminal in popup window: don't jump to another window
1853 return IN_OTHER_WIN;
1854#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001855 // Only change window focus when not clicking on or dragging the
1856 // status line. Do change focus when releasing the mouse button
1857 // (MOUSE_FOCUS was set above if we dragged first).
1858 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1859 win_enter(wp, TRUE); // can make wp invalid!
1860
1861 if (curwin != old_curwin)
1862 {
1863#ifdef CHECK_DOUBLE_CLICK
1864 // set topline, to be able to check for double click ourselves
1865 set_mouse_topline(curwin);
1866#endif
1867#ifdef FEAT_TERMINAL
1868 // when entering a terminal window may change state
1869 term_win_entered();
1870#endif
1871 }
1872 if (on_status_line) // In (or below) status line
1873 {
1874 // Don't use start_arrow() if we're in the same window
1875 if (curwin == old_curwin)
1876 return IN_STATUS_LINE;
1877 else
1878 return IN_STATUS_LINE | CURSOR_MOVED;
1879 }
1880 if (on_sep_line) // In (or below) status line
1881 {
1882 // Don't use start_arrow() if we're in the same window
1883 if (curwin == old_curwin)
1884 return IN_SEP_LINE;
1885 else
1886 return IN_SEP_LINE | CURSOR_MOVED;
1887 }
1888
1889 curwin->w_cursor.lnum = curwin->w_topline;
1890#ifdef FEAT_GUI
1891 // remember topline, needed for double click
1892 gui_prev_topline = curwin->w_topline;
1893# ifdef FEAT_DIFF
1894 gui_prev_topfill = curwin->w_topfill;
1895# endif
1896#endif
1897 }
1898 else if (on_status_line && which_button == MOUSE_LEFT)
1899 {
1900 if (dragwin != NULL)
1901 {
1902 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001903 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001904 - on_status_line;
1905 win_drag_status_line(dragwin, count);
1906 did_drag |= count;
1907 }
1908 return IN_STATUS_LINE; // Cursor didn't move
1909 }
1910 else if (on_sep_line && which_button == MOUSE_LEFT)
1911 {
1912 if (dragwin != NULL)
1913 {
1914 // Drag the separator column
1915 count = col - dragwin->w_wincol - dragwin->w_width + 1
1916 - on_sep_line;
1917 win_drag_vsep_line(dragwin, count);
1918 did_drag |= count;
1919 }
1920 return IN_SEP_LINE; // Cursor didn't move
1921 }
1922#ifdef FEAT_MENU
1923 else if (in_winbar)
1924 {
1925 // After a click on the window toolbar don't start Visual mode.
1926 return IN_OTHER_WIN | MOUSE_WINBAR;
1927 }
1928#endif
1929 else // keep_window_focus must be TRUE
1930 {
1931 // before moving the cursor for a left click, stop Visual mode
1932 if (flags & MOUSE_MAY_STOP_VIS)
1933 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001934 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001935 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001936 }
1937
Martin Tournoij7904fa42022-10-04 16:28:45 +01001938#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001939 // Continue a modeless selection in another window.
1940 if (cmdwin_type != 0 && row < curwin->w_winrow)
1941 return IN_OTHER_WIN;
1942#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001943#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001944 if (in_popup_win)
1945 {
1946 if (popup_dragwin != NULL)
1947 {
1948 // dragging a popup window
1949 popup_drag(popup_dragwin);
1950 return IN_UNKNOWN;
1951 }
1952 // continue a modeless selection in a popup window
1953 click_in_popup_win = NULL;
1954 return IN_OTHER_WIN;
1955 }
1956#endif
1957
1958 row -= W_WINROW(curwin);
1959 col -= curwin->w_wincol;
1960
1961 // When clicking beyond the end of the window, scroll the screen.
1962 // Scroll by however many rows outside the window we are.
1963 if (row < 0)
1964 {
1965 count = 0;
1966 for (first = TRUE; curwin->w_topline > 1; )
1967 {
1968#ifdef FEAT_DIFF
1969 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1970 ++count;
1971 else
1972#endif
1973 count += plines(curwin->w_topline - 1);
1974 if (!first && count > -row)
1975 break;
1976 first = FALSE;
1977#ifdef FEAT_FOLDING
1978 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1979#endif
1980#ifdef FEAT_DIFF
1981 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1982 ++curwin->w_topfill;
1983 else
1984#endif
1985 {
1986 --curwin->w_topline;
1987#ifdef FEAT_DIFF
1988 curwin->w_topfill = 0;
1989#endif
1990 }
1991 }
1992#ifdef FEAT_DIFF
1993 check_topfill(curwin, FALSE);
1994#endif
1995 curwin->w_valid &=
1996 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001997 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001998 row = 0;
1999 }
2000 else if (row >= curwin->w_height)
2001 {
2002 count = 0;
2003 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
2004 {
2005#ifdef FEAT_DIFF
2006 if (curwin->w_topfill > 0)
2007 ++count;
2008 else
2009#endif
2010 count += plines(curwin->w_topline);
2011 if (!first && count > row - curwin->w_height + 1)
2012 break;
2013 first = FALSE;
2014#ifdef FEAT_FOLDING
2015 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
2016 && curwin->w_topline == curbuf->b_ml.ml_line_count)
2017 break;
2018#endif
2019#ifdef FEAT_DIFF
2020 if (curwin->w_topfill > 0)
2021 --curwin->w_topfill;
2022 else
2023#endif
2024 {
2025 ++curwin->w_topline;
2026#ifdef FEAT_DIFF
2027 curwin->w_topfill =
2028 diff_check_fill(curwin, curwin->w_topline);
2029#endif
2030 }
2031 }
2032#ifdef FEAT_DIFF
2033 check_topfill(curwin, FALSE);
2034#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002035 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002036 curwin->w_valid &=
2037 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2038 row = curwin->w_height - 1;
2039 }
2040 else if (row == 0)
2041 {
2042 // When dragging the mouse, while the text has been scrolled up as
2043 // far as it goes, moving the mouse in the top line should scroll
2044 // the text down (done later when recomputing w_topline).
2045 if (mouse_dragging > 0
2046 && curwin->w_cursor.lnum
2047 == curwin->w_buffer->b_ml.ml_line_count
2048 && curwin->w_cursor.lnum == curwin->w_topline)
2049 curwin->w_valid &= ~(VALID_TOPLINE);
2050 }
2051 }
2052
zeertzjqec149242023-12-19 20:28:31 +01002053 if (prev_row >= W_WINROW(curwin)
2054 && prev_row < W_WINROW(curwin) + curwin->w_height
2055 && prev_col >= curwin->w_wincol && prev_col < W_ENDCOL(curwin)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002056 && 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.
zeertzjqe500ae82023-08-17 22:35:26 +02002063 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE)
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
zeertzjqe500ae82023-08-17 22:35:26 +02002101 if (col_from_screen == MAXCOL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002102 {
zeertzjqe500ae82023-08-17 22:35:26 +02002103 // When clicking after end of line, still need to set correct curswant
zeertzjq03cd6972023-09-20 20:08:40 +02002104 int off_l = LineOffset[prev_row] + curwin->w_wincol;
zeertzjqe500ae82023-08-17 22:35:26 +02002105 if (ScreenCols[off_l] < MAXCOL)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002106 {
zeertzjqe500ae82023-08-17 22:35:26 +02002107 // Binary search to find last char in line
zeertzjq03cd6972023-09-20 20:08:40 +02002108 int off_r = LineOffset[prev_row] + prev_col;
zeertzjqe500ae82023-08-17 22:35:26 +02002109 int off_click = off_r;
2110 while (off_l < off_r)
2111 {
2112 int off_m = (off_l + off_r + 1) / 2;
2113 if (ScreenCols[off_m] < MAXCOL)
2114 off_l = off_m;
2115 else
2116 off_r = off_m - 1;
2117 }
zeertzjqdb54e982023-09-21 16:33:09 +02002118 colnr_T eol_vcol = ScreenCols[off_r];
2119 if (eol_vcol < 0)
2120 // Empty line or whole line before w_leftcol,
2121 // with columns before buffer text
2122 eol_vcol = curwin->w_leftcol - 1;
2123 col = eol_vcol + (off_click - off_r);
Bram Moolenaarb9081882022-07-09 04:56:24 +01002124 }
2125 else
zeertzjqdb54e982023-09-21 16:33:09 +02002126 // Empty line or whole line before w_leftcol
2127 col = prev_col - curwin->w_wincol + curwin->w_leftcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002128 }
zeertzjqe500ae82023-08-17 22:35:26 +02002129 else if (col_from_screen >= 0)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002130 {
zeertzjqe500ae82023-08-17 22:35:26 +02002131 // Use the virtual column from ScreenCols[], it is accurate also after
2132 // concealed characters.
2133 col = col_from_screen;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002134 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002135
zeertzjqe500ae82023-08-17 22:35:26 +02002136 curwin->w_curswant = col;
2137 curwin->w_set_curswant = FALSE; // May still have been TRUE
2138 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2139 {
2140 if (inclusive != NULL)
2141 *inclusive = TRUE;
2142 mouse_past_eol = TRUE;
2143 }
2144 else if (inclusive != NULL)
2145 *inclusive = FALSE;
2146
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002147 count = IN_BUFFER;
2148 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2149 || curwin->w_cursor.col != old_cursor.col)
2150 count |= CURSOR_MOVED; // Cursor has moved
2151
Christopher Plewright696d0a82022-11-18 17:53:34 +00002152#ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002153 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002154 count |= MOUSE_FOLD_OPEN;
2155 else if (mouse_char != ' ')
2156 count |= MOUSE_FOLD_CLOSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002157#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002158
2159 return count;
2160}
2161
2162/*
Christopher Plewright44c22092022-11-15 17:43:36 +00002163 * Make a horizontal scroll to "leftcol".
2164 * Return TRUE if the cursor moved, FALSE otherwise.
2165 */
2166 int
2167do_mousescroll_horiz(long_u leftcol)
2168{
2169 if (curwin->w_p_wrap)
Christian Brabandtee17b6f2023-09-09 11:23:50 +02002170 return FALSE; // no horizontal scrolling when wrapping
Christopher Plewright44c22092022-11-15 17:43:36 +00002171
2172 if (curwin->w_leftcol == (colnr_T)leftcol)
2173 return FALSE; // already there
2174
Christopher Plewright44c22092022-11-15 17:43:36 +00002175 // When the line of the cursor is too short, move the cursor to the
2176 // longest visible line.
2177 if (
2178#ifdef FEAT_GUI
2179 (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
2180#endif
2181 !virtual_active()
2182 && (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
2183 {
2184 curwin->w_cursor.lnum = ui_find_longest_lnum();
2185 curwin->w_cursor.col = 0;
2186 }
2187
Bram Moolenaar0c34d562022-11-18 14:07:20 +00002188 return set_leftcol((colnr_T)leftcol);
Christopher Plewright44c22092022-11-15 17:43:36 +00002189}
2190
2191/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002192 * Normal and Visual modes implementation for scrolling in direction
2193 * "cap->arg", which is one of the MSCR_ values.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002194 */
2195 void
Christopher Plewright696d0a82022-11-18 17:53:34 +00002196nv_mousescroll(cmdarg_T *cap)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002197{
Christopher Plewright696d0a82022-11-18 17:53:34 +00002198 win_T *old_curwin = curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002199
2200 if (mouse_row >= 0 && mouse_col >= 0)
2201 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002202 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002203 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00002204 int row = mouse_row;
2205 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002206 curwin = mouse_find_win(&row, &col, FIND_POPUP);
2207 if (curwin == NULL)
2208 {
2209 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002210 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002211 }
2212
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002213#ifdef FEAT_PROP_POPUP
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002214 if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
2215 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002216 // cannot scroll this popup window
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002217 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002218 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002219 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002220#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002221 curbuf = curwin->w_buffer;
2222 }
Christopher Plewright44c22092022-11-15 17:43:36 +00002223
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002224 // Call the common mouse scroll function shared with other modes.
2225 do_mousescroll(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00002226
Christopher Plewright696d0a82022-11-18 17:53:34 +00002227#ifdef FEAT_SYN_HL
2228 if (curwin != old_curwin && curwin->w_p_cul)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002229 redraw_for_cursorline(curwin);
Christopher Plewright696d0a82022-11-18 17:53:34 +00002230#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002231 curwin->w_redr_status = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002232 curwin = old_curwin;
2233 curbuf = curwin->w_buffer;
2234}
2235
2236/*
2237 * Mouse clicks and drags.
2238 */
2239 void
2240nv_mouse(cmdarg_T *cap)
2241{
2242 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2243}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002244
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002245static int held_button = MOUSE_RELEASE;
2246
2247 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00002248reset_held_button(void)
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002249{
2250 held_button = MOUSE_RELEASE;
2251}
2252
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002253/*
2254 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2255 * modifiers found in typebuf in 'modifiers'.
2256 */
2257 int
2258check_termcode_mouse(
2259 char_u *tp,
2260 int *slen,
2261 char_u *key_name,
2262 char_u *modifiers_start,
2263 int idx,
2264 int *modifiers)
2265{
2266 int j;
2267 char_u *p;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002268#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002269 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2270 char_u bytes[6];
2271 int num_bytes;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002272#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002273 int mouse_code = 0; // init for GCC
2274 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002275 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002276 int wheel_code = 0;
2277 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002278 static int orig_num_clicks = 1;
2279 static int orig_mouse_code = 0x0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002280#ifdef CHECK_DOUBLE_CLICK
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002281 static int orig_mouse_col = 0;
2282 static int orig_mouse_row = 0;
2283 static struct timeval orig_mouse_time = {0, 0};
2284 // time of previous mouse click
2285 struct timeval mouse_time; // time of current mouse click
2286 long timediff; // elapsed time in msec
Christopher Plewright696d0a82022-11-18 17:53:34 +00002287#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002288
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002289 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002290
Christopher Plewright696d0a82022-11-18 17:53:34 +00002291#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002292 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2293 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002294# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002295 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002296# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002297 )
2298 {
2299 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002300 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002301 * 0x20 = left button down
2302 * 0x21 = middle button down
2303 * 0x22 = right button down
2304 * 0x23 = any button release
2305 * 0x60 = button 4 down (scroll wheel down)
2306 * 0x61 = button 5 down (scroll wheel up)
2307 * add 0x04 for SHIFT
2308 * add 0x08 for ALT
2309 * add 0x10 for CTRL
2310 * add 0x20 for mouse drag (0x40 is drag with left button)
2311 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2312 * 0x43 (drag + release) is also move
2313 * c == column + ' ' + 1 == column + 33
2314 * r == row + ' ' + 1 == row + 33
2315 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002316 * The coordinates are passed on through global variables. Ugly, but
2317 * this avoids trouble with mouse clicks at an unexpected moment and
2318 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002319 */
2320 for (;;)
2321 {
Christopher Plewright36446bb2022-11-23 22:28:08 +00002322 // For the GUI and for MS-Windows two bytes each are used for row
2323 // and column. Allows for more than 223 columns.
2324# if defined(FEAT_GUI) || defined(MSWIN)
2325 if (TRUE
2326# if defined(FEAT_GUI) && !defined(MSWIN)
2327 && gui.in_use
2328# endif
2329 )
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002330 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002331 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2332 if (num_bytes == -1) // not enough coordinates
2333 return -1;
2334 mouse_code = bytes[0];
2335 mouse_col = 128 * (bytes[1] - ' ' - 1)
2336 + bytes[2] - ' ' - 1;
2337 mouse_row = 128 * (bytes[3] - ' ' - 1)
2338 + bytes[4] - ' ' - 1;
2339 }
2340 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002341# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002342 {
2343 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2344 if (num_bytes == -1) // not enough coordinates
2345 return -1;
2346 mouse_code = bytes[0];
2347 mouse_col = bytes[1] - ' ' - 1;
2348 mouse_row = bytes[2] - ' ' - 1;
2349 }
2350 *slen += num_bytes;
2351
Bram Moolenaar13c04632020-07-12 13:47:42 +02002352 // If the following bytes is also a mouse code and it has the same
2353 // code, dump this one and get the next. This makes dragging a
2354 // whole lot faster.
Christopher Plewright696d0a82022-11-18 17:53:34 +00002355# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002356 if (gui.in_use)
2357 j = 3;
2358 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002359# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002360 j = get_termcode_len(idx);
2361 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2362 && tp[*slen + j] == mouse_code
2363 && tp[*slen + j + 1] != NUL
2364 && tp[*slen + j + 2] != NUL
Christopher Plewright696d0a82022-11-18 17:53:34 +00002365# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002366 && (!gui.in_use
2367 || (tp[*slen + j + 3] != NUL
2368 && tp[*slen + j + 4] != NUL))
Christopher Plewright696d0a82022-11-18 17:53:34 +00002369# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002370 )
2371 *slen += j;
2372 else
2373 break;
2374 }
2375 }
2376
2377 if (key_name[0] == KS_URXVT_MOUSE
2378 || key_name[0] == KS_SGR_MOUSE
2379 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2380 {
2381 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002382 // Almost identical to xterm mouse mode, except the values are decimal
2383 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002384 //
2385 // \033[%d;%d;%dM
2386 // ^-- row
2387 // ^----- column
2388 // ^-------- code
2389 //
2390 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002391 // Almost identical to xterm mouse mode, except the values are decimal
2392 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002393 //
2394 // \033[<%d;%d;%dM
2395 // ^-- row
2396 // ^----- column
2397 // ^-------- code
2398 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002399 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002400 // ^-- row
2401 // ^----- column
2402 // ^-------- code
2403 p = modifiers_start;
2404 if (p == NULL)
2405 return -1;
2406
2407 mouse_code = getdigits(&p);
2408 if (*p++ != ';')
2409 return -1;
2410
2411 // when mouse reporting is SGR, add 32 to mouse code
2412 if (key_name[0] == KS_SGR_MOUSE
2413 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2414 mouse_code += 32;
2415
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002416 mouse_col = getdigits(&p) - 1;
2417 if (*p++ != ';')
2418 return -1;
2419
2420 mouse_row = getdigits(&p) - 1;
2421
Bram Moolenaar13c04632020-07-12 13:47:42 +02002422 // The modifiers were the mouse coordinates, not the modifier keys
2423 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002424 *modifiers = 0;
2425 }
2426
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002427 if (key_name[0] == KS_SGR_MOUSE
2428 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2429 {
2430 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002431 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002432 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002433 // This is used below to set held_button.
2434 mouse_code |= MOUSE_RELEASE;
2435 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002436 }
2437 else
2438 {
2439 release_is_ambiguous = TRUE;
2440 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2441 is_release = TRUE;
2442 }
2443
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002444 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002445# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002446 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002447# endif
2448# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002449 || key_name[0] == KS_URXVT_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002450# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002451 || key_name[0] == KS_SGR_MOUSE
2452 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2453 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002454# if !defined(MSWIN)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002455 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002456 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002457 * Recognize the xterm mouse wheel, but not in the GUI, the
2458 * Linux console with GPM and the MS-DOS or Win32 console
2459 * (multi-clicks use >= 0x60).
2460 */
2461 if (mouse_code >= MOUSEWHEEL_LOW
Christopher Plewright696d0a82022-11-18 17:53:34 +00002462# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002463 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002464# endif
2465# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002466 && key_name[0] != KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002467# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002468 )
2469 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002470# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002471 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2472 // mouse-move event, using MOUSE_DRAG works
2473 mouse_code = MOUSE_DRAG;
2474 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002475# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002476 // Keep the mouse_code before it's changed, so that we
2477 // remember that it was a mouse wheel click.
2478 wheel_code = mouse_code;
2479 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002480# ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002481 else if (held_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002482# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002483 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002484# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002485 && (mouse_code == 0x23 || mouse_code == 0x24
2486 || mouse_code == 0x40 || mouse_code == 0x41))
2487 {
2488 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2489 // And 0x40 and 0x41 are used by some xterm emulator.
2490 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002491 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002492 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002493# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002494
Christopher Plewright696d0a82022-11-18 17:53:34 +00002495# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002496 else if (use_xterm_mouse() > 1)
2497 {
2498 if (mouse_code & MOUSE_DRAG_XTERM)
2499 mouse_code |= MOUSE_DRAG;
2500 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002501# endif
2502# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002503 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2504 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002505 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002506 stop_xterm_trace();
2507 else
2508 start_xterm_trace(mouse_code);
2509 }
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002510# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002511# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002512 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002513#endif // !UNIX || FEAT_MOUSE_XTERM
2514#ifdef FEAT_MOUSE_NET
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002515 if (key_name[0] == KS_NETTERM_MOUSE)
2516 {
2517 int mc, mr;
2518
2519 // expect a rather limited sequence like: balancing {
2520 // \033}6,45\r
2521 // '6' is the row, 45 is the column
2522 p = tp + *slen;
2523 mr = getdigits(&p);
2524 if (*p++ != ',')
2525 return -1;
2526 mc = getdigits(&p);
2527 if (*p++ != '\r')
2528 return -1;
2529
2530 mouse_col = mc - 1;
2531 mouse_row = mr - 1;
2532 mouse_code = MOUSE_LEFT;
2533 *slen += (int)(p - (tp + *slen));
2534 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002535#endif // FEAT_MOUSE_NET
2536#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002537 if (key_name[0] == KS_JSBTERM_MOUSE)
2538 {
2539 int mult, val, iter, button, status;
2540
2541 /*
2542 * JSBTERM Input Model
2543 * \033[0~zw uniq escape sequence
2544 * (L-x) Left button pressed - not pressed x not reporting
2545 * (M-x) Middle button pressed - not pressed x not reporting
2546 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002547 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2548 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002549 * ### X cursor position padded to 3 digits
2550 * ### Y cursor position padded to 3 digits
2551 * (s-x) SHIFT key pressed - not pressed x not reporting
2552 * (c-x) CTRL key pressed - not pressed x not reporting
2553 * \033\\ terminating sequence
2554 */
2555 p = tp + *slen;
2556 button = mouse_code = 0;
2557 switch (*p++)
2558 {
2559 case 'L': button = 1; break;
2560 case '-': break;
2561 case 'x': break; // ignore sequence
2562 default: return -1; // Unknown Result
2563 }
2564 switch (*p++)
2565 {
2566 case 'M': button |= 2; break;
2567 case '-': break;
2568 case 'x': break; // ignore sequence
2569 default: return -1; // Unknown Result
2570 }
2571 switch (*p++)
2572 {
2573 case 'R': button |= 4; break;
2574 case '-': break;
2575 case 'x': break; // ignore sequence
2576 default: return -1; // Unknown Result
2577 }
2578 status = *p++;
2579 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2580 mult /= 10, p++)
2581 if (*p >= '0' && *p <= '9')
2582 val += (*p - '0') * mult;
2583 else
2584 return -1;
2585 mouse_col = val;
2586 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2587 mult /= 10, p++)
2588 if (*p >= '0' && *p <= '9')
2589 val += (*p - '0') * mult;
2590 else
2591 return -1;
2592 mouse_row = val;
2593 switch (*p++)
2594 {
2595 case 's': button |= 8; break; // SHIFT key Pressed
2596 case '-': break; // Not Pressed
2597 case 'x': break; // Not Reporting
2598 default: return -1; // Unknown Result
2599 }
2600 switch (*p++)
2601 {
2602 case 'c': button |= 16; break; // CTRL key Pressed
2603 case '-': break; // Not Pressed
2604 case 'x': break; // Not Reporting
2605 default: return -1; // Unknown Result
2606 }
2607 if (*p++ != '\033')
2608 return -1;
2609 if (*p++ != '\\')
2610 return -1;
2611 switch (status)
2612 {
2613 case 'D': // Double Click
2614 case 'S': // Single Click
2615 if (button & 1) mouse_code |= MOUSE_LEFT;
2616 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2617 if (button & 4) mouse_code |= MOUSE_RIGHT;
2618 if (button & 8) mouse_code |= MOUSE_SHIFT;
2619 if (button & 16) mouse_code |= MOUSE_CTRL;
2620 break;
2621 case 'm': // Mouse move
2622 if (button & 1) mouse_code |= MOUSE_LEFT;
2623 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2624 if (button & 4) mouse_code |= MOUSE_RIGHT;
2625 if (button & 8) mouse_code |= MOUSE_SHIFT;
2626 if (button & 16) mouse_code |= MOUSE_CTRL;
2627 if ((button & 7) != 0)
2628 {
2629 held_button = mouse_code;
2630 mouse_code |= MOUSE_DRAG;
2631 }
2632 is_drag = TRUE;
2633 showmode();
2634 break;
2635 case 'd': // Button Down
2636 if (button & 1) mouse_code |= MOUSE_LEFT;
2637 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2638 if (button & 4) mouse_code |= MOUSE_RIGHT;
2639 if (button & 8) mouse_code |= MOUSE_SHIFT;
2640 if (button & 16) mouse_code |= MOUSE_CTRL;
2641 break;
2642 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002643 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002644 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002645 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002646 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002647 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002648 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002649 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002650 if (button & 8)
2651 mouse_code |= MOUSE_SHIFT;
2652 if (button & 16)
2653 mouse_code |= MOUSE_CTRL;
2654 break;
2655 default: return -1; // Unknown Result
2656 }
2657
2658 *slen += (p - (tp + *slen));
2659 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002660#endif // FEAT_MOUSE_JSB
2661#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002662 if (key_name[0] == KS_DEC_MOUSE)
2663 {
2664 /*
2665 * The DEC Locator Input Model
2666 * Netterm delivers the code sequence:
2667 * \033[2;4;24;80&w (left button down)
2668 * \033[3;0;24;80&w (left button up)
2669 * \033[6;1;24;80&w (right button down)
2670 * \033[7;0;24;80&w (right button up)
2671 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2672 * Pe is the event code
2673 * Pb is the button code
2674 * Pr is the row coordinate
2675 * Pc is the column coordinate
2676 * Pp is the third coordinate (page number)
2677 * Pe, the event code indicates what event caused this report
2678 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002679 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002680 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002681 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002682 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002683 * 2 - left button down
2684 * 3 - left button up
2685 * 4 - middle button down
2686 * 5 - middle button up
2687 * 6 - right button down
2688 * 7 - right button up
2689 * 8 - fourth button down
2690 * 9 - fourth button up
2691 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002692 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2693 * down if any. The state of the four buttons on the locator
2694 * correspond to the low four bits of the decimal value, "1" means
2695 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002696 * 0 - no buttons down,
2697 * 1 - right,
2698 * 2 - middle,
2699 * 4 - left,
2700 * 8 - fourth
2701 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002702 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2703 * position is undefined (outside the terminal window for example).
2704 * Pc is the column coordinate of the locator position in the page,
2705 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2706 * position is undefined (outside the terminal window for example).
2707 * Pp is the page coordinate of the locator position encoded as an
2708 * ASCII decimal value. The page coordinate may be omitted if the
2709 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002710 */
2711 int Pe, Pb, Pr, Pc;
2712
2713 p = tp + *slen;
2714
2715 // get event status
2716 Pe = getdigits(&p);
2717 if (*p++ != ';')
2718 return -1;
2719
2720 // get button status
2721 Pb = getdigits(&p);
2722 if (*p++ != ';')
2723 return -1;
2724
2725 // get row status
2726 Pr = getdigits(&p);
2727 if (*p++ != ';')
2728 return -1;
2729
2730 // get column status
2731 Pc = getdigits(&p);
2732
2733 // the page parameter is optional
2734 if (*p == ';')
2735 {
2736 p++;
2737 (void)getdigits(&p);
2738 }
2739 if (*p++ != '&')
2740 return -1;
2741 if (*p++ != 'w')
2742 return -1;
2743
2744 mouse_code = 0;
2745 switch (Pe)
2746 {
2747 case 0: return -1; // position request while unavailable
2748 case 1: // a response to a locator position request includes
2749 // the status of all buttons
2750 Pb &= 7; // mask off and ignore fourth button
2751 if (Pb & 4)
2752 mouse_code = MOUSE_LEFT;
2753 if (Pb & 2)
2754 mouse_code = MOUSE_MIDDLE;
2755 if (Pb & 1)
2756 mouse_code = MOUSE_RIGHT;
2757 if (Pb)
2758 {
2759 held_button = mouse_code;
2760 mouse_code |= MOUSE_DRAG;
2761 WantQueryMouse = TRUE;
2762 }
2763 is_drag = TRUE;
2764 showmode();
2765 break;
2766 case 2: mouse_code = MOUSE_LEFT;
2767 WantQueryMouse = TRUE;
2768 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002769 case 3: mouse_code = MOUSE_LEFT;
2770 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002771 break;
2772 case 4: mouse_code = MOUSE_MIDDLE;
2773 WantQueryMouse = TRUE;
2774 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002775 case 5: mouse_code = MOUSE_MIDDLE;
2776 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002777 break;
2778 case 6: mouse_code = MOUSE_RIGHT;
2779 WantQueryMouse = TRUE;
2780 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002781 case 7: mouse_code = MOUSE_RIGHT;
2782 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002783 break;
2784 case 8: return -1; // fourth button down
2785 case 9: return -1; // fourth button up
2786 case 10: return -1; // mouse outside of filter rectangle
2787 default: return -1; // should never occur
2788 }
2789
2790 mouse_col = Pc - 1;
2791 mouse_row = Pr - 1;
2792
2793 *slen += (int)(p - (tp + *slen));
2794 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002795#endif // FEAT_MOUSE_DEC
2796#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002797 if (key_name[0] == KS_PTERM_MOUSE)
2798 {
2799 int button, num_clicks, action;
2800
2801 p = tp + *slen;
2802
2803 action = getdigits(&p);
2804 if (*p++ != ';')
2805 return -1;
2806
2807 mouse_row = getdigits(&p);
2808 if (*p++ != ';')
2809 return -1;
2810 mouse_col = getdigits(&p);
2811 if (*p++ != ';')
2812 return -1;
2813
2814 button = getdigits(&p);
2815 mouse_code = 0;
2816
2817 switch (button)
2818 {
2819 case 4: mouse_code = MOUSE_LEFT; break;
2820 case 1: mouse_code = MOUSE_RIGHT; break;
2821 case 2: mouse_code = MOUSE_MIDDLE; break;
2822 default: return -1;
2823 }
2824
2825 switch (action)
2826 {
2827 case 31: // Initial press
2828 if (*p++ != ';')
2829 return -1;
2830
2831 num_clicks = getdigits(&p); // Not used
2832 break;
2833
2834 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002835 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002836 break;
2837
2838 case 33: // Drag
2839 held_button = mouse_code;
2840 mouse_code |= MOUSE_DRAG;
2841 break;
2842
2843 default:
2844 return -1;
2845 }
2846
2847 if (*p++ != 't')
2848 return -1;
2849
2850 *slen += (p - (tp + *slen));
2851 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002852#endif // FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002853
2854 // Interpret the mouse code
2855 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002856 if (is_release)
2857 current_button |= MOUSE_RELEASE;
2858
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002859 if (current_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002860#ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002861 && wheel_code == 0
Christopher Plewright696d0a82022-11-18 17:53:34 +00002862#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002863 )
2864 {
2865 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002866 * If we get a mouse drag or release event when there is no mouse
2867 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2868 * below.
2869 * (can happen when you hold down two buttons and then let them go, or
2870 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002871 */
2872 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2873 is_drag = TRUE;
2874 current_button = held_button;
2875 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002876 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002877 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002878 if (wheel_code == 0)
2879 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002880#ifdef CHECK_DOUBLE_CLICK
2881# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002882 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002883 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2884 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002885 */
Christopher Plewright696d0a82022-11-18 17:53:34 +00002886# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002887 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002888# else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002889 if (key_name[0] != KS_GPM_MOUSE)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002890# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002891# else
2892# ifdef FEAT_GUI
2893 if (!gui.in_use)
2894# endif
2895# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002896 {
2897 /*
2898 * Compute the time elapsed since the previous mouse click.
2899 */
2900 gettimeofday(&mouse_time, NULL);
2901 if (orig_mouse_time.tv_sec == 0)
2902 {
2903 /*
2904 * Avoid computing the difference between mouse_time
2905 * and orig_mouse_time for the first click, as the
2906 * difference would be huge and would cause
2907 * multiplication overflow.
2908 */
2909 timediff = p_mouset;
2910 }
2911 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002912 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002913 orig_mouse_time = mouse_time;
2914 if (mouse_code == orig_mouse_code
2915 && timediff < p_mouset
2916 && orig_num_clicks != 4
2917 && orig_mouse_col == mouse_col
2918 && orig_mouse_row == mouse_row
2919 && (is_mouse_topline(curwin)
2920 // Double click in tab pages line also works
2921 // when window contents changes.
2922 || (mouse_row == 0 && firstwin->w_winrow > 0))
2923 )
2924 ++orig_num_clicks;
2925 else
2926 orig_num_clicks = 1;
2927 orig_mouse_col = mouse_col;
2928 orig_mouse_row = mouse_row;
2929 set_mouse_topline(curwin);
2930 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002931# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002932 else
2933 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002934# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002935#else
2936 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2937#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002938 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002939 }
2940 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002941 }
2942 if (!is_drag)
2943 held_button = mouse_code & MOUSE_CLICK_MASK;
2944
2945 /*
2946 * Translate the actual mouse event into a pseudo mouse event.
2947 * First work out what modifiers are to be used.
2948 */
2949 if (orig_mouse_code & MOUSE_SHIFT)
2950 *modifiers |= MOD_MASK_SHIFT;
2951 if (orig_mouse_code & MOUSE_CTRL)
2952 *modifiers |= MOD_MASK_CTRL;
2953 if (orig_mouse_code & MOUSE_ALT)
2954 *modifiers |= MOD_MASK_ALT;
2955 if (orig_num_clicks == 2)
2956 *modifiers |= MOD_MASK_2CLICK;
2957 else if (orig_num_clicks == 3)
2958 *modifiers |= MOD_MASK_3CLICK;
2959 else if (orig_num_clicks == 4)
2960 *modifiers |= MOD_MASK_4CLICK;
2961
Bram Moolenaar13c04632020-07-12 13:47:42 +02002962 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2963 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002964 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002965 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002966 {
2967 if (wheel_code & MOUSE_CTRL)
2968 *modifiers |= MOD_MASK_CTRL;
2969 if (wheel_code & MOUSE_ALT)
2970 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002971
2972 if (wheel_code & 1 && wheel_code & 2)
2973 key_name[1] = (int)KE_MOUSELEFT;
2974 else if (wheel_code & 2)
2975 key_name[1] = (int)KE_MOUSERIGHT;
2976 else if (wheel_code & 1)
2977 key_name[1] = (int)KE_MOUSEUP;
2978 else
2979 key_name[1] = (int)KE_MOUSEDOWN;
2980
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002981 held_button = MOUSE_RELEASE;
2982 }
2983 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002984 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002985
Bram Moolenaar13c04632020-07-12 13:47:42 +02002986
2987 // Make sure the mouse position is valid. Some terminals may return weird
2988 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002989 if (mouse_col >= Columns)
2990 mouse_col = Columns - 1;
2991 if (mouse_row >= Rows)
2992 mouse_row = Rows - 1;
2993
2994 return 0;
2995}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002996
2997// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002998
2999/*
3000 * Compute the buffer line position from the screen position "rowp" / "colp" in
3001 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02003002 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
3003 * caches the plines_win() result from a previous call. Entry is zero if not
3004 * computed yet. There must be no text or setting changes since the entry is
3005 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003006 * Returns TRUE if the position is below the last line.
3007 */
3008 int
3009mouse_comp_pos(
3010 win_T *win,
3011 int *rowp,
3012 int *colp,
3013 linenr_T *lnump,
3014 int *plines_cache)
3015{
3016 int col = *colp;
3017 int row = *rowp;
3018 linenr_T lnum;
3019 int retval = FALSE;
3020 int off;
3021 int count;
3022
3023#ifdef FEAT_RIGHTLEFT
3024 if (win->w_p_rl)
3025 col = win->w_width - 1 - col;
3026#endif
3027
3028 lnum = win->w_topline;
3029
3030 while (row > 0)
3031 {
3032 int cache_idx = lnum - win->w_topline;
3033
Bram Moolenaar452143c2020-07-15 17:38:21 +02003034 // Only "Rows" lines are cached, with folding we'll run out of entries
3035 // and use the slow way.
3036 if (plines_cache != NULL && cache_idx < Rows
3037 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003038 count = plines_cache[cache_idx];
3039 else
3040 {
3041#ifdef FEAT_DIFF
3042 // Don't include filler lines in "count"
3043 if (win->w_p_diff
3044# ifdef FEAT_FOLDING
3045 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3046# endif
3047 )
3048 {
3049 if (lnum == win->w_topline)
3050 row -= win->w_topfill;
3051 else
3052 row -= diff_check_fill(win, lnum);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003053 count = plines_win_nofill(win, lnum, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003054 }
3055 else
3056#endif
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003057 count = plines_win(win, lnum, FALSE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02003058 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003059 plines_cache[cache_idx] = count;
3060 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003061
3062 if (win->w_skipcol > 0 && lnum == win->w_topline)
3063 {
3064 // Adjust for 'smoothscroll' clipping the top screen lines.
3065 // A similar formula is used in curs_columns().
3066 int width1 = win->w_width - win_col_off(win);
3067 int skip_lines = 0;
3068 if (win->w_skipcol > width1)
3069 skip_lines = (win->w_skipcol - width1)
3070 / (width1 + win_col_off2(win)) + 1;
3071 else if (win->w_skipcol > 0)
3072 skip_lines = 1;
3073 count -= skip_lines;
3074 }
3075
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003076 if (count > row)
3077 break; // Position is in this buffer line.
3078#ifdef FEAT_FOLDING
3079 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3080#endif
3081 if (lnum == win->w_buffer->b_ml.ml_line_count)
3082 {
3083 retval = TRUE;
3084 break; // past end of file
3085 }
3086 row -= count;
3087 ++lnum;
3088 }
3089
3090 if (!retval)
3091 {
3092 // Compute the column without wrapping.
3093 off = win_col_off(win) - win_col_off2(win);
3094 if (col < off)
3095 col = off;
3096 col += row * (win->w_width - off);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003097
3098 // Add skip column for the topline.
3099 if (lnum == win->w_topline)
3100 col += win->w_skipcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003101 }
3102
3103 if (!win->w_p_wrap)
3104 col += win->w_leftcol;
3105
3106 // skip line number and fold column in front of the line
3107 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003108 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003109 {
3110#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003111 // if mouse is clicked on the gutter, then inform the netbeans server
3112 if (*colp < win_col_off(win))
3113 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003114#endif
3115 col = 0;
3116 }
3117
3118 *colp = col;
3119 *rowp = row;
3120 *lnump = lnum;
3121 return retval;
3122}
3123
3124/*
3125 * Find the window at screen position "*rowp" and "*colp". The positions are
3126 * updated to become relative to the top-left of the window.
3127 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3128 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3129 * windows.
3130 * Returns NULL when something is wrong.
3131 */
3132 win_T *
3133mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3134{
3135 frame_T *fp;
3136 win_T *wp;
3137
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003138#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003139 win_T *pwp = NULL;
3140
3141 if (popup != IGNORE_POPUP)
3142 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003143 popup_reset_handled(POPUP_HANDLED_1);
3144 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003145 {
3146 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3147 && *colp >= wp->w_wincol
3148 && *colp < wp->w_wincol + popup_width(wp))
3149 pwp = wp;
3150 }
3151 if (pwp != NULL)
3152 {
3153 if (popup == FAIL_POPUP)
3154 return NULL;
3155 *rowp -= pwp->w_winrow;
3156 *colp -= pwp->w_wincol;
3157 return pwp;
3158 }
3159 }
3160#endif
3161
3162 fp = topframe;
3163 *rowp -= firstwin->w_winrow;
3164 for (;;)
3165 {
3166 if (fp->fr_layout == FR_LEAF)
3167 break;
3168 if (fp->fr_layout == FR_ROW)
3169 {
3170 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3171 {
3172 if (*colp < fp->fr_width)
3173 break;
3174 *colp -= fp->fr_width;
3175 }
3176 }
3177 else // fr_layout == FR_COL
3178 {
3179 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3180 {
3181 if (*rowp < fp->fr_height)
3182 break;
3183 *rowp -= fp->fr_height;
3184 }
3185 }
3186 }
3187 // When using a timer that closes a window the window might not actually
3188 // exist.
3189 FOR_ALL_WINDOWS(wp)
3190 if (wp == fp->fr_win)
3191 {
3192#ifdef FEAT_MENU
3193 *rowp -= wp->w_winbar_height;
3194#endif
3195 return wp;
3196 }
3197 return NULL;
3198}
3199
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003200#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003201 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003202/*
3203 * Convert a virtual (screen) column to a character column.
zeertzjqb583eda2023-10-14 11:32:28 +02003204 * The first column is zero.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003205 */
3206 int
zeertzjqf5a94d52023-10-15 10:03:30 +02003207vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003208{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003209 char_u *line;
3210 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003211
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003212 // try to advance to the specified column
3213 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3214 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3215 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003216 {
zeertzjqb583eda2023-10-14 11:32:28 +02003217 int size = win_lbr_chartabsize(&cts, NULL);
3218 if (cts.cts_vcol + size > vcol)
3219 break;
3220 cts.cts_vcol += size;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003221 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003222 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003223 clear_chartabsize_arg(&cts);
3224
zeertzjqf5a94d52023-10-15 10:03:30 +02003225 if (coladdp != NULL)
3226 *coladdp = vcol - cts.cts_vcol;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003227 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003228}
3229#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003230
3231#if defined(FEAT_EVAL) || defined(PROTO)
Christopher Plewright696d0a82022-11-18 17:53:34 +00003232/*
3233 * "getmousepos()" function.
3234 */
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003235 void
3236f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3237{
3238 dict_T *d;
3239 win_T *wp;
3240 int row = mouse_row;
3241 int col = mouse_col;
3242 varnumber_T winid = 0;
3243 varnumber_T winrow = 0;
3244 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003245 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003246 varnumber_T column = 0;
zeertzjqf5a94d52023-10-15 10:03:30 +02003247 colnr_T coladd = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003248
Bram Moolenaar93a10962022-06-16 11:42:09 +01003249 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003250 return;
3251 d = rettv->vval.v_dict;
3252
3253 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3254 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3255
3256 wp = mouse_find_win(&row, &col, FIND_POPUP);
3257 if (wp != NULL)
3258 {
3259 int top_off = 0;
3260 int left_off = 0;
3261 int height = wp->w_height + wp->w_status_height;
3262
Christopher Plewright696d0a82022-11-18 17:53:34 +00003263# ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003264 if (WIN_IS_POPUP(wp))
3265 {
3266 top_off = popup_top_extra(wp);
3267 left_off = popup_left_extra(wp);
3268 height = popup_height(wp);
3269 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00003270# endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003271 if (row < height)
3272 {
3273 winid = wp->w_id;
3274 winrow = row + 1;
3275 wincol = col + 1;
3276 row -= top_off;
3277 col -= left_off;
3278 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3279 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003280 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
zeertzjqf5a94d52023-10-15 10:03:30 +02003281 col = vcol2col(wp, lnum, col, &coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003282 column = col + 1;
3283 }
3284 }
3285 }
3286 dict_add_number(d, "winid", winid);
3287 dict_add_number(d, "winrow", winrow);
3288 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003289 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003290 dict_add_number(d, "column", column);
zeertzjqf5a94d52023-10-15 10:03:30 +02003291 dict_add_number(d, "coladd", coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003292}
3293#endif