blob: 0d05e643d32e2ebf29542951b571d8c3a48cbbdd [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
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200235#if defined(FEAT_TABPANEL)
236 static int in_tabpanel = FALSE; // mouse clicked in tabpanel
237#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200238 int in_sep_line; // mouse in vertical separator line
239 int c1, c2;
240#if defined(FEAT_FOLDING)
241 pos_T save_cursor;
242#endif
243 win_T *old_curwin = curwin;
244 static pos_T orig_cursor;
245 colnr_T leftcol, rightcol;
246 pos_T end_visual;
247 int diff;
248 int old_active = VIsual_active;
249 int old_mode = VIsual_mode;
250 int regname;
251
252#if defined(FEAT_FOLDING)
253 save_cursor = curwin->w_cursor;
254#endif
255
256 // When GUI is active, always recognize mouse events, otherwise:
257 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
258 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
259 // - For command line and insert mode 'mouse' is checked before calling
260 // do_mouse().
261 if (do_always)
262 do_always = FALSE;
263 else
264#ifdef FEAT_GUI
265 if (!gui.in_use)
266#endif
267 {
268 if (VIsual_active)
269 {
270 if (!mouse_has(MOUSE_VISUAL))
271 return FALSE;
272 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100273 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200274 return FALSE;
275 }
276
277 for (;;)
278 {
279 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
280 if (is_drag)
281 {
282 // If the next character is the same mouse event then use that
283 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100284 // Note: Since characters added to the stuff buffer in the code
285 // below need to come before the next character, do not do this
286 // when the current character was stuffed.
287 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200288 {
289 int nc;
290 int save_mouse_row = mouse_row;
291 int save_mouse_col = mouse_col;
292
293 // Need to get the character, peeking doesn't get the actual
294 // one.
295 nc = safe_vgetc();
296 if (c == nc)
297 continue;
298 vungetc(nc);
299 mouse_row = save_mouse_row;
300 mouse_col = save_mouse_col;
301 }
302 }
303 break;
304 }
305
306 if (c == K_MOUSEMOVE)
307 {
308 // Mouse moved without a button pressed.
309#ifdef FEAT_BEVAL_TERM
310 ui_may_remove_balloon();
311 if (p_bevalterm)
312 {
313 profile_setlimit(p_bdlay, &bevalexpr_due);
314 bevalexpr_due_set = TRUE;
315 }
316#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100317#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200318 popup_handle_mouse_moved();
319#endif
320 return FALSE;
321 }
322
323#ifdef FEAT_MOUSESHAPE
324 // May have stopped dragging the status or separator line. The pointer is
325 // most likely still on the status or separator line.
326 if (!is_drag && drag_status_line)
327 {
328 drag_status_line = FALSE;
329 update_mouseshape(SHAPE_IDX_STATUS);
330 }
331 if (!is_drag && drag_sep_line)
332 {
333 drag_sep_line = FALSE;
334 update_mouseshape(SHAPE_IDX_VSEP);
335 }
336#endif
337
338 // Ignore drag and release events if we didn't get a click.
339 if (is_click)
zeertzjq8e0ccb62022-11-01 18:35:27 +0000340 got_click = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200341 else
342 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000343 if (!got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200344 return FALSE;
zeertzjq8e0ccb62022-11-01 18:35:27 +0000345 if (!is_drag) // release, reset got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200346 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000347 got_click = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200348 if (in_tab_line
349#if defined(FEAT_TABPANEL)
350 || in_tabpanel
351#endif
352 )
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200353 {
354 in_tab_line = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200355#if defined(FEAT_TABPANEL)
356 in_tabpanel = FALSE;
357#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200358 return FALSE;
359 }
360 }
361 }
362
363 // CTRL right mouse button does CTRL-T
364 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
365 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100366 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200367 stuffcharReadbuff(Ctrl_O);
368 if (count > 1)
369 stuffnumReadbuff(count);
370 stuffcharReadbuff(Ctrl_T);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000371 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200372 return FALSE;
373 }
374
375 // CTRL only works with left mouse button
376 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
377 return FALSE;
378
379 // When a modifier is down, ignore drag and release events, as well as
380 // multiple clicks and the middle mouse button.
381 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
382 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
383 | MOD_MASK_META))
384 && (!is_click
385 || (mod_mask & MOD_MASK_MULTI_CLICK)
386 || which_button == MOUSE_MIDDLE)
387 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
388 && mouse_model_popup()
389 && which_button == MOUSE_LEFT)
390 && !((mod_mask & MOD_MASK_ALT)
391 && !mouse_model_popup()
392 && which_button == MOUSE_RIGHT)
393 )
394 return FALSE;
395
396 // If the button press was used as the movement command for an operator
397 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
398 // drag/release events.
399 if (!is_click && which_button == MOUSE_MIDDLE)
400 return FALSE;
401
402 if (oap != NULL)
403 regname = oap->regname;
404 else
405 regname = 0;
406
407 // Middle mouse button does a 'put' of the selected text
408 if (which_button == MOUSE_MIDDLE)
409 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100410 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200411 {
412 // If an operator was pending, we don't know what the user wanted
413 // to do. Go back to normal mode: Clear the operator and beep().
414 if (oap != NULL && oap->op_type != OP_NOP)
415 {
416 clearopbeep(oap);
417 return FALSE;
418 }
419
420 // If visual was active, yank the highlighted text and put it
421 // before the mouse pointer position.
422 // In Select mode replace the highlighted text with the clipboard.
423 if (VIsual_active)
424 {
425 if (VIsual_select)
426 {
427 stuffcharReadbuff(Ctrl_G);
428 stuffReadbuff((char_u *)"\"+p");
429 }
430 else
431 {
432 stuffcharReadbuff('y');
433 stuffcharReadbuff(K_MIDDLEMOUSE);
434 }
435 do_always = TRUE; // ignore 'mouse' setting next time
436 return FALSE;
437 }
438 // The rest is below jump_to_mouse()
439 }
440
Bram Moolenaar24959102022-05-07 20:01:16 +0100441 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200442 return FALSE;
443
444 // Middle click in insert mode doesn't move the mouse, just insert the
445 // contents of a register. '.' register is special, can't insert that
446 // with do_put().
447 // Also paste at the cursor if the current mode isn't in 'mouse' (only
448 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100449 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200450 {
451 if (regname == '.')
452 insert_reg(regname, TRUE);
453 else
454 {
455#ifdef FEAT_CLIPBOARD
456 if (clip_star.available && regname == 0)
457 regname = '*';
458#endif
459 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
460 insert_reg(regname, TRUE);
461 else
462 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200463 do_put(regname, NULL, BACKWARD, 1L,
464 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200465
466 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
467 AppendCharToRedobuff(Ctrl_R);
468 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
469 AppendCharToRedobuff(regname == 0 ? '"' : regname);
470 }
471 }
472 return FALSE;
473 }
474 }
475
476 // When dragging or button-up stay in the same window.
477 if (!is_click)
478 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
479
480 start_visual.lnum = 0;
481
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200482 // Check for clicking in the tab page panel.
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200483#if defined(FEAT_TABPANEL)
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200484 if (mouse_row < firstwin->w_winrow + topframe->fr_height
485 && (mouse_col < firstwin->w_wincol
486 || mouse_col >= firstwin->w_wincol + topframe->fr_width))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200487 {
488 if (is_drag)
489 {
490 if (in_tabpanel)
491 {
492 c1 = get_tabpagenr_on_tabpanel();
493 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
494 ? c1 - 1 : c1);
495 }
496 return FALSE;
497 }
498
499 // click in a tab selects that tab page
500 if (is_click
501# ifdef FEAT_CMDWIN
502 && cmdwin_type == 0
503# endif
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200504 )
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200505 {
506 in_tabpanel = TRUE;
507 c1 = get_tabpagenr_on_tabpanel();
508 if (c1 >= 0)
509 {
510 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
511 {
512 // double click opens new page
513 end_visual_mode();
514 tabpage_new();
515 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
516 }
517 else
518 {
519 // Go to specified tab page, or next one if not clicking
520 // on a label.
521 goto_tabpage(c1);
522
523 // It's like clicking on the status line of a window.
524 if (curwin != old_curwin)
525 end_visual_mode();
526 }
527 }
528 else
529 {
530 tabpage_T *tp;
531
532 // Close the current or specified tab page.
533 if (c1 == -999)
534 tp = curtab;
535 else
536 tp = find_tabpage(-c1);
537 if (tp == curtab)
538 {
539 if (first_tabpage->tp_next != NULL)
540 tabpage_close(FALSE);
541 }
542 else if (tp != NULL)
543 tabpage_close_other(tp, FALSE);
544 }
545 }
546 return TRUE;
547 }
548 else if (is_drag && in_tabpanel)
549 {
550 c1 = get_tabpagenr_on_tabpanel();
551 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
552 return FALSE;
553 }
554#endif
555
Bram Moolenaar80525752022-08-24 19:27:45 +0100556 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200557 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100558 // Check for clicking in the tab page line.
559 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200560 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100561 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200562 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100563 if (in_tab_line)
564 {
565 c1 = TabPageIdxs[mouse_col];
566 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100567 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100568 }
569 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200570 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100571
572 // click in a tab selects that tab page
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200573 if (is_click && cmdwin_type == 0
574 && mouse_col < firstwin->w_wincol + topframe->fr_width)
Bram Moolenaar80525752022-08-24 19:27:45 +0100575 {
576 in_tab_line = TRUE;
577 c1 = TabPageIdxs[mouse_col];
578 if (c1 >= 0)
579 {
580 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
581 {
582 // double click opens new page
583 end_visual_mode_keep_button();
584 tabpage_new();
585 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
586 }
587 else
588 {
589 // Go to specified tab page, or next one if not clicking
590 // on a label.
591 goto_tabpage(c1);
592
593 // It's like clicking on the status line of a window.
594 if (curwin != old_curwin)
595 end_visual_mode_keep_button();
596 }
597 }
598 else
599 {
600 tabpage_T *tp;
601
602 // Close the current or specified tab page.
603 if (c1 == -999)
604 tp = curtab;
605 else
606 tp = find_tabpage(-c1);
607 if (tp == curtab)
608 {
609 if (first_tabpage->tp_next != NULL)
610 tabpage_close(FALSE);
611 }
612 else if (tp != NULL)
613 tabpage_close_other(tp, FALSE);
614 }
615 }
616 return TRUE;
617 }
618 else if (is_drag && in_tab_line)
619 {
620 c1 = TabPageIdxs[mouse_col];
621 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200622 return FALSE;
623 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200624 }
625
626 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
627 // right button up -> pop-up menu
628 // shift-left button -> right button
629 // alt-left button -> alt-right button
630 if (mouse_model_popup())
631 {
632 if (which_button == MOUSE_RIGHT
633 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
634 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200635#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200636# ifdef FEAT_GUI
637 if (gui.in_use)
638 {
639# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200640 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200641 if (!is_click)
642 // Ignore right button release events, only shows the popup
643 // menu on the button down event.
644 return FALSE;
645# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100646# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200647 if (is_click || is_drag)
648 // Ignore right button down and drag mouse events. Windows
649 // only shows the popup menu on the button up event.
650 return FALSE;
651# endif
652 }
653# endif
654# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
655 else
656# endif
657# if defined(FEAT_TERM_POPUP_MENU)
658 if (!is_click)
659 // Ignore right button release events, only shows the popup
660 // menu on the button down event.
661 return FALSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +0000662# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200663
664 jump_flags = 0;
665 if (STRCMP(p_mousem, "popup_setpos") == 0)
666 {
667 // First set the cursor position before showing the popup
668 // menu.
669 if (VIsual_active)
670 {
671 pos_T m_pos;
672
673 // set MOUSE_MAY_STOP_VIS if we are outside the
674 // selection or the current window (might have false
675 // negative here)
676 if (mouse_row < curwin->w_winrow
677 || mouse_row
678 > (curwin->w_winrow + curwin->w_height))
679 jump_flags = MOUSE_MAY_STOP_VIS;
680 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
681 jump_flags = MOUSE_MAY_STOP_VIS;
682 else
683 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100684 if (VIsual_mode == 'V')
685 {
686 if ((curwin->w_cursor.lnum <= VIsual.lnum
687 && (m_pos.lnum < curwin->w_cursor.lnum
688 || VIsual.lnum < m_pos.lnum))
689 || (VIsual.lnum < curwin->w_cursor.lnum
690 && (m_pos.lnum < VIsual.lnum
691 || curwin->w_cursor.lnum < m_pos.lnum)))
692 {
693 jump_flags = MOUSE_MAY_STOP_VIS;
694 }
695 }
696 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200697 && (LT_POS(m_pos, curwin->w_cursor)
698 || LT_POS(VIsual, m_pos)))
699 || (LT_POS(VIsual, curwin->w_cursor)
700 && (LT_POS(m_pos, VIsual)
701 || LT_POS(curwin->w_cursor, m_pos))))
702 {
703 jump_flags = MOUSE_MAY_STOP_VIS;
704 }
705 else if (VIsual_mode == Ctrl_V)
706 {
707 getvcols(curwin, &curwin->w_cursor, &VIsual,
708 &leftcol, &rightcol);
709 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
710 if (m_pos.col < leftcol || m_pos.col > rightcol)
711 jump_flags = MOUSE_MAY_STOP_VIS;
712 }
713 }
714 }
715 else
716 jump_flags = MOUSE_MAY_STOP_VIS;
717 }
718 if (jump_flags)
719 {
720 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100721 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200722 setcursor();
723 out_flush(); // Update before showing popup menu
724 }
725# ifdef FEAT_MENU
726 show_popupmenu();
zeertzjq8e0ccb62022-11-01 18:35:27 +0000727 got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200728# endif
729 return (jump_flags & CURSOR_MOVED) != 0;
730#else
731 return FALSE;
732#endif
733 }
734 if (which_button == MOUSE_LEFT
735 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
736 {
737 which_button = MOUSE_RIGHT;
738 mod_mask &= ~MOD_MASK_SHIFT;
739 }
740 }
741
Bram Moolenaar24959102022-05-07 20:01:16 +0100742 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200743 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
744 {
745 if (which_button == MOUSE_LEFT)
746 {
747 if (is_click)
748 {
749 // stop Visual mode for a left click in a window, but not when
750 // on a status line
751 if (VIsual_active)
752 jump_flags |= MOUSE_MAY_STOP_VIS;
753 }
754 else if (mouse_has(MOUSE_VISUAL))
755 jump_flags |= MOUSE_MAY_VIS;
756 }
757 else if (which_button == MOUSE_RIGHT)
758 {
759 if (is_click && VIsual_active)
760 {
761 // Remember the start and end of visual before moving the
762 // cursor.
763 if (LT_POS(curwin->w_cursor, VIsual))
764 {
765 start_visual = curwin->w_cursor;
766 end_visual = VIsual;
767 }
768 else
769 {
770 start_visual = VIsual;
771 end_visual = curwin->w_cursor;
772 }
773 }
774 jump_flags |= MOUSE_FOCUS;
775 if (mouse_has(MOUSE_VISUAL))
776 jump_flags |= MOUSE_MAY_VIS;
777 }
778 }
779
780 // If an operator is pending, ignore all drags and releases until the
781 // next mouse click.
782 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
783 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000784 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200785 oap->motion_type = MCHAR;
786 }
787
788 // When releasing the button let jump_to_mouse() know.
789 if (!is_click && !is_drag)
790 jump_flags |= MOUSE_RELEASED;
791
792 // JUMP!
793 jump_flags = jump_to_mouse(jump_flags,
794 oap == NULL ? NULL : &(oap->inclusive), which_button);
795
796#ifdef FEAT_MENU
797 // A click in the window toolbar has no side effects.
798 if (jump_flags & MOUSE_WINBAR)
799 return FALSE;
800#endif
801 moved = (jump_flags & CURSOR_MOVED);
802 in_status_line = (jump_flags & IN_STATUS_LINE);
803 in_sep_line = (jump_flags & IN_SEP_LINE);
804
805#ifdef FEAT_NETBEANS_INTG
806 if (isNetbeansBuffer(curbuf)
807 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
808 {
809 int key = KEY2TERMCAP1(c);
810
811 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
812 || key == (int)KE_RIGHTRELEASE)
813 netbeans_button_release(which_button);
814 }
815#endif
816
817 // When jumping to another window, clear a pending operator. That's a bit
818 // friendlier than beeping and not jumping to that window.
819 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
820 clearop(oap);
821
822#ifdef FEAT_FOLDING
823 if (mod_mask == 0
824 && !is_drag
825 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
826 && which_button == MOUSE_LEFT)
827 {
828 // open or close a fold at this line
829 if (jump_flags & MOUSE_FOLD_OPEN)
830 openFold(curwin->w_cursor.lnum, 1L);
831 else
832 closeFold(curwin->w_cursor.lnum, 1L);
833 // don't move the cursor if still in the same window
834 if (curwin == old_curwin)
835 curwin->w_cursor = save_cursor;
836 }
837#endif
838
Martin Tournoij7904fa42022-10-04 16:28:45 +0100839#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200840 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
841 {
842 clip_modeless(which_button, is_click, is_drag);
843 return FALSE;
844 }
845#endif
846
847 // Set global flag that we are extending the Visual area with mouse
848 // dragging; temporarily minimize 'scrolloff'.
849 if (VIsual_active && is_drag && get_scrolloff_value())
850 {
851 // In the very first line, allow scrolling one line
852 if (mouse_row == 0)
853 mouse_dragging = 2;
854 else
855 mouse_dragging = 1;
856 }
857
858 // When dragging the mouse above the window, scroll down.
859 if (is_drag && mouse_row < 0 && !in_status_line)
860 {
861 scroll_redraw(FALSE, 1L);
862 mouse_row = 0;
863 }
864
865 if (start_visual.lnum) // right click in visual mode
866 {
867 // When ALT is pressed make Visual mode blockwise.
868 if (mod_mask & MOD_MASK_ALT)
869 VIsual_mode = Ctrl_V;
870
871 // In Visual-block mode, divide the area in four, pick up the corner
872 // that is in the quarter that the cursor is in.
873 if (VIsual_mode == Ctrl_V)
874 {
875 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
876 if (curwin->w_curswant > (leftcol + rightcol) / 2)
877 end_visual.col = leftcol;
878 else
879 end_visual.col = rightcol;
880 if (curwin->w_cursor.lnum >=
881 (start_visual.lnum + end_visual.lnum) / 2)
882 end_visual.lnum = start_visual.lnum;
883
884 // move VIsual to the right column
885 start_visual = curwin->w_cursor; // save the cursor pos
886 curwin->w_cursor = end_visual;
887 coladvance(end_visual.col);
888 VIsual = curwin->w_cursor;
889 curwin->w_cursor = start_visual; // restore the cursor
890 }
891 else
892 {
893 // If the click is before the start of visual, change the start.
894 // If the click is after the end of visual, change the end. If
895 // the click is inside the visual, change the closest side.
896 if (LT_POS(curwin->w_cursor, start_visual))
897 VIsual = end_visual;
898 else if (LT_POS(end_visual, curwin->w_cursor))
899 VIsual = start_visual;
900 else
901 {
902 // In the same line, compare column number
903 if (end_visual.lnum == start_visual.lnum)
904 {
905 if (curwin->w_cursor.col - start_visual.col >
906 end_visual.col - curwin->w_cursor.col)
907 VIsual = start_visual;
908 else
909 VIsual = end_visual;
910 }
911
912 // In different lines, compare line number
913 else
914 {
915 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
916 (end_visual.lnum - curwin->w_cursor.lnum);
917
918 if (diff > 0) // closest to end
919 VIsual = start_visual;
920 else if (diff < 0) // closest to start
921 VIsual = end_visual;
922 else // in the middle line
923 {
924 if (curwin->w_cursor.col <
925 (start_visual.col + end_visual.col) / 2)
926 VIsual = end_visual;
927 else
928 VIsual = start_visual;
929 }
930 }
931 }
932 }
933 }
934 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100935 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200936 stuffcharReadbuff(Ctrl_O);
937
938 // Middle mouse click: Put text before cursor.
939 if (which_button == MOUSE_MIDDLE)
940 {
941#ifdef FEAT_CLIPBOARD
942 if (clip_star.available && regname == 0)
943 regname = '*';
944#endif
945 if (yank_register_mline(regname))
946 {
947 if (mouse_past_bottom)
948 dir = FORWARD;
949 }
950 else if (mouse_past_eol)
951 dir = FORWARD;
952
953 if (fixindent)
954 {
955 c1 = (dir == BACKWARD) ? '[' : ']';
956 c2 = 'p';
957 }
958 else
959 {
960 c1 = (dir == FORWARD) ? 'p' : 'P';
961 c2 = NUL;
962 }
963 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
964
965 // Remember where the paste started, so in edit() Insstart can be set
966 // to this position
967 if (restart_edit != 0)
968 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200969 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200970 }
971
972#if defined(FEAT_QUICKFIX)
973 // Ctrl-Mouse click or double click in a quickfix window jumps to the
974 // error under the mouse pointer.
975 else if (((mod_mask & MOD_MASK_CTRL)
976 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
977 && bt_quickfix(curbuf))
978 {
979 if (curwin->w_llist_ref == NULL) // quickfix window
980 do_cmdline_cmd((char_u *)".cc");
981 else // location list window
982 do_cmdline_cmd((char_u *)".ll");
zeertzjq8e0ccb62022-11-01 18:35:27 +0000983 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200984 }
985#endif
986
987 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
988 // under the mouse pointer.
989 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
990 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
991 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100992 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200993 stuffcharReadbuff(Ctrl_O);
994 stuffcharReadbuff(Ctrl_RSB);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000995 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200996 }
997
998 // Shift-Mouse click searches for the next occurrence of the word under
999 // the mouse pointer
1000 else if ((mod_mask & MOD_MASK_SHIFT))
1001 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001002 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001003 stuffcharReadbuff(Ctrl_O);
1004 if (which_button == MOUSE_LEFT)
1005 stuffcharReadbuff('*');
1006 else // MOUSE_RIGHT
1007 stuffcharReadbuff('#');
1008 }
1009
1010 // Handle double clicks, unless on status line
1011 else if (in_status_line)
1012 {
1013#ifdef FEAT_MOUSESHAPE
1014 if ((is_drag || is_click) && !drag_status_line)
1015 {
1016 drag_status_line = TRUE;
1017 update_mouseshape(-1);
1018 }
1019#endif
1020 }
1021 else if (in_sep_line)
1022 {
1023#ifdef FEAT_MOUSESHAPE
1024 if ((is_drag || is_click) && !drag_sep_line)
1025 {
1026 drag_sep_line = TRUE;
1027 update_mouseshape(-1);
1028 }
1029#endif
1030 }
Bram Moolenaar24959102022-05-07 20:01:16 +01001031 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
1032 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001033 && mouse_has(MOUSE_VISUAL))
1034 {
1035 if (is_click || !VIsual_active)
1036 {
1037 if (VIsual_active)
1038 orig_cursor = VIsual;
1039 else
1040 {
1041 check_visual_highlight();
1042 VIsual = curwin->w_cursor;
1043 orig_cursor = VIsual;
1044 VIsual_active = TRUE;
1045 VIsual_reselect = TRUE;
1046 // start Select mode if 'selectmode' contains "mouse"
1047 may_start_select('o');
1048 setmouse();
1049 }
1050 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1051 {
1052 // Double click with ALT pressed makes it blockwise.
1053 if (mod_mask & MOD_MASK_ALT)
1054 VIsual_mode = Ctrl_V;
1055 else
1056 VIsual_mode = 'v';
1057 }
1058 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
1059 VIsual_mode = 'V';
1060 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
1061 VIsual_mode = Ctrl_V;
1062#ifdef FEAT_CLIPBOARD
1063 // Make sure the clipboard gets updated. Needed because start and
1064 // end may still be the same, and the selection needs to be owned
1065 clip_star.vmode = NUL;
1066#endif
1067 }
1068 // A double click selects a word or a block.
1069 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1070 {
1071 pos_T *pos = NULL;
1072 int gc;
1073
1074 if (is_click)
1075 {
1076 // If the character under the cursor (skipping white space) is
1077 // not a word character, try finding a match and select a (),
1078 // {}, [], #if/#endif, etc. block.
1079 end_visual = curwin->w_cursor;
1080 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
1081 inc(&end_visual);
1082 if (oap != NULL)
1083 oap->motion_type = MCHAR;
1084 if (oap != NULL
1085 && VIsual_mode == 'v'
1086 && !vim_iswordc(gchar_pos(&end_visual))
1087 && EQUAL_POS(curwin->w_cursor, VIsual)
1088 && (pos = findmatch(oap, NUL)) != NULL)
1089 {
1090 curwin->w_cursor = *pos;
1091 if (oap->motion_type == MLINE)
1092 VIsual_mode = 'V';
1093 else if (*p_sel == 'e')
1094 {
1095 if (LT_POS(curwin->w_cursor, VIsual))
1096 ++VIsual.col;
1097 else
1098 ++curwin->w_cursor.col;
1099 }
1100 }
1101 }
1102
1103 if (pos == NULL && (is_click || is_drag))
1104 {
1105 // When not found a match or when dragging: extend to include
1106 // a word.
1107 if (LT_POS(curwin->w_cursor, orig_cursor))
1108 {
1109 find_start_of_word(&curwin->w_cursor);
1110 find_end_of_word(&VIsual);
1111 }
1112 else
1113 {
1114 find_start_of_word(&VIsual);
1115 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1116 curwin->w_cursor.col +=
1117 (*mb_ptr2len)(ml_get_cursor());
1118 find_end_of_word(&curwin->w_cursor);
1119 }
1120 }
1121 curwin->w_set_curswant = TRUE;
1122 }
1123 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001124 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001125 }
1126 else if (VIsual_active && !old_active)
1127 {
1128 if (mod_mask & MOD_MASK_ALT)
1129 VIsual_mode = Ctrl_V;
1130 else
1131 VIsual_mode = 'v';
1132 }
1133
1134 // If Visual mode changed show it later.
1135 if ((!VIsual_active && old_active && mode_displayed)
1136 || (VIsual_active && p_smd && msg_silent == 0
1137 && (!old_active || VIsual_mode != old_mode)))
1138 redraw_cmdline = TRUE;
1139
1140 return moved;
1141}
1142
1143 void
1144ins_mouse(int c)
1145{
1146 pos_T tpos;
1147 win_T *old_curwin = curwin;
1148
Christopher Plewright696d0a82022-11-18 17:53:34 +00001149#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001150 // When GUI is active, also move/paste when 'mouse' is empty
1151 if (!gui.in_use)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001152#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001153 if (!mouse_has(MOUSE_INSERT))
1154 return;
1155
1156 undisplay_dollar();
1157 tpos = curwin->w_cursor;
1158 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1159 {
1160 win_T *new_curwin = curwin;
1161
1162 if (curwin != old_curwin && win_valid(old_curwin))
1163 {
1164 // Mouse took us to another window. We need to go back to the
1165 // previous one to stop insert there properly.
1166 curwin = old_curwin;
1167 curbuf = curwin->w_buffer;
1168#ifdef FEAT_JOB_CHANNEL
1169 if (bt_prompt(curbuf))
1170 // Restart Insert mode when re-entering the prompt buffer.
1171 curbuf->b_prompt_insert = 'A';
1172#endif
1173 }
1174 start_arrow(curwin == old_curwin ? &tpos : NULL);
1175 if (curwin != new_curwin && win_valid(new_curwin))
1176 {
1177 curwin = new_curwin;
1178 curbuf = curwin->w_buffer;
1179 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001180 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001181 }
1182
1183 // redraw status lines (in case another window became active)
1184 redraw_statuslines();
1185}
1186
Christopher Plewright44c22092022-11-15 17:43:36 +00001187/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001188 * Common mouse wheel scrolling, shared between Insert mode and NV modes.
1189 * Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
1190 * depending on the scroll direction) or one page when Shift or Ctrl is used.
1191 * Direction is indicated by "cap->arg":
1192 * K_MOUSEUP - MSCR_UP
1193 * K_MOUSEDOWN - MSCR_DOWN
1194 * K_MOUSELEFT - MSCR_LEFT
1195 * K_MOUSERIGHT - MSCR_RIGHT
1196 * "curwin" may have been changed to the window that should be scrolled and
1197 * differ from the window that actually has focus.
1198 */
1199 static void
1200do_mousescroll(cmdarg_T *cap)
1201{
1202 int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
1203
1204#ifdef FEAT_TERMINAL
1205 if (term_use_loop())
1206 // This window is a terminal window, send the mouse event there.
1207 // Set "typed" to FALSE to avoid an endless loop.
1208 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
1209 else
1210#endif
1211 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1212 {
1213 // Vertical scrolling
1214 if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
1215 {
1216 // whole page up or down
Luuk van Baal5a2e3ec2024-03-28 10:07:29 +01001217 pagescroll(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L, FALSE);
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001218 }
1219 else
1220 {
1221 if (mouse_vert_step < 0 || shift_or_ctrl)
1222 {
1223 // whole page up or down
1224 cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
1225 }
1226 // Don't scroll more than half the window height.
1227 else if (curwin->w_height < mouse_vert_step * 2)
1228 {
1229 cap->count1 = curwin->w_height / 2;
1230 if (cap->count1 == 0)
1231 cap->count1 = 1;
1232 }
1233 else
1234 {
1235 cap->count1 = mouse_vert_step;
1236 }
1237 cap->count0 = cap->count1;
1238 nv_scroll_line(cap);
1239 }
1240
1241#ifdef FEAT_PROP_POPUP
1242 if (WIN_IS_POPUP(curwin))
1243 popup_set_firstline(curwin);
1244#endif
1245 }
1246 else
1247 {
1248 // Horizontal scrolling
1249 long step = (mouse_hor_step < 0 || shift_or_ctrl)
1250 ? curwin->w_width : mouse_hor_step;
1251 long leftcol = curwin->w_leftcol
1252 + (cap->arg == MSCR_RIGHT ? -step : step);
1253 if (leftcol < 0)
1254 leftcol = 0;
1255 do_mousescroll_horiz((long_u)leftcol);
1256 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001257 may_trigger_win_scrolled_resized();
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001258}
1259
1260/*
1261 * Insert mode implementation for scrolling in direction "dir", which is
1262 * one of the MSCR_ values.
Christopher Plewright44c22092022-11-15 17:43:36 +00001263 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001264 void
1265ins_mousescroll(int dir)
1266{
Christopher Plewright696d0a82022-11-18 17:53:34 +00001267 cmdarg_T cap;
1268 oparg_T oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001269 CLEAR_FIELD(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00001270 clear_oparg(&oa);
1271 cap.oap = &oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001272 cap.arg = dir;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001273
Christopher Plewright44c22092022-11-15 17:43:36 +00001274 switch (dir)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001275 {
Christopher Plewright44c22092022-11-15 17:43:36 +00001276 case MSCR_UP:
1277 cap.cmdchar = K_MOUSEUP;
1278 break;
1279 case MSCR_DOWN:
1280 cap.cmdchar = K_MOUSEDOWN;
1281 break;
1282 case MSCR_LEFT:
1283 cap.cmdchar = K_MOUSELEFT;
1284 break;
1285 case MSCR_RIGHT:
1286 cap.cmdchar = K_MOUSERIGHT;
1287 break;
1288 default:
1289 siemsg("Invalid ins_mousescroll() argument: %d", dir);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001290 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00001291
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001292 win_T *old_curwin = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001293 if (mouse_row >= 0 && mouse_col >= 0)
1294 {
1295 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001296 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00001297 int row = mouse_row;
1298 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001299 curwin = mouse_find_win(&row, &col, FIND_POPUP);
1300 if (curwin == NULL)
1301 {
1302 curwin = old_curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001303 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001304 }
1305 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001306 }
1307
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001308 if (curwin == old_curwin)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001309 {
1310 // Don't scroll the current window if the popup menu is visible.
1311 if (pum_visible())
1312 return;
1313
1314 undisplay_dollar();
1315 }
1316
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001317 linenr_T orig_topline = curwin->w_topline;
1318 colnr_T orig_leftcol = curwin->w_leftcol;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001319 pos_T orig_cursor = curwin->w_cursor;
1320
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001321 // Call the common mouse scroll function shared with other modes.
1322 do_mousescroll(&cap);
1323
1324 int did_scroll = (orig_topline != curwin->w_topline
1325 || orig_leftcol != curwin->w_leftcol);
1326
1327 curwin->w_redr_status = TRUE;
1328 curwin = old_curwin;
1329 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001330
1331 // If the window actually scrolled and the popup menu may overlay the
1332 // window, need to redraw it.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001333 if (did_scroll && pum_visible())
Christopher Plewright696d0a82022-11-18 17:53:34 +00001334 {
1335 // TODO: Would be more efficient to only redraw the windows that are
1336 // overlapped by the popup menu.
1337 redraw_all_later(UPD_NOT_VALID);
1338 ins_compl_show_pum();
1339 }
1340
1341 if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
1342 {
1343 start_arrow(&orig_cursor);
1344 set_can_cindent(TRUE);
1345 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001346}
1347
1348/*
1349 * Return TRUE if "c" is a mouse key.
1350 */
1351 int
1352is_mouse_key(int c)
1353{
1354 return c == K_LEFTMOUSE
1355 || c == K_LEFTMOUSE_NM
1356 || c == K_LEFTDRAG
1357 || c == K_LEFTRELEASE
1358 || c == K_LEFTRELEASE_NM
1359 || c == K_MOUSEMOVE
1360 || c == K_MIDDLEMOUSE
1361 || c == K_MIDDLEDRAG
1362 || c == K_MIDDLERELEASE
1363 || c == K_RIGHTMOUSE
1364 || c == K_RIGHTDRAG
1365 || c == K_RIGHTRELEASE
1366 || c == K_MOUSEDOWN
1367 || c == K_MOUSEUP
1368 || c == K_MOUSELEFT
1369 || c == K_MOUSERIGHT
1370 || c == K_X1MOUSE
1371 || c == K_X1DRAG
1372 || c == K_X1RELEASE
1373 || c == K_X2MOUSE
1374 || c == K_X2DRAG
1375 || c == K_X2RELEASE;
1376}
1377
1378static struct mousetable
1379{
1380 int pseudo_code; // Code for pseudo mouse event
1381 int button; // Which mouse button is it?
1382 int is_click; // Is it a mouse button click event?
1383 int is_drag; // Is it a mouse drag event?
1384} mouse_table[] =
1385{
1386 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1387#ifdef FEAT_GUI
1388 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1389#endif
1390 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1391 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1392#ifdef FEAT_GUI
1393 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1394#endif
1395 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1396 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1397 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1398 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1399 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1400 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1401 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1402 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1403 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1404 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1405 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1406 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1407 // DRAG without CLICK
1408 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1409 // RELEASE without CLICK
1410 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1411 {0, 0, 0, 0},
1412};
1413
1414/*
1415 * Look up the given mouse code to return the relevant information in the other
1416 * arguments. Return which button is down or was released.
1417 */
1418 int
1419get_mouse_button(int code, int *is_click, int *is_drag)
1420{
1421 int i;
1422
1423 for (i = 0; mouse_table[i].pseudo_code; i++)
1424 if (code == mouse_table[i].pseudo_code)
1425 {
1426 *is_click = mouse_table[i].is_click;
1427 *is_drag = mouse_table[i].is_drag;
1428 return mouse_table[i].button;
1429 }
1430 return 0; // Shouldn't get here
1431}
1432
1433/*
1434 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1435 * the given information about which mouse button is down, and whether the
1436 * mouse was clicked, dragged or released.
1437 */
1438 int
1439get_pseudo_mouse_code(
1440 int button, // eg MOUSE_LEFT
1441 int is_click,
1442 int is_drag)
1443{
1444 int i;
1445
1446 for (i = 0; mouse_table[i].pseudo_code; i++)
1447 if (button == mouse_table[i].button
1448 && is_click == mouse_table[i].is_click
1449 && is_drag == mouse_table[i].is_drag)
1450 {
1451#ifdef FEAT_GUI
1452 // Trick: a non mappable left click and release has mouse_col -1
1453 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1454 // gui_mouse_moved()
1455 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1456 {
1457 if (mouse_col < 0)
1458 mouse_col = 0;
1459 else
1460 mouse_col -= MOUSE_COLOFF;
1461 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1462 return (int)KE_LEFTMOUSE_NM;
1463 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1464 return (int)KE_LEFTRELEASE_NM;
1465 }
1466#endif
1467 return mouse_table[i].pseudo_code;
1468 }
1469 return (int)KE_IGNORE; // not recognized, ignore it
1470}
1471
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001472# define HMT_NORMAL 1
1473# define HMT_NETTERM 2
1474# define HMT_DEC 4
1475# define HMT_JSBTERM 8
1476# define HMT_PTERM 16
1477# define HMT_URXVT 32
1478# define HMT_GPM 64
1479# define HMT_SGR 128
1480# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001481static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001482
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001483 void
1484set_mouse_termcode(
1485 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1486 char_u *s)
1487{
1488 char_u name[2];
1489
1490 name[0] = n;
1491 name[1] = KE_FILLER;
1492 add_termcode(name, s, FALSE);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001493#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001494 if (n == KS_JSBTERM_MOUSE)
1495 has_mouse_termcode |= HMT_JSBTERM;
1496 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001497#endif
1498#ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001499 if (n == KS_NETTERM_MOUSE)
1500 has_mouse_termcode |= HMT_NETTERM;
1501 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001502#endif
1503#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001504 if (n == KS_DEC_MOUSE)
1505 has_mouse_termcode |= HMT_DEC;
1506 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001507#endif
1508#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001509 if (n == KS_PTERM_MOUSE)
1510 has_mouse_termcode |= HMT_PTERM;
1511 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001512#endif
1513#ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001514 if (n == KS_URXVT_MOUSE)
1515 has_mouse_termcode |= HMT_URXVT;
1516 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001517#endif
1518#ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001519 if (n == KS_GPM_MOUSE)
1520 has_mouse_termcode |= HMT_GPM;
1521 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001522#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001523 if (n == KS_SGR_MOUSE)
1524 has_mouse_termcode |= HMT_SGR;
1525 else if (n == KS_SGR_MOUSE_RELEASE)
1526 has_mouse_termcode |= HMT_SGR_REL;
1527 else
1528 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001529}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001530
Christopher Plewright696d0a82022-11-18 17:53:34 +00001531#if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001532 void
1533del_mouse_termcode(
1534 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1535{
1536 char_u name[2];
1537
1538 name[0] = n;
1539 name[1] = KE_FILLER;
1540 del_termcode(name);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001541# ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001542 if (n == KS_JSBTERM_MOUSE)
1543 has_mouse_termcode &= ~HMT_JSBTERM;
1544 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001545# endif
1546# ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001547 if (n == KS_NETTERM_MOUSE)
1548 has_mouse_termcode &= ~HMT_NETTERM;
1549 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001550# endif
1551# ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001552 if (n == KS_DEC_MOUSE)
1553 has_mouse_termcode &= ~HMT_DEC;
1554 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001555# endif
1556# ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001557 if (n == KS_PTERM_MOUSE)
1558 has_mouse_termcode &= ~HMT_PTERM;
1559 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001560# endif
1561# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001562 if (n == KS_URXVT_MOUSE)
1563 has_mouse_termcode &= ~HMT_URXVT;
1564 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001565# endif
1566# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001567 if (n == KS_GPM_MOUSE)
1568 has_mouse_termcode &= ~HMT_GPM;
1569 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001570# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001571 if (n == KS_SGR_MOUSE)
1572 has_mouse_termcode &= ~HMT_SGR;
1573 else if (n == KS_SGR_MOUSE_RELEASE)
1574 has_mouse_termcode &= ~HMT_SGR_REL;
1575 else
1576 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001577}
Christopher Plewright696d0a82022-11-18 17:53:34 +00001578#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001579
1580/*
1581 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1582 */
1583 void
1584setmouse(void)
1585{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001586 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001587
Christopher Plewright696d0a82022-11-18 17:53:34 +00001588#ifdef FEAT_MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001589 update_mouseshape(-1);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001590#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001591
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001592 // Should be outside proc, but may break MOUSESHAPE
Christopher Plewright696d0a82022-11-18 17:53:34 +00001593#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001594 // In the GUI the mouse is always enabled.
1595 if (gui.in_use)
1596 return;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001597#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001598 // be quick when mouse is off
1599 if (*p_mouse == NUL || has_mouse_termcode == 0)
1600 return;
1601
1602 // don't switch mouse on when not in raw mode (Ex mode)
1603 if (cur_tmode != TMODE_RAW)
1604 {
1605 mch_setmouse(FALSE);
1606 return;
1607 }
1608
1609 if (VIsual_active)
1610 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001611 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1612 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001613 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001614 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001615 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001616 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001617 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001618 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001619 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1620 else
1621 checkfor = MOUSE_NORMAL; // assume normal mode
1622
1623 if (mouse_has(checkfor))
1624 mch_setmouse(TRUE);
1625 else
1626 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001627}
1628
1629/*
1630 * Return TRUE if
1631 * - "c" is in 'mouse', or
1632 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1633 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1634 * normal editing mode (not at hit-return message).
1635 */
1636 int
1637mouse_has(int c)
1638{
1639 char_u *p;
1640
1641 for (p = p_mouse; *p; ++p)
1642 switch (*p)
1643 {
1644 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1645 return TRUE;
1646 break;
1647 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1648 return TRUE;
1649 break;
1650 default: if (c == *p) return TRUE; break;
1651 }
1652 return FALSE;
1653}
1654
1655/*
1656 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1657 */
1658 int
1659mouse_model_popup(void)
1660{
1661 return (p_mousem[0] == 'p');
1662}
1663
zeertzjq8e0ccb62022-11-01 18:35:27 +00001664static win_T *dragwin = NULL; // window being dragged
1665
1666/*
1667 * Reset the window being dragged. To be called when switching tab page.
1668 */
1669 void
1670reset_dragwin(void)
1671{
1672 dragwin = NULL;
1673}
1674
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001675/*
1676 * Move the cursor to the specified row and column on the screen.
1677 * Change current window if necessary. Returns an integer with the
1678 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1679 *
1680 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1681 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1682 *
1683 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1684 * if the mouse is outside the window then the text will scroll, or if the
1685 * mouse was previously on a status line, then the status line may be dragged.
1686 *
1687 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1688 * cursor is moved unless the cursor was on a status line.
1689 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1690 * IN_SEP_LINE depending on where the cursor was clicked.
1691 *
1692 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1693 * the mouse is on the status line of the same window.
1694 *
1695 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1696 * the last call.
1697 *
1698 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1699 * remembered.
1700 */
1701 int
1702jump_to_mouse(
1703 int flags,
1704 int *inclusive, // used for inclusive operator, can be NULL
1705 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1706{
1707 static int on_status_line = 0; // #lines below bottom of window
1708 static int on_sep_line = 0; // on separator right of window
1709#ifdef FEAT_MENU
1710 static int in_winbar = FALSE;
1711#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001712#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001713 static int in_popup_win = FALSE;
1714 static win_T *click_in_popup_win = NULL;
1715#endif
1716 static int prev_row = -1;
1717 static int prev_col = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001718 static int did_drag = FALSE; // drag was noticed
1719
1720 win_T *wp, *old_curwin;
1721 pos_T old_cursor;
1722 int count;
1723 int first;
1724 int row = mouse_row;
1725 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001726 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001727#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001728 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001729#endif
1730
1731 mouse_past_bottom = FALSE;
1732 mouse_past_eol = FALSE;
1733
1734 if (flags & MOUSE_RELEASED)
1735 {
1736 // On button release we may change window focus if positioned on a
1737 // status line and no dragging happened.
1738 if (dragwin != NULL && !did_drag)
1739 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1740 dragwin = NULL;
1741 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001742#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001743 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1744 popup_close_for_mouse_click(click_in_popup_win);
1745
1746 popup_dragwin = NULL;
1747 click_in_popup_win = NULL;
1748#endif
1749 }
1750
1751 if ((flags & MOUSE_DID_MOVE)
1752 && prev_row == mouse_row
1753 && prev_col == mouse_col)
1754 {
1755retnomove:
1756 // before moving the cursor for a left click which is NOT in a status
1757 // line, stop Visual mode
1758 if (on_status_line)
1759 return IN_STATUS_LINE;
1760 if (on_sep_line)
1761 return IN_SEP_LINE;
1762#ifdef FEAT_MENU
1763 if (in_winbar)
1764 {
1765 // A quick second click may arrive as a double-click, but we use it
1766 // as a second click in the WinBar.
1767 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1768 {
1769 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1770 if (wp == NULL)
1771 return IN_UNKNOWN;
1772 winbar_click(wp, col);
1773 }
1774 return IN_OTHER_WIN | MOUSE_WINBAR;
1775 }
1776#endif
1777 if (flags & MOUSE_MAY_STOP_VIS)
1778 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001779 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001780 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001781 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001782#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001783 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01001784 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001785 return IN_OTHER_WIN;
1786#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001787#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001788 // Continue a modeless selection in a popup window or dragging it.
1789 if (in_popup_win)
1790 {
1791 click_in_popup_win = NULL; // don't close it on release
1792 if (popup_dragwin != NULL)
1793 {
1794 // dragging a popup window
1795 popup_drag(popup_dragwin);
1796 return IN_UNKNOWN;
1797 }
1798 return IN_OTHER_WIN;
1799 }
1800#endif
1801 return IN_BUFFER;
1802 }
1803
1804 prev_row = mouse_row;
1805 prev_col = mouse_col;
1806
1807 if (flags & MOUSE_SETPOS)
1808 goto retnomove; // ugly goto...
1809
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001810 old_curwin = curwin;
1811 old_cursor = curwin->w_cursor;
1812
1813 if (!(flags & MOUSE_FOCUS))
1814 {
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02001815 if (row < 0 || col < 0) // check if it makes sense
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001816 return IN_UNKNOWN;
1817
1818 // find the window where the row is in and adjust "row" and "col" to be
1819 // relative to top-left of the window
1820 wp = mouse_find_win(&row, &col, FIND_POPUP);
1821 if (wp == NULL)
1822 return IN_UNKNOWN;
1823 dragwin = NULL;
1824
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001825#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001826 // Click in a popup window may start dragging or modeless selection,
1827 // but not much else.
1828 if (WIN_IS_POPUP(wp))
1829 {
1830 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001831 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001832 in_popup_win = TRUE;
1833 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1834 {
1835 return IN_UNKNOWN;
1836 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001837 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001838 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001839 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001840 {
1841 popup_dragwin = wp;
1842 popup_start_drag(wp, row, col);
1843 return IN_UNKNOWN;
1844 }
1845 // Only close on release, otherwise it's not possible to drag or do
1846 // modeless selection.
1847 else if (wp->w_popup_close == POPCLOSE_CLICK
1848 && which_button == MOUSE_LEFT)
1849 {
1850 click_in_popup_win = wp;
1851 }
1852 else if (which_button == MOUSE_LEFT)
1853 // If the click is in the scrollbar, may scroll up/down.
1854 popup_handle_scrollbar_click(wp, row, col);
1855# ifdef FEAT_CLIPBOARD
1856 return IN_OTHER_WIN;
1857# else
1858 return IN_UNKNOWN;
1859# endif
1860 }
1861 in_popup_win = FALSE;
1862 popup_dragwin = NULL;
1863#endif
1864#ifdef FEAT_MENU
1865 if (row == -1)
1866 {
1867 // A click in the window toolbar does not enter another window or
1868 // change Visual highlighting.
1869 winbar_click(wp, col);
1870 in_winbar = TRUE;
1871 return IN_OTHER_WIN | MOUSE_WINBAR;
1872 }
1873 in_winbar = FALSE;
1874#endif
1875
1876 // winpos and height may change in win_enter()!
1877 if (row >= wp->w_height) // In (or below) status line
1878 {
1879 on_status_line = row - wp->w_height + 1;
1880 dragwin = wp;
1881 }
1882 else
1883 on_status_line = 0;
1884 if (col >= wp->w_width) // In separator line
1885 {
1886 on_sep_line = col - wp->w_width + 1;
1887 dragwin = wp;
1888 }
1889 else
1890 on_sep_line = 0;
1891
1892 // The rightmost character of the status line might be a vertical
1893 // separator character if there is no connecting window to the right.
1894 if (on_status_line && on_sep_line)
1895 {
1896 if (stl_connected(wp))
1897 on_sep_line = 0;
1898 else
1899 on_status_line = 0;
1900 }
1901
1902 // Before jumping to another buffer, or moving the cursor for a left
1903 // click, stop Visual mode.
1904 if (VIsual_active
1905 && (wp->w_buffer != curwin->w_buffer
1906 || (!on_status_line && !on_sep_line
1907#ifdef FEAT_FOLDING
1908 && (
1909# ifdef FEAT_RIGHTLEFT
1910 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1911# endif
Sean Dewar988f7432023-08-16 14:17:36 +01001912 col >= wp->w_p_fdc + (wp != cmdwin_win ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001913 )
1914#endif
1915 && (flags & MOUSE_MAY_STOP_VIS))))
1916 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001917 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001918 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001919 }
Sean Dewar988f7432023-08-16 14:17:36 +01001920 if (cmdwin_type != 0 && wp != cmdwin_win)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001921 {
1922 // A click outside the command-line window: Use modeless
1923 // selection if possible. Allow dragging the status lines.
1924 on_sep_line = 0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001925#ifdef FEAT_CLIPBOARD
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001926 if (on_status_line)
1927 return IN_STATUS_LINE;
1928 return IN_OTHER_WIN;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001929#else
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001930 row = 0;
1931 col += wp->w_wincol;
Sean Dewar988f7432023-08-16 14:17:36 +01001932 wp = cmdwin_win;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001933#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001934 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001935#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1936 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1937 // terminal in popup window: don't jump to another window
1938 return IN_OTHER_WIN;
1939#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001940 // Only change window focus when not clicking on or dragging the
1941 // status line. Do change focus when releasing the mouse button
1942 // (MOUSE_FOCUS was set above if we dragged first).
1943 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1944 win_enter(wp, TRUE); // can make wp invalid!
1945
1946 if (curwin != old_curwin)
1947 {
1948#ifdef CHECK_DOUBLE_CLICK
1949 // set topline, to be able to check for double click ourselves
1950 set_mouse_topline(curwin);
1951#endif
1952#ifdef FEAT_TERMINAL
1953 // when entering a terminal window may change state
1954 term_win_entered();
1955#endif
1956 }
1957 if (on_status_line) // In (or below) status line
1958 {
1959 // Don't use start_arrow() if we're in the same window
1960 if (curwin == old_curwin)
1961 return IN_STATUS_LINE;
1962 else
1963 return IN_STATUS_LINE | CURSOR_MOVED;
1964 }
1965 if (on_sep_line) // In (or below) status line
1966 {
1967 // Don't use start_arrow() if we're in the same window
1968 if (curwin == old_curwin)
1969 return IN_SEP_LINE;
1970 else
1971 return IN_SEP_LINE | CURSOR_MOVED;
1972 }
1973
1974 curwin->w_cursor.lnum = curwin->w_topline;
1975#ifdef FEAT_GUI
1976 // remember topline, needed for double click
1977 gui_prev_topline = curwin->w_topline;
1978# ifdef FEAT_DIFF
1979 gui_prev_topfill = curwin->w_topfill;
1980# endif
1981#endif
1982 }
1983 else if (on_status_line && which_button == MOUSE_LEFT)
1984 {
1985 if (dragwin != NULL)
1986 {
1987 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001988 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001989 - on_status_line;
1990 win_drag_status_line(dragwin, count);
1991 did_drag |= count;
1992 }
1993 return IN_STATUS_LINE; // Cursor didn't move
1994 }
1995 else if (on_sep_line && which_button == MOUSE_LEFT)
1996 {
1997 if (dragwin != NULL)
1998 {
1999 // Drag the separator column
2000 count = col - dragwin->w_wincol - dragwin->w_width + 1
2001 - on_sep_line;
2002 win_drag_vsep_line(dragwin, count);
2003 did_drag |= count;
2004 }
2005 return IN_SEP_LINE; // Cursor didn't move
2006 }
2007#ifdef FEAT_MENU
2008 else if (in_winbar)
2009 {
2010 // After a click on the window toolbar don't start Visual mode.
2011 return IN_OTHER_WIN | MOUSE_WINBAR;
2012 }
2013#endif
2014 else // keep_window_focus must be TRUE
2015 {
2016 // before moving the cursor for a left click, stop Visual mode
2017 if (flags & MOUSE_MAY_STOP_VIS)
2018 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02002019 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002020 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002021 }
2022
Martin Tournoij7904fa42022-10-04 16:28:45 +01002023#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002024 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01002025 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002026 return IN_OTHER_WIN;
2027#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002028#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002029 if (in_popup_win)
2030 {
2031 if (popup_dragwin != NULL)
2032 {
2033 // dragging a popup window
2034 popup_drag(popup_dragwin);
2035 return IN_UNKNOWN;
2036 }
2037 // continue a modeless selection in a popup window
2038 click_in_popup_win = NULL;
2039 return IN_OTHER_WIN;
2040 }
2041#endif
2042
2043 row -= W_WINROW(curwin);
2044 col -= curwin->w_wincol;
2045
2046 // When clicking beyond the end of the window, scroll the screen.
2047 // Scroll by however many rows outside the window we are.
2048 if (row < 0)
2049 {
2050 count = 0;
2051 for (first = TRUE; curwin->w_topline > 1; )
2052 {
2053#ifdef FEAT_DIFF
2054 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2055 ++count;
2056 else
2057#endif
2058 count += plines(curwin->w_topline - 1);
2059 if (!first && count > -row)
2060 break;
2061 first = FALSE;
2062#ifdef FEAT_FOLDING
2063 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2064#endif
2065#ifdef FEAT_DIFF
2066 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2067 ++curwin->w_topfill;
2068 else
2069#endif
2070 {
2071 --curwin->w_topline;
2072#ifdef FEAT_DIFF
2073 curwin->w_topfill = 0;
2074#endif
2075 }
2076 }
2077#ifdef FEAT_DIFF
2078 check_topfill(curwin, FALSE);
2079#endif
2080 curwin->w_valid &=
2081 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002082 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002083 row = 0;
2084 }
2085 else if (row >= curwin->w_height)
2086 {
2087 count = 0;
2088 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
2089 {
2090#ifdef FEAT_DIFF
2091 if (curwin->w_topfill > 0)
2092 ++count;
2093 else
2094#endif
2095 count += plines(curwin->w_topline);
2096 if (!first && count > row - curwin->w_height + 1)
2097 break;
2098 first = FALSE;
2099#ifdef FEAT_FOLDING
2100 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
2101 && curwin->w_topline == curbuf->b_ml.ml_line_count)
2102 break;
2103#endif
2104#ifdef FEAT_DIFF
2105 if (curwin->w_topfill > 0)
2106 --curwin->w_topfill;
2107 else
2108#endif
2109 {
2110 ++curwin->w_topline;
2111#ifdef FEAT_DIFF
2112 curwin->w_topfill =
2113 diff_check_fill(curwin, curwin->w_topline);
2114#endif
2115 }
2116 }
2117#ifdef FEAT_DIFF
2118 check_topfill(curwin, FALSE);
2119#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002120 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002121 curwin->w_valid &=
2122 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2123 row = curwin->w_height - 1;
2124 }
2125 else if (row == 0)
2126 {
2127 // When dragging the mouse, while the text has been scrolled up as
2128 // far as it goes, moving the mouse in the top line should scroll
2129 // the text down (done later when recomputing w_topline).
2130 if (mouse_dragging > 0
2131 && curwin->w_cursor.lnum
2132 == curwin->w_buffer->b_ml.ml_line_count
2133 && curwin->w_cursor.lnum == curwin->w_topline)
2134 curwin->w_valid &= ~(VALID_TOPLINE);
2135 }
2136 }
2137
zeertzjqec149242023-12-19 20:28:31 +01002138 if (prev_row >= W_WINROW(curwin)
2139 && prev_row < W_WINROW(curwin) + curwin->w_height
2140 && prev_col >= curwin->w_wincol && prev_col < W_ENDCOL(curwin)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002141 && ScreenLines != NULL)
2142 {
2143 int off = LineOffset[prev_row] + prev_col;
2144
2145 // Only use ScreenCols[] after the window was redrawn. Mainly matters
2146 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01002147 // Do not use when 'virtualedit' is active.
zeertzjqe500ae82023-08-17 22:35:26 +02002148 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002149 col_from_screen = ScreenCols[off];
2150#ifdef FEAT_FOLDING
2151 // Remember the character under the mouse, it might be a '-' or '+' in
2152 // the fold column.
2153 mouse_char = ScreenLines[off];
2154#endif
2155 }
2156
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002157#ifdef FEAT_FOLDING
2158 // Check for position outside of the fold column.
2159 if (
2160# ifdef FEAT_RIGHTLEFT
2161 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
2162# endif
Sean Dewar988f7432023-08-16 14:17:36 +01002163 col >= curwin->w_p_fdc + (cmdwin_win != curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002164 )
2165 mouse_char = ' ';
2166#endif
2167
2168 // compute the position in the buffer line from the posn on the screen
2169 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
2170 mouse_past_bottom = TRUE;
2171
2172 // Start Visual mode before coladvance(), for when 'sel' != "old"
2173 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2174 {
2175 check_visual_highlight();
2176 VIsual = old_cursor;
2177 VIsual_active = TRUE;
2178 VIsual_reselect = TRUE;
2179 // if 'selectmode' contains "mouse", start Select mode
2180 may_start_select('o');
2181 setmouse();
2182 if (p_smd && msg_silent == 0)
2183 redraw_cmdline = TRUE; // show visual mode later
2184 }
2185
zeertzjqd0c1b772024-03-16 15:03:33 +01002186 if (col_from_screen >= 0)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002187 {
zeertzjqe500ae82023-08-17 22:35:26 +02002188 // Use the virtual column from ScreenCols[], it is accurate also after
2189 // concealed characters.
2190 col = col_from_screen;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002191 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002192
zeertzjqe500ae82023-08-17 22:35:26 +02002193 curwin->w_curswant = col;
2194 curwin->w_set_curswant = FALSE; // May still have been TRUE
2195 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2196 {
2197 if (inclusive != NULL)
2198 *inclusive = TRUE;
2199 mouse_past_eol = TRUE;
2200 }
2201 else if (inclusive != NULL)
2202 *inclusive = FALSE;
2203
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002204 count = IN_BUFFER;
2205 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2206 || curwin->w_cursor.col != old_cursor.col)
2207 count |= CURSOR_MOVED; // Cursor has moved
2208
Christopher Plewright696d0a82022-11-18 17:53:34 +00002209#ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002210 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002211 count |= MOUSE_FOLD_OPEN;
2212 else if (mouse_char != ' ')
2213 count |= MOUSE_FOLD_CLOSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002214#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002215
2216 return count;
2217}
2218
2219/*
Christopher Plewright44c22092022-11-15 17:43:36 +00002220 * Make a horizontal scroll to "leftcol".
2221 * Return TRUE if the cursor moved, FALSE otherwise.
2222 */
2223 int
2224do_mousescroll_horiz(long_u leftcol)
2225{
2226 if (curwin->w_p_wrap)
Christian Brabandtee17b6f2023-09-09 11:23:50 +02002227 return FALSE; // no horizontal scrolling when wrapping
Christopher Plewright44c22092022-11-15 17:43:36 +00002228
2229 if (curwin->w_leftcol == (colnr_T)leftcol)
2230 return FALSE; // already there
2231
Christopher Plewright44c22092022-11-15 17:43:36 +00002232 // When the line of the cursor is too short, move the cursor to the
2233 // longest visible line.
2234 if (
2235#ifdef FEAT_GUI
2236 (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
2237#endif
2238 !virtual_active()
2239 && (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
2240 {
2241 curwin->w_cursor.lnum = ui_find_longest_lnum();
2242 curwin->w_cursor.col = 0;
2243 }
2244
Bram Moolenaar0c34d562022-11-18 14:07:20 +00002245 return set_leftcol((colnr_T)leftcol);
Christopher Plewright44c22092022-11-15 17:43:36 +00002246}
2247
2248/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002249 * Normal and Visual modes implementation for scrolling in direction
2250 * "cap->arg", which is one of the MSCR_ values.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002251 */
2252 void
Christopher Plewright696d0a82022-11-18 17:53:34 +00002253nv_mousescroll(cmdarg_T *cap)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002254{
Christopher Plewright696d0a82022-11-18 17:53:34 +00002255 win_T *old_curwin = curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002256
2257 if (mouse_row >= 0 && mouse_col >= 0)
2258 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002259 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002260 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00002261 int row = mouse_row;
2262 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002263 curwin = mouse_find_win(&row, &col, FIND_POPUP);
2264 if (curwin == NULL)
2265 {
2266 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002267 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002268 }
2269
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002270#ifdef FEAT_PROP_POPUP
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002271 if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
2272 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002273 // cannot scroll this popup window
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002274 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002275 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002276 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002277#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002278 curbuf = curwin->w_buffer;
2279 }
Christopher Plewright44c22092022-11-15 17:43:36 +00002280
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002281 // Call the common mouse scroll function shared with other modes.
2282 do_mousescroll(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00002283
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002284 curwin->w_redr_status = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002285 curwin = old_curwin;
2286 curbuf = curwin->w_buffer;
2287}
2288
2289/*
2290 * Mouse clicks and drags.
2291 */
2292 void
2293nv_mouse(cmdarg_T *cap)
2294{
2295 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2296}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002297
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002298static int held_button = MOUSE_RELEASE;
2299
2300 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00002301reset_held_button(void)
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002302{
2303 held_button = MOUSE_RELEASE;
2304}
2305
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002306/*
2307 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2308 * modifiers found in typebuf in 'modifiers'.
2309 */
2310 int
2311check_termcode_mouse(
2312 char_u *tp,
2313 int *slen,
2314 char_u *key_name,
2315 char_u *modifiers_start,
2316 int idx,
2317 int *modifiers)
2318{
2319 int j;
2320 char_u *p;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002321#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002322 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2323 char_u bytes[6];
2324 int num_bytes;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002325#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002326 int mouse_code = 0; // init for GCC
2327 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002328 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002329 int wheel_code = 0;
2330 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002331 static int orig_num_clicks = 1;
2332 static int orig_mouse_code = 0x0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002333#ifdef CHECK_DOUBLE_CLICK
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002334 static int orig_mouse_col = 0;
2335 static int orig_mouse_row = 0;
2336 static struct timeval orig_mouse_time = {0, 0};
2337 // time of previous mouse click
2338 struct timeval mouse_time; // time of current mouse click
2339 long timediff; // elapsed time in msec
Christopher Plewright696d0a82022-11-18 17:53:34 +00002340#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002341
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002342 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002343
Christopher Plewright696d0a82022-11-18 17:53:34 +00002344#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002345 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2346 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002347# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002348 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002349# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002350 )
2351 {
2352 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002353 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002354 * 0x20 = left button down
2355 * 0x21 = middle button down
2356 * 0x22 = right button down
2357 * 0x23 = any button release
2358 * 0x60 = button 4 down (scroll wheel down)
2359 * 0x61 = button 5 down (scroll wheel up)
2360 * add 0x04 for SHIFT
2361 * add 0x08 for ALT
2362 * add 0x10 for CTRL
2363 * add 0x20 for mouse drag (0x40 is drag with left button)
2364 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2365 * 0x43 (drag + release) is also move
2366 * c == column + ' ' + 1 == column + 33
2367 * r == row + ' ' + 1 == row + 33
2368 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002369 * The coordinates are passed on through global variables. Ugly, but
2370 * this avoids trouble with mouse clicks at an unexpected moment and
2371 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002372 */
2373 for (;;)
2374 {
Christopher Plewright36446bb2022-11-23 22:28:08 +00002375 // For the GUI and for MS-Windows two bytes each are used for row
2376 // and column. Allows for more than 223 columns.
2377# if defined(FEAT_GUI) || defined(MSWIN)
2378 if (TRUE
2379# if defined(FEAT_GUI) && !defined(MSWIN)
2380 && gui.in_use
2381# endif
2382 )
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002383 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002384 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2385 if (num_bytes == -1) // not enough coordinates
2386 return -1;
2387 mouse_code = bytes[0];
2388 mouse_col = 128 * (bytes[1] - ' ' - 1)
2389 + bytes[2] - ' ' - 1;
2390 mouse_row = 128 * (bytes[3] - ' ' - 1)
2391 + bytes[4] - ' ' - 1;
2392 }
2393 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002394# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002395 {
2396 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2397 if (num_bytes == -1) // not enough coordinates
2398 return -1;
2399 mouse_code = bytes[0];
2400 mouse_col = bytes[1] - ' ' - 1;
2401 mouse_row = bytes[2] - ' ' - 1;
2402 }
2403 *slen += num_bytes;
2404
Bram Moolenaar13c04632020-07-12 13:47:42 +02002405 // If the following bytes is also a mouse code and it has the same
2406 // code, dump this one and get the next. This makes dragging a
2407 // whole lot faster.
Christopher Plewright696d0a82022-11-18 17:53:34 +00002408# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002409 if (gui.in_use)
2410 j = 3;
2411 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002412# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002413 j = get_termcode_len(idx);
2414 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2415 && tp[*slen + j] == mouse_code
2416 && tp[*slen + j + 1] != NUL
2417 && tp[*slen + j + 2] != NUL
Christopher Plewright696d0a82022-11-18 17:53:34 +00002418# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002419 && (!gui.in_use
2420 || (tp[*slen + j + 3] != NUL
2421 && tp[*slen + j + 4] != NUL))
Christopher Plewright696d0a82022-11-18 17:53:34 +00002422# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002423 )
2424 *slen += j;
2425 else
2426 break;
2427 }
2428 }
2429
2430 if (key_name[0] == KS_URXVT_MOUSE
2431 || key_name[0] == KS_SGR_MOUSE
2432 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2433 {
2434 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002435 // Almost identical to xterm mouse mode, except the values are decimal
2436 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002437 //
2438 // \033[%d;%d;%dM
2439 // ^-- row
2440 // ^----- column
2441 // ^-------- code
2442 //
2443 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002444 // Almost identical to xterm mouse mode, except the values are decimal
2445 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002446 //
2447 // \033[<%d;%d;%dM
2448 // ^-- row
2449 // ^----- column
2450 // ^-------- code
2451 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002452 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002453 // ^-- row
2454 // ^----- column
2455 // ^-------- code
2456 p = modifiers_start;
2457 if (p == NULL)
2458 return -1;
2459
2460 mouse_code = getdigits(&p);
2461 if (*p++ != ';')
2462 return -1;
2463
2464 // when mouse reporting is SGR, add 32 to mouse code
2465 if (key_name[0] == KS_SGR_MOUSE
2466 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2467 mouse_code += 32;
2468
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002469 mouse_col = getdigits(&p) - 1;
2470 if (*p++ != ';')
2471 return -1;
2472
2473 mouse_row = getdigits(&p) - 1;
2474
Bram Moolenaar13c04632020-07-12 13:47:42 +02002475 // The modifiers were the mouse coordinates, not the modifier keys
2476 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002477 *modifiers = 0;
2478 }
2479
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002480 if (key_name[0] == KS_SGR_MOUSE
2481 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2482 {
2483 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002484 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002485 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002486 // This is used below to set held_button.
2487 mouse_code |= MOUSE_RELEASE;
2488 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002489 }
2490 else
2491 {
2492 release_is_ambiguous = TRUE;
2493 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2494 is_release = TRUE;
2495 }
2496
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002497 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002498# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002499 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002500# endif
2501# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002502 || key_name[0] == KS_URXVT_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002503# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002504 || key_name[0] == KS_SGR_MOUSE
2505 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2506 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002507# if !defined(MSWIN)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002508 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002509 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002510 * Recognize the xterm mouse wheel, but not in the GUI, the
2511 * Linux console with GPM and the MS-DOS or Win32 console
2512 * (multi-clicks use >= 0x60).
2513 */
2514 if (mouse_code >= MOUSEWHEEL_LOW
Christopher Plewright696d0a82022-11-18 17:53:34 +00002515# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002516 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002517# endif
2518# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002519 && key_name[0] != KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002520# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002521 )
2522 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002523# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002524 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2525 // mouse-move event, using MOUSE_DRAG works
2526 mouse_code = MOUSE_DRAG;
2527 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002528# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002529 // Keep the mouse_code before it's changed, so that we
2530 // remember that it was a mouse wheel click.
2531 wheel_code = mouse_code;
2532 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002533# ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002534 else if (held_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002535# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002536 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002537# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002538 && (mouse_code == 0x23 || mouse_code == 0x24
2539 || mouse_code == 0x40 || mouse_code == 0x41))
2540 {
2541 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2542 // And 0x40 and 0x41 are used by some xterm emulator.
2543 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002544 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002545 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002546# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002547
Christopher Plewright696d0a82022-11-18 17:53:34 +00002548# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002549 else if (use_xterm_mouse() > 1)
2550 {
2551 if (mouse_code & MOUSE_DRAG_XTERM)
2552 mouse_code |= MOUSE_DRAG;
2553 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002554# endif
2555# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002556 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2557 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002558 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002559 stop_xterm_trace();
2560 else
2561 start_xterm_trace(mouse_code);
2562 }
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002563# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002564# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002565 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002566#endif // !UNIX || FEAT_MOUSE_XTERM
2567#ifdef FEAT_MOUSE_NET
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002568 if (key_name[0] == KS_NETTERM_MOUSE)
2569 {
2570 int mc, mr;
2571
2572 // expect a rather limited sequence like: balancing {
2573 // \033}6,45\r
2574 // '6' is the row, 45 is the column
2575 p = tp + *slen;
2576 mr = getdigits(&p);
2577 if (*p++ != ',')
2578 return -1;
2579 mc = getdigits(&p);
2580 if (*p++ != '\r')
2581 return -1;
2582
2583 mouse_col = mc - 1;
2584 mouse_row = mr - 1;
2585 mouse_code = MOUSE_LEFT;
2586 *slen += (int)(p - (tp + *slen));
2587 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002588#endif // FEAT_MOUSE_NET
2589#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002590 if (key_name[0] == KS_JSBTERM_MOUSE)
2591 {
2592 int mult, val, iter, button, status;
2593
2594 /*
2595 * JSBTERM Input Model
2596 * \033[0~zw uniq escape sequence
2597 * (L-x) Left button pressed - not pressed x not reporting
2598 * (M-x) Middle button pressed - not pressed x not reporting
2599 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002600 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2601 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002602 * ### X cursor position padded to 3 digits
2603 * ### Y cursor position padded to 3 digits
2604 * (s-x) SHIFT key pressed - not pressed x not reporting
2605 * (c-x) CTRL key pressed - not pressed x not reporting
2606 * \033\\ terminating sequence
2607 */
2608 p = tp + *slen;
2609 button = mouse_code = 0;
2610 switch (*p++)
2611 {
2612 case 'L': button = 1; break;
2613 case '-': break;
2614 case 'x': break; // ignore sequence
2615 default: return -1; // Unknown Result
2616 }
2617 switch (*p++)
2618 {
2619 case 'M': button |= 2; break;
2620 case '-': break;
2621 case 'x': break; // ignore sequence
2622 default: return -1; // Unknown Result
2623 }
2624 switch (*p++)
2625 {
2626 case 'R': button |= 4; break;
2627 case '-': break;
2628 case 'x': break; // ignore sequence
2629 default: return -1; // Unknown Result
2630 }
2631 status = *p++;
2632 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2633 mult /= 10, p++)
2634 if (*p >= '0' && *p <= '9')
2635 val += (*p - '0') * mult;
2636 else
2637 return -1;
2638 mouse_col = val;
2639 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2640 mult /= 10, p++)
2641 if (*p >= '0' && *p <= '9')
2642 val += (*p - '0') * mult;
2643 else
2644 return -1;
2645 mouse_row = val;
2646 switch (*p++)
2647 {
2648 case 's': button |= 8; break; // SHIFT key Pressed
2649 case '-': break; // Not Pressed
2650 case 'x': break; // Not Reporting
2651 default: return -1; // Unknown Result
2652 }
2653 switch (*p++)
2654 {
2655 case 'c': button |= 16; break; // CTRL key Pressed
2656 case '-': break; // Not Pressed
2657 case 'x': break; // Not Reporting
2658 default: return -1; // Unknown Result
2659 }
2660 if (*p++ != '\033')
2661 return -1;
2662 if (*p++ != '\\')
2663 return -1;
2664 switch (status)
2665 {
2666 case 'D': // Double Click
2667 case 'S': // Single Click
2668 if (button & 1) mouse_code |= MOUSE_LEFT;
2669 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2670 if (button & 4) mouse_code |= MOUSE_RIGHT;
2671 if (button & 8) mouse_code |= MOUSE_SHIFT;
2672 if (button & 16) mouse_code |= MOUSE_CTRL;
2673 break;
2674 case 'm': // Mouse move
2675 if (button & 1) mouse_code |= MOUSE_LEFT;
2676 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2677 if (button & 4) mouse_code |= MOUSE_RIGHT;
2678 if (button & 8) mouse_code |= MOUSE_SHIFT;
2679 if (button & 16) mouse_code |= MOUSE_CTRL;
2680 if ((button & 7) != 0)
2681 {
2682 held_button = mouse_code;
2683 mouse_code |= MOUSE_DRAG;
2684 }
2685 is_drag = TRUE;
2686 showmode();
2687 break;
2688 case 'd': // Button Down
2689 if (button & 1) mouse_code |= MOUSE_LEFT;
2690 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2691 if (button & 4) mouse_code |= MOUSE_RIGHT;
2692 if (button & 8) mouse_code |= MOUSE_SHIFT;
2693 if (button & 16) mouse_code |= MOUSE_CTRL;
2694 break;
2695 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002696 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002697 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002698 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002699 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002700 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002701 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002702 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002703 if (button & 8)
2704 mouse_code |= MOUSE_SHIFT;
2705 if (button & 16)
2706 mouse_code |= MOUSE_CTRL;
2707 break;
2708 default: return -1; // Unknown Result
2709 }
2710
2711 *slen += (p - (tp + *slen));
2712 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002713#endif // FEAT_MOUSE_JSB
2714#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002715 if (key_name[0] == KS_DEC_MOUSE)
2716 {
2717 /*
2718 * The DEC Locator Input Model
2719 * Netterm delivers the code sequence:
2720 * \033[2;4;24;80&w (left button down)
2721 * \033[3;0;24;80&w (left button up)
2722 * \033[6;1;24;80&w (right button down)
2723 * \033[7;0;24;80&w (right button up)
2724 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2725 * Pe is the event code
2726 * Pb is the button code
2727 * Pr is the row coordinate
2728 * Pc is the column coordinate
2729 * Pp is the third coordinate (page number)
2730 * Pe, the event code indicates what event caused this report
2731 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002732 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002733 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002734 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002735 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002736 * 2 - left button down
2737 * 3 - left button up
2738 * 4 - middle button down
2739 * 5 - middle button up
2740 * 6 - right button down
2741 * 7 - right button up
2742 * 8 - fourth button down
2743 * 9 - fourth button up
2744 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002745 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2746 * down if any. The state of the four buttons on the locator
2747 * correspond to the low four bits of the decimal value, "1" means
2748 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002749 * 0 - no buttons down,
2750 * 1 - right,
2751 * 2 - middle,
2752 * 4 - left,
2753 * 8 - fourth
2754 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002755 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2756 * position is undefined (outside the terminal window for example).
2757 * Pc is the column coordinate of the locator position in the page,
2758 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2759 * position is undefined (outside the terminal window for example).
2760 * Pp is the page coordinate of the locator position encoded as an
2761 * ASCII decimal value. The page coordinate may be omitted if the
2762 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002763 */
2764 int Pe, Pb, Pr, Pc;
2765
2766 p = tp + *slen;
2767
2768 // get event status
2769 Pe = getdigits(&p);
2770 if (*p++ != ';')
2771 return -1;
2772
2773 // get button status
2774 Pb = getdigits(&p);
2775 if (*p++ != ';')
2776 return -1;
2777
2778 // get row status
2779 Pr = getdigits(&p);
2780 if (*p++ != ';')
2781 return -1;
2782
2783 // get column status
2784 Pc = getdigits(&p);
2785
2786 // the page parameter is optional
2787 if (*p == ';')
2788 {
2789 p++;
2790 (void)getdigits(&p);
2791 }
2792 if (*p++ != '&')
2793 return -1;
2794 if (*p++ != 'w')
2795 return -1;
2796
2797 mouse_code = 0;
2798 switch (Pe)
2799 {
2800 case 0: return -1; // position request while unavailable
2801 case 1: // a response to a locator position request includes
2802 // the status of all buttons
2803 Pb &= 7; // mask off and ignore fourth button
2804 if (Pb & 4)
2805 mouse_code = MOUSE_LEFT;
2806 if (Pb & 2)
2807 mouse_code = MOUSE_MIDDLE;
2808 if (Pb & 1)
2809 mouse_code = MOUSE_RIGHT;
2810 if (Pb)
2811 {
2812 held_button = mouse_code;
2813 mouse_code |= MOUSE_DRAG;
2814 WantQueryMouse = TRUE;
2815 }
2816 is_drag = TRUE;
2817 showmode();
2818 break;
2819 case 2: mouse_code = MOUSE_LEFT;
2820 WantQueryMouse = TRUE;
2821 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002822 case 3: mouse_code = MOUSE_LEFT;
2823 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002824 break;
2825 case 4: mouse_code = MOUSE_MIDDLE;
2826 WantQueryMouse = TRUE;
2827 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002828 case 5: mouse_code = MOUSE_MIDDLE;
2829 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002830 break;
2831 case 6: mouse_code = MOUSE_RIGHT;
2832 WantQueryMouse = TRUE;
2833 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002834 case 7: mouse_code = MOUSE_RIGHT;
2835 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002836 break;
2837 case 8: return -1; // fourth button down
2838 case 9: return -1; // fourth button up
2839 case 10: return -1; // mouse outside of filter rectangle
2840 default: return -1; // should never occur
2841 }
2842
2843 mouse_col = Pc - 1;
2844 mouse_row = Pr - 1;
2845
2846 *slen += (int)(p - (tp + *slen));
2847 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002848#endif // FEAT_MOUSE_DEC
2849#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002850 if (key_name[0] == KS_PTERM_MOUSE)
2851 {
2852 int button, num_clicks, action;
2853
2854 p = tp + *slen;
2855
2856 action = getdigits(&p);
2857 if (*p++ != ';')
2858 return -1;
2859
2860 mouse_row = getdigits(&p);
2861 if (*p++ != ';')
2862 return -1;
2863 mouse_col = getdigits(&p);
2864 if (*p++ != ';')
2865 return -1;
2866
2867 button = getdigits(&p);
2868 mouse_code = 0;
2869
2870 switch (button)
2871 {
2872 case 4: mouse_code = MOUSE_LEFT; break;
2873 case 1: mouse_code = MOUSE_RIGHT; break;
2874 case 2: mouse_code = MOUSE_MIDDLE; break;
2875 default: return -1;
2876 }
2877
2878 switch (action)
2879 {
2880 case 31: // Initial press
2881 if (*p++ != ';')
2882 return -1;
2883
2884 num_clicks = getdigits(&p); // Not used
2885 break;
2886
2887 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002888 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002889 break;
2890
2891 case 33: // Drag
2892 held_button = mouse_code;
2893 mouse_code |= MOUSE_DRAG;
2894 break;
2895
2896 default:
2897 return -1;
2898 }
2899
2900 if (*p++ != 't')
2901 return -1;
2902
2903 *slen += (p - (tp + *slen));
2904 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002905#endif // FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002906
2907 // Interpret the mouse code
2908 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002909 if (is_release)
2910 current_button |= MOUSE_RELEASE;
2911
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002912 if (current_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002913#ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002914 && wheel_code == 0
Christopher Plewright696d0a82022-11-18 17:53:34 +00002915#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002916 )
2917 {
2918 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002919 * If we get a mouse drag or release event when there is no mouse
2920 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2921 * below.
2922 * (can happen when you hold down two buttons and then let them go, or
2923 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002924 */
2925 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2926 is_drag = TRUE;
2927 current_button = held_button;
2928 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002929 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002930 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002931 if (wheel_code == 0)
2932 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002933#ifdef CHECK_DOUBLE_CLICK
2934# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002935 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002936 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2937 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002938 */
Christopher Plewright696d0a82022-11-18 17:53:34 +00002939# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002940 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002941# else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002942 if (key_name[0] != KS_GPM_MOUSE)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002943# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002944# else
2945# ifdef FEAT_GUI
2946 if (!gui.in_use)
2947# endif
2948# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002949 {
2950 /*
2951 * Compute the time elapsed since the previous mouse click.
2952 */
2953 gettimeofday(&mouse_time, NULL);
2954 if (orig_mouse_time.tv_sec == 0)
2955 {
2956 /*
2957 * Avoid computing the difference between mouse_time
2958 * and orig_mouse_time for the first click, as the
2959 * difference would be huge and would cause
2960 * multiplication overflow.
2961 */
2962 timediff = p_mouset;
2963 }
2964 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002965 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002966 orig_mouse_time = mouse_time;
2967 if (mouse_code == orig_mouse_code
2968 && timediff < p_mouset
2969 && orig_num_clicks != 4
2970 && orig_mouse_col == mouse_col
2971 && orig_mouse_row == mouse_row
2972 && (is_mouse_topline(curwin)
2973 // Double click in tab pages line also works
2974 // when window contents changes.
2975 || (mouse_row == 0 && firstwin->w_winrow > 0))
2976 )
2977 ++orig_num_clicks;
2978 else
2979 orig_num_clicks = 1;
2980 orig_mouse_col = mouse_col;
2981 orig_mouse_row = mouse_row;
2982 set_mouse_topline(curwin);
2983 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002984# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002985 else
2986 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002987# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002988#else
2989 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2990#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002991 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002992 }
2993 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002994 }
2995 if (!is_drag)
2996 held_button = mouse_code & MOUSE_CLICK_MASK;
2997
2998 /*
2999 * Translate the actual mouse event into a pseudo mouse event.
3000 * First work out what modifiers are to be used.
3001 */
3002 if (orig_mouse_code & MOUSE_SHIFT)
3003 *modifiers |= MOD_MASK_SHIFT;
3004 if (orig_mouse_code & MOUSE_CTRL)
3005 *modifiers |= MOD_MASK_CTRL;
3006 if (orig_mouse_code & MOUSE_ALT)
3007 *modifiers |= MOD_MASK_ALT;
3008 if (orig_num_clicks == 2)
3009 *modifiers |= MOD_MASK_2CLICK;
3010 else if (orig_num_clicks == 3)
3011 *modifiers |= MOD_MASK_3CLICK;
3012 else if (orig_num_clicks == 4)
3013 *modifiers |= MOD_MASK_4CLICK;
3014
Bram Moolenaar13c04632020-07-12 13:47:42 +02003015 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
3016 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003017 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003018 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003019 {
3020 if (wheel_code & MOUSE_CTRL)
3021 *modifiers |= MOD_MASK_CTRL;
3022 if (wheel_code & MOUSE_ALT)
3023 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003024
3025 if (wheel_code & 1 && wheel_code & 2)
3026 key_name[1] = (int)KE_MOUSELEFT;
3027 else if (wheel_code & 2)
3028 key_name[1] = (int)KE_MOUSERIGHT;
3029 else if (wheel_code & 1)
3030 key_name[1] = (int)KE_MOUSEUP;
3031 else
3032 key_name[1] = (int)KE_MOUSEDOWN;
3033
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003034 held_button = MOUSE_RELEASE;
3035 }
3036 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02003037 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003038
Bram Moolenaar13c04632020-07-12 13:47:42 +02003039
3040 // Make sure the mouse position is valid. Some terminals may return weird
3041 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003042 if (mouse_col >= Columns)
3043 mouse_col = Columns - 1;
3044 if (mouse_row >= Rows)
3045 mouse_row = Rows - 1;
3046
3047 return 0;
3048}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003049
3050// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003051
3052/*
3053 * Compute the buffer line position from the screen position "rowp" / "colp" in
3054 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02003055 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
3056 * caches the plines_win() result from a previous call. Entry is zero if not
3057 * computed yet. There must be no text or setting changes since the entry is
3058 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003059 * Returns TRUE if the position is below the last line.
3060 */
3061 int
3062mouse_comp_pos(
3063 win_T *win,
3064 int *rowp,
3065 int *colp,
3066 linenr_T *lnump,
3067 int *plines_cache)
3068{
3069 int col = *colp;
3070 int row = *rowp;
3071 linenr_T lnum;
3072 int retval = FALSE;
3073 int off;
3074 int count;
3075
3076#ifdef FEAT_RIGHTLEFT
3077 if (win->w_p_rl)
3078 col = win->w_width - 1 - col;
3079#endif
3080
3081 lnum = win->w_topline;
3082
3083 while (row > 0)
3084 {
3085 int cache_idx = lnum - win->w_topline;
3086
Bram Moolenaar452143c2020-07-15 17:38:21 +02003087 // Only "Rows" lines are cached, with folding we'll run out of entries
3088 // and use the slow way.
3089 if (plines_cache != NULL && cache_idx < Rows
3090 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003091 count = plines_cache[cache_idx];
3092 else
3093 {
3094#ifdef FEAT_DIFF
3095 // Don't include filler lines in "count"
3096 if (win->w_p_diff
3097# ifdef FEAT_FOLDING
3098 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3099# endif
3100 )
3101 {
3102 if (lnum == win->w_topline)
3103 row -= win->w_topfill;
3104 else
3105 row -= diff_check_fill(win, lnum);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003106 count = plines_win_nofill(win, lnum, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003107 }
3108 else
3109#endif
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003110 count = plines_win(win, lnum, FALSE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02003111 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003112 plines_cache[cache_idx] = count;
3113 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003114
3115 if (win->w_skipcol > 0 && lnum == win->w_topline)
3116 {
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003117 int width1 = win->w_width - win_col_off(win);
zeertzjq031a7452024-05-11 11:23:37 +02003118
3119 if (width1 > 0)
3120 {
3121 int skip_lines = 0;
3122
3123 // Adjust for 'smoothscroll' clipping the top screen lines.
3124 // A similar formula is used in curs_columns().
3125 if (win->w_skipcol > width1)
3126 skip_lines = (win->w_skipcol - width1)
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003127 / (width1 + win_col_off2(win)) + 1;
zeertzjq031a7452024-05-11 11:23:37 +02003128 else if (win->w_skipcol > 0)
3129 skip_lines = 1;
3130
3131 count -= skip_lines;
3132 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003133 }
3134
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003135 if (count > row)
3136 break; // Position is in this buffer line.
3137#ifdef FEAT_FOLDING
3138 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3139#endif
3140 if (lnum == win->w_buffer->b_ml.ml_line_count)
3141 {
3142 retval = TRUE;
3143 break; // past end of file
3144 }
3145 row -= count;
3146 ++lnum;
3147 }
3148
3149 if (!retval)
3150 {
3151 // Compute the column without wrapping.
3152 off = win_col_off(win) - win_col_off2(win);
3153 if (col < off)
3154 col = off;
3155 col += row * (win->w_width - off);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003156
3157 // Add skip column for the topline.
3158 if (lnum == win->w_topline)
3159 col += win->w_skipcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003160 }
3161
3162 if (!win->w_p_wrap)
3163 col += win->w_leftcol;
3164
3165 // skip line number and fold column in front of the line
3166 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003167 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003168 {
3169#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003170 // if mouse is clicked on the gutter, then inform the netbeans server
3171 if (*colp < win_col_off(win))
3172 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003173#endif
3174 col = 0;
3175 }
3176
3177 *colp = col;
3178 *rowp = row;
3179 *lnump = lnum;
3180 return retval;
3181}
3182
3183/*
3184 * Find the window at screen position "*rowp" and "*colp". The positions are
3185 * updated to become relative to the top-left of the window.
3186 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3187 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3188 * windows.
3189 * Returns NULL when something is wrong.
3190 */
3191 win_T *
3192mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3193{
3194 frame_T *fp;
3195 win_T *wp;
3196
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003197#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003198 win_T *pwp = NULL;
3199
3200 if (popup != IGNORE_POPUP)
3201 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003202 popup_reset_handled(POPUP_HANDLED_1);
3203 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003204 {
3205 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3206 && *colp >= wp->w_wincol
3207 && *colp < wp->w_wincol + popup_width(wp))
3208 pwp = wp;
3209 }
3210 if (pwp != NULL)
3211 {
3212 if (popup == FAIL_POPUP)
3213 return NULL;
3214 *rowp -= pwp->w_winrow;
3215 *colp -= pwp->w_wincol;
3216 return pwp;
3217 }
3218 }
3219#endif
3220
3221 fp = topframe;
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02003222
3223 if (*colp < firstwin->w_wincol
3224 || *colp >= firstwin->w_wincol + fp->fr_width
3225 || *rowp < firstwin->w_winrow)
3226 return NULL;
3227
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003228 *rowp -= firstwin->w_winrow;
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02003229 *colp -= firstwin->w_wincol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003230 for (;;)
3231 {
3232 if (fp->fr_layout == FR_LEAF)
3233 break;
3234 if (fp->fr_layout == FR_ROW)
3235 {
3236 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3237 {
3238 if (*colp < fp->fr_width)
3239 break;
3240 *colp -= fp->fr_width;
3241 }
3242 }
3243 else // fr_layout == FR_COL
3244 {
3245 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3246 {
3247 if (*rowp < fp->fr_height)
3248 break;
3249 *rowp -= fp->fr_height;
3250 }
3251 }
3252 }
3253 // When using a timer that closes a window the window might not actually
3254 // exist.
3255 FOR_ALL_WINDOWS(wp)
3256 if (wp == fp->fr_win)
3257 {
3258#ifdef FEAT_MENU
3259 *rowp -= wp->w_winbar_height;
3260#endif
3261 return wp;
3262 }
3263 return NULL;
3264}
3265
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003266#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003267 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003268/*
3269 * Convert a virtual (screen) column to a character column.
zeertzjqb583eda2023-10-14 11:32:28 +02003270 * The first column is zero.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003271 */
3272 int
zeertzjqf5a94d52023-10-15 10:03:30 +02003273vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003274{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003275 char_u *line;
3276 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003277
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003278 // try to advance to the specified column
3279 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3280 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3281 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003282 {
zeertzjqb583eda2023-10-14 11:32:28 +02003283 int size = win_lbr_chartabsize(&cts, NULL);
3284 if (cts.cts_vcol + size > vcol)
3285 break;
3286 cts.cts_vcol += size;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003287 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003288 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003289 clear_chartabsize_arg(&cts);
3290
zeertzjqf5a94d52023-10-15 10:03:30 +02003291 if (coladdp != NULL)
3292 *coladdp = vcol - cts.cts_vcol;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003293 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003294}
3295#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003296
3297#if defined(FEAT_EVAL) || defined(PROTO)
Christopher Plewright696d0a82022-11-18 17:53:34 +00003298/*
3299 * "getmousepos()" function.
3300 */
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003301 void
3302f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3303{
3304 dict_T *d;
3305 win_T *wp;
3306 int row = mouse_row;
3307 int col = mouse_col;
3308 varnumber_T winid = 0;
3309 varnumber_T winrow = 0;
3310 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003311 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003312 varnumber_T column = 0;
zeertzjqf5a94d52023-10-15 10:03:30 +02003313 colnr_T coladd = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003314
Bram Moolenaar93a10962022-06-16 11:42:09 +01003315 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003316 return;
3317 d = rettv->vval.v_dict;
3318
3319 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3320 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3321
3322 wp = mouse_find_win(&row, &col, FIND_POPUP);
3323 if (wp != NULL)
3324 {
3325 int top_off = 0;
3326 int left_off = 0;
3327 int height = wp->w_height + wp->w_status_height;
3328
Christopher Plewright696d0a82022-11-18 17:53:34 +00003329# ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003330 if (WIN_IS_POPUP(wp))
3331 {
3332 top_off = popup_top_extra(wp);
3333 left_off = popup_left_extra(wp);
3334 height = popup_height(wp);
3335 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00003336# endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003337 if (row < height)
3338 {
3339 winid = wp->w_id;
3340 winrow = row + 1;
3341 wincol = col + 1;
3342 row -= top_off;
3343 col -= left_off;
3344 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3345 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003346 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
zeertzjqf5a94d52023-10-15 10:03:30 +02003347 col = vcol2col(wp, lnum, col, &coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003348 column = col + 1;
3349 }
3350 }
3351 }
3352 dict_add_number(d, "winid", winid);
3353 dict_add_number(d, "winrow", winrow);
3354 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003355 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003356 dict_add_number(d, "column", column);
zeertzjqf5a94d52023-10-15 10:03:30 +02003357 dict_add_number(d, "coladd", coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003358}
3359#endif