blob: dcb6cbe1478fd74155f0b259b83475fa11fe9f51 [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)
Hirohito Higashia1522f72025-06-03 21:07:25 +0200340 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000341 got_click = TRUE;
Hirohito Higashia1522f72025-06-03 21:07:25 +0200342 in_tab_line = FALSE;
343#if defined(FEAT_TABPANEL)
344 in_tabpanel = FALSE;
345#endif
346 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200347 else
348 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000349 if (!got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200350 return FALSE;
zeertzjq8e0ccb62022-11-01 18:35:27 +0000351 if (!is_drag) // release, reset got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200352 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000353 got_click = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200354 if (in_tab_line
355#if defined(FEAT_TABPANEL)
356 || in_tabpanel
357#endif
358 )
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200359 {
360 in_tab_line = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200361#if defined(FEAT_TABPANEL)
362 in_tabpanel = FALSE;
363#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200364 return FALSE;
365 }
366 }
367 }
368
369 // CTRL right mouse button does CTRL-T
370 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
371 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100372 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200373 stuffcharReadbuff(Ctrl_O);
374 if (count > 1)
375 stuffnumReadbuff(count);
376 stuffcharReadbuff(Ctrl_T);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000377 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200378 return FALSE;
379 }
380
381 // CTRL only works with left mouse button
382 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
383 return FALSE;
384
385 // When a modifier is down, ignore drag and release events, as well as
386 // multiple clicks and the middle mouse button.
387 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
388 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
389 | MOD_MASK_META))
390 && (!is_click
391 || (mod_mask & MOD_MASK_MULTI_CLICK)
392 || which_button == MOUSE_MIDDLE)
393 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
394 && mouse_model_popup()
395 && which_button == MOUSE_LEFT)
396 && !((mod_mask & MOD_MASK_ALT)
397 && !mouse_model_popup()
398 && which_button == MOUSE_RIGHT)
399 )
400 return FALSE;
401
402 // If the button press was used as the movement command for an operator
403 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
404 // drag/release events.
405 if (!is_click && which_button == MOUSE_MIDDLE)
406 return FALSE;
407
408 if (oap != NULL)
409 regname = oap->regname;
410 else
411 regname = 0;
412
413 // Middle mouse button does a 'put' of the selected text
414 if (which_button == MOUSE_MIDDLE)
415 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100416 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200417 {
418 // If an operator was pending, we don't know what the user wanted
419 // to do. Go back to normal mode: Clear the operator and beep().
420 if (oap != NULL && oap->op_type != OP_NOP)
421 {
422 clearopbeep(oap);
423 return FALSE;
424 }
425
426 // If visual was active, yank the highlighted text and put it
427 // before the mouse pointer position.
428 // In Select mode replace the highlighted text with the clipboard.
429 if (VIsual_active)
430 {
431 if (VIsual_select)
432 {
433 stuffcharReadbuff(Ctrl_G);
434 stuffReadbuff((char_u *)"\"+p");
435 }
436 else
437 {
438 stuffcharReadbuff('y');
439 stuffcharReadbuff(K_MIDDLEMOUSE);
440 }
441 do_always = TRUE; // ignore 'mouse' setting next time
442 return FALSE;
443 }
444 // The rest is below jump_to_mouse()
445 }
446
Bram Moolenaar24959102022-05-07 20:01:16 +0100447 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200448 return FALSE;
449
450 // Middle click in insert mode doesn't move the mouse, just insert the
451 // contents of a register. '.' register is special, can't insert that
452 // with do_put().
453 // Also paste at the cursor if the current mode isn't in 'mouse' (only
454 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100455 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200456 {
457 if (regname == '.')
458 insert_reg(regname, TRUE);
459 else
460 {
461#ifdef FEAT_CLIPBOARD
462 if (clip_star.available && regname == 0)
463 regname = '*';
464#endif
465 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
466 insert_reg(regname, TRUE);
467 else
468 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200469 do_put(regname, NULL, BACKWARD, 1L,
470 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200471
472 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
473 AppendCharToRedobuff(Ctrl_R);
474 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
475 AppendCharToRedobuff(regname == 0 ? '"' : regname);
476 }
477 }
478 return FALSE;
479 }
480 }
481
482 // When dragging or button-up stay in the same window.
483 if (!is_click)
484 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
485
486 start_visual.lnum = 0;
487
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200488 // Check for clicking in the tab page panel.
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200489#if defined(FEAT_TABPANEL)
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200490 if (mouse_row < firstwin->w_winrow + topframe->fr_height
491 && (mouse_col < firstwin->w_wincol
492 || mouse_col >= firstwin->w_wincol + topframe->fr_width))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200493 {
494 if (is_drag)
495 {
496 if (in_tabpanel)
497 {
498 c1 = get_tabpagenr_on_tabpanel();
499 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
500 ? c1 - 1 : c1);
501 }
502 return FALSE;
503 }
504
505 // click in a tab selects that tab page
506 if (is_click
507# ifdef FEAT_CMDWIN
508 && cmdwin_type == 0
509# endif
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200510 )
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200511 {
512 in_tabpanel = TRUE;
513 c1 = get_tabpagenr_on_tabpanel();
514 if (c1 >= 0)
515 {
516 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
517 {
518 // double click opens new page
519 end_visual_mode();
520 tabpage_new();
521 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
522 }
523 else
524 {
525 // Go to specified tab page, or next one if not clicking
526 // on a label.
527 goto_tabpage(c1);
528
529 // It's like clicking on the status line of a window.
530 if (curwin != old_curwin)
531 end_visual_mode();
532 }
533 }
534 else
535 {
536 tabpage_T *tp;
537
538 // Close the current or specified tab page.
539 if (c1 == -999)
540 tp = curtab;
541 else
542 tp = find_tabpage(-c1);
543 if (tp == curtab)
544 {
545 if (first_tabpage->tp_next != NULL)
546 tabpage_close(FALSE);
547 }
548 else if (tp != NULL)
549 tabpage_close_other(tp, FALSE);
550 }
551 }
552 return TRUE;
553 }
554 else if (is_drag && in_tabpanel)
555 {
556 c1 = get_tabpagenr_on_tabpanel();
557 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
558 return FALSE;
559 }
560#endif
561
Bram Moolenaar80525752022-08-24 19:27:45 +0100562 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200563 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100564 // Check for clicking in the tab page line.
565 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200566 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100567 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200568 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100569 if (in_tab_line)
570 {
571 c1 = TabPageIdxs[mouse_col];
572 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100573 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100574 }
575 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200576 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100577
578 // click in a tab selects that tab page
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200579 if (is_click && cmdwin_type == 0
580 && mouse_col < firstwin->w_wincol + topframe->fr_width)
Bram Moolenaar80525752022-08-24 19:27:45 +0100581 {
582 in_tab_line = TRUE;
583 c1 = TabPageIdxs[mouse_col];
584 if (c1 >= 0)
585 {
586 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
587 {
588 // double click opens new page
589 end_visual_mode_keep_button();
590 tabpage_new();
591 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
592 }
593 else
594 {
595 // Go to specified tab page, or next one if not clicking
596 // on a label.
597 goto_tabpage(c1);
598
599 // It's like clicking on the status line of a window.
600 if (curwin != old_curwin)
601 end_visual_mode_keep_button();
602 }
603 }
604 else
605 {
606 tabpage_T *tp;
607
608 // Close the current or specified tab page.
609 if (c1 == -999)
610 tp = curtab;
611 else
612 tp = find_tabpage(-c1);
613 if (tp == curtab)
614 {
615 if (first_tabpage->tp_next != NULL)
616 tabpage_close(FALSE);
617 }
618 else if (tp != NULL)
619 tabpage_close_other(tp, FALSE);
620 }
621 }
622 return TRUE;
623 }
624 else if (is_drag && in_tab_line)
625 {
626 c1 = TabPageIdxs[mouse_col];
627 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200628 return FALSE;
629 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200630 }
631
632 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
633 // right button up -> pop-up menu
634 // shift-left button -> right button
635 // alt-left button -> alt-right button
636 if (mouse_model_popup())
637 {
638 if (which_button == MOUSE_RIGHT
639 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
640 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200641#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200642# ifdef FEAT_GUI
643 if (gui.in_use)
644 {
645# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200646 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200647 if (!is_click)
648 // Ignore right button release events, only shows the popup
649 // menu on the button down event.
650 return FALSE;
651# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100652# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200653 if (is_click || is_drag)
654 // Ignore right button down and drag mouse events. Windows
655 // only shows the popup menu on the button up event.
656 return FALSE;
657# endif
658 }
659# endif
660# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
661 else
662# endif
663# if defined(FEAT_TERM_POPUP_MENU)
664 if (!is_click)
665 // Ignore right button release events, only shows the popup
666 // menu on the button down event.
667 return FALSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +0000668# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200669
670 jump_flags = 0;
671 if (STRCMP(p_mousem, "popup_setpos") == 0)
672 {
673 // First set the cursor position before showing the popup
674 // menu.
675 if (VIsual_active)
676 {
677 pos_T m_pos;
678
679 // set MOUSE_MAY_STOP_VIS if we are outside the
680 // selection or the current window (might have false
681 // negative here)
682 if (mouse_row < curwin->w_winrow
683 || mouse_row
684 > (curwin->w_winrow + curwin->w_height))
685 jump_flags = MOUSE_MAY_STOP_VIS;
686 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
687 jump_flags = MOUSE_MAY_STOP_VIS;
688 else
689 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100690 if (VIsual_mode == 'V')
691 {
692 if ((curwin->w_cursor.lnum <= VIsual.lnum
693 && (m_pos.lnum < curwin->w_cursor.lnum
694 || VIsual.lnum < m_pos.lnum))
695 || (VIsual.lnum < curwin->w_cursor.lnum
696 && (m_pos.lnum < VIsual.lnum
697 || curwin->w_cursor.lnum < m_pos.lnum)))
698 {
699 jump_flags = MOUSE_MAY_STOP_VIS;
700 }
701 }
702 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200703 && (LT_POS(m_pos, curwin->w_cursor)
704 || LT_POS(VIsual, m_pos)))
705 || (LT_POS(VIsual, curwin->w_cursor)
706 && (LT_POS(m_pos, VIsual)
707 || LT_POS(curwin->w_cursor, m_pos))))
708 {
709 jump_flags = MOUSE_MAY_STOP_VIS;
710 }
711 else if (VIsual_mode == Ctrl_V)
712 {
713 getvcols(curwin, &curwin->w_cursor, &VIsual,
714 &leftcol, &rightcol);
715 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
716 if (m_pos.col < leftcol || m_pos.col > rightcol)
717 jump_flags = MOUSE_MAY_STOP_VIS;
718 }
719 }
720 }
721 else
722 jump_flags = MOUSE_MAY_STOP_VIS;
723 }
724 if (jump_flags)
725 {
726 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100727 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200728 setcursor();
729 out_flush(); // Update before showing popup menu
730 }
731# ifdef FEAT_MENU
732 show_popupmenu();
zeertzjq8e0ccb62022-11-01 18:35:27 +0000733 got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200734# endif
735 return (jump_flags & CURSOR_MOVED) != 0;
736#else
737 return FALSE;
738#endif
739 }
740 if (which_button == MOUSE_LEFT
741 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
742 {
743 which_button = MOUSE_RIGHT;
744 mod_mask &= ~MOD_MASK_SHIFT;
745 }
746 }
747
Bram Moolenaar24959102022-05-07 20:01:16 +0100748 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200749 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
750 {
751 if (which_button == MOUSE_LEFT)
752 {
753 if (is_click)
754 {
755 // stop Visual mode for a left click in a window, but not when
756 // on a status line
757 if (VIsual_active)
758 jump_flags |= MOUSE_MAY_STOP_VIS;
759 }
760 else if (mouse_has(MOUSE_VISUAL))
761 jump_flags |= MOUSE_MAY_VIS;
762 }
763 else if (which_button == MOUSE_RIGHT)
764 {
765 if (is_click && VIsual_active)
766 {
767 // Remember the start and end of visual before moving the
768 // cursor.
769 if (LT_POS(curwin->w_cursor, VIsual))
770 {
771 start_visual = curwin->w_cursor;
772 end_visual = VIsual;
773 }
774 else
775 {
776 start_visual = VIsual;
777 end_visual = curwin->w_cursor;
778 }
779 }
780 jump_flags |= MOUSE_FOCUS;
781 if (mouse_has(MOUSE_VISUAL))
782 jump_flags |= MOUSE_MAY_VIS;
783 }
784 }
785
786 // If an operator is pending, ignore all drags and releases until the
787 // next mouse click.
788 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
789 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000790 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200791 oap->motion_type = MCHAR;
792 }
793
794 // When releasing the button let jump_to_mouse() know.
795 if (!is_click && !is_drag)
796 jump_flags |= MOUSE_RELEASED;
797
798 // JUMP!
799 jump_flags = jump_to_mouse(jump_flags,
800 oap == NULL ? NULL : &(oap->inclusive), which_button);
801
802#ifdef FEAT_MENU
803 // A click in the window toolbar has no side effects.
804 if (jump_flags & MOUSE_WINBAR)
805 return FALSE;
806#endif
807 moved = (jump_flags & CURSOR_MOVED);
808 in_status_line = (jump_flags & IN_STATUS_LINE);
809 in_sep_line = (jump_flags & IN_SEP_LINE);
810
811#ifdef FEAT_NETBEANS_INTG
812 if (isNetbeansBuffer(curbuf)
813 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
814 {
815 int key = KEY2TERMCAP1(c);
816
817 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
818 || key == (int)KE_RIGHTRELEASE)
819 netbeans_button_release(which_button);
820 }
821#endif
822
823 // When jumping to another window, clear a pending operator. That's a bit
824 // friendlier than beeping and not jumping to that window.
825 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
826 clearop(oap);
827
828#ifdef FEAT_FOLDING
829 if (mod_mask == 0
830 && !is_drag
831 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
832 && which_button == MOUSE_LEFT)
833 {
834 // open or close a fold at this line
835 if (jump_flags & MOUSE_FOLD_OPEN)
836 openFold(curwin->w_cursor.lnum, 1L);
837 else
838 closeFold(curwin->w_cursor.lnum, 1L);
839 // don't move the cursor if still in the same window
840 if (curwin == old_curwin)
841 curwin->w_cursor = save_cursor;
842 }
843#endif
844
Martin Tournoij7904fa42022-10-04 16:28:45 +0100845#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200846 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
847 {
848 clip_modeless(which_button, is_click, is_drag);
849 return FALSE;
850 }
851#endif
852
853 // Set global flag that we are extending the Visual area with mouse
854 // dragging; temporarily minimize 'scrolloff'.
855 if (VIsual_active && is_drag && get_scrolloff_value())
856 {
857 // In the very first line, allow scrolling one line
858 if (mouse_row == 0)
859 mouse_dragging = 2;
860 else
861 mouse_dragging = 1;
862 }
863
864 // When dragging the mouse above the window, scroll down.
865 if (is_drag && mouse_row < 0 && !in_status_line)
866 {
867 scroll_redraw(FALSE, 1L);
868 mouse_row = 0;
869 }
870
871 if (start_visual.lnum) // right click in visual mode
872 {
873 // When ALT is pressed make Visual mode blockwise.
874 if (mod_mask & MOD_MASK_ALT)
875 VIsual_mode = Ctrl_V;
876
877 // In Visual-block mode, divide the area in four, pick up the corner
878 // that is in the quarter that the cursor is in.
879 if (VIsual_mode == Ctrl_V)
880 {
881 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
882 if (curwin->w_curswant > (leftcol + rightcol) / 2)
883 end_visual.col = leftcol;
884 else
885 end_visual.col = rightcol;
886 if (curwin->w_cursor.lnum >=
887 (start_visual.lnum + end_visual.lnum) / 2)
888 end_visual.lnum = start_visual.lnum;
889
890 // move VIsual to the right column
891 start_visual = curwin->w_cursor; // save the cursor pos
892 curwin->w_cursor = end_visual;
893 coladvance(end_visual.col);
894 VIsual = curwin->w_cursor;
895 curwin->w_cursor = start_visual; // restore the cursor
896 }
897 else
898 {
899 // If the click is before the start of visual, change the start.
900 // If the click is after the end of visual, change the end. If
901 // the click is inside the visual, change the closest side.
902 if (LT_POS(curwin->w_cursor, start_visual))
903 VIsual = end_visual;
904 else if (LT_POS(end_visual, curwin->w_cursor))
905 VIsual = start_visual;
906 else
907 {
908 // In the same line, compare column number
909 if (end_visual.lnum == start_visual.lnum)
910 {
911 if (curwin->w_cursor.col - start_visual.col >
912 end_visual.col - curwin->w_cursor.col)
913 VIsual = start_visual;
914 else
915 VIsual = end_visual;
916 }
917
918 // In different lines, compare line number
919 else
920 {
921 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
922 (end_visual.lnum - curwin->w_cursor.lnum);
923
924 if (diff > 0) // closest to end
925 VIsual = start_visual;
926 else if (diff < 0) // closest to start
927 VIsual = end_visual;
928 else // in the middle line
929 {
930 if (curwin->w_cursor.col <
931 (start_visual.col + end_visual.col) / 2)
932 VIsual = end_visual;
933 else
934 VIsual = start_visual;
935 }
936 }
937 }
938 }
939 }
940 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100941 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200942 stuffcharReadbuff(Ctrl_O);
943
944 // Middle mouse click: Put text before cursor.
945 if (which_button == MOUSE_MIDDLE)
946 {
947#ifdef FEAT_CLIPBOARD
948 if (clip_star.available && regname == 0)
949 regname = '*';
950#endif
951 if (yank_register_mline(regname))
952 {
953 if (mouse_past_bottom)
954 dir = FORWARD;
955 }
956 else if (mouse_past_eol)
957 dir = FORWARD;
958
959 if (fixindent)
960 {
961 c1 = (dir == BACKWARD) ? '[' : ']';
962 c2 = 'p';
963 }
964 else
965 {
966 c1 = (dir == FORWARD) ? 'p' : 'P';
967 c2 = NUL;
968 }
969 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
970
971 // Remember where the paste started, so in edit() Insstart can be set
972 // to this position
973 if (restart_edit != 0)
974 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200975 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200976 }
977
978#if defined(FEAT_QUICKFIX)
979 // Ctrl-Mouse click or double click in a quickfix window jumps to the
980 // error under the mouse pointer.
981 else if (((mod_mask & MOD_MASK_CTRL)
982 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
983 && bt_quickfix(curbuf))
984 {
985 if (curwin->w_llist_ref == NULL) // quickfix window
986 do_cmdline_cmd((char_u *)".cc");
987 else // location list window
988 do_cmdline_cmd((char_u *)".ll");
zeertzjq8e0ccb62022-11-01 18:35:27 +0000989 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200990 }
991#endif
992
993 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
994 // under the mouse pointer.
995 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
996 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
997 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100998 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200999 stuffcharReadbuff(Ctrl_O);
1000 stuffcharReadbuff(Ctrl_RSB);
zeertzjq8e0ccb62022-11-01 18:35:27 +00001001 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001002 }
1003
1004 // Shift-Mouse click searches for the next occurrence of the word under
1005 // the mouse pointer
1006 else if ((mod_mask & MOD_MASK_SHIFT))
1007 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001008 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001009 stuffcharReadbuff(Ctrl_O);
1010 if (which_button == MOUSE_LEFT)
1011 stuffcharReadbuff('*');
1012 else // MOUSE_RIGHT
1013 stuffcharReadbuff('#');
1014 }
1015
1016 // Handle double clicks, unless on status line
1017 else if (in_status_line)
1018 {
1019#ifdef FEAT_MOUSESHAPE
1020 if ((is_drag || is_click) && !drag_status_line)
1021 {
1022 drag_status_line = TRUE;
1023 update_mouseshape(-1);
1024 }
1025#endif
1026 }
1027 else if (in_sep_line)
1028 {
1029#ifdef FEAT_MOUSESHAPE
1030 if ((is_drag || is_click) && !drag_sep_line)
1031 {
1032 drag_sep_line = TRUE;
1033 update_mouseshape(-1);
1034 }
1035#endif
1036 }
Bram Moolenaar24959102022-05-07 20:01:16 +01001037 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
1038 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001039 && mouse_has(MOUSE_VISUAL))
1040 {
1041 if (is_click || !VIsual_active)
1042 {
1043 if (VIsual_active)
1044 orig_cursor = VIsual;
1045 else
1046 {
1047 check_visual_highlight();
1048 VIsual = curwin->w_cursor;
1049 orig_cursor = VIsual;
1050 VIsual_active = TRUE;
1051 VIsual_reselect = TRUE;
1052 // start Select mode if 'selectmode' contains "mouse"
1053 may_start_select('o');
1054 setmouse();
1055 }
1056 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1057 {
1058 // Double click with ALT pressed makes it blockwise.
1059 if (mod_mask & MOD_MASK_ALT)
1060 VIsual_mode = Ctrl_V;
1061 else
1062 VIsual_mode = 'v';
1063 }
1064 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
1065 VIsual_mode = 'V';
1066 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
1067 VIsual_mode = Ctrl_V;
1068#ifdef FEAT_CLIPBOARD
1069 // Make sure the clipboard gets updated. Needed because start and
1070 // end may still be the same, and the selection needs to be owned
1071 clip_star.vmode = NUL;
1072#endif
1073 }
1074 // A double click selects a word or a block.
1075 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1076 {
1077 pos_T *pos = NULL;
1078 int gc;
1079
1080 if (is_click)
1081 {
1082 // If the character under the cursor (skipping white space) is
1083 // not a word character, try finding a match and select a (),
1084 // {}, [], #if/#endif, etc. block.
1085 end_visual = curwin->w_cursor;
1086 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
1087 inc(&end_visual);
1088 if (oap != NULL)
1089 oap->motion_type = MCHAR;
1090 if (oap != NULL
1091 && VIsual_mode == 'v'
1092 && !vim_iswordc(gchar_pos(&end_visual))
1093 && EQUAL_POS(curwin->w_cursor, VIsual)
1094 && (pos = findmatch(oap, NUL)) != NULL)
1095 {
1096 curwin->w_cursor = *pos;
1097 if (oap->motion_type == MLINE)
1098 VIsual_mode = 'V';
1099 else if (*p_sel == 'e')
1100 {
1101 if (LT_POS(curwin->w_cursor, VIsual))
1102 ++VIsual.col;
1103 else
1104 ++curwin->w_cursor.col;
1105 }
1106 }
1107 }
1108
1109 if (pos == NULL && (is_click || is_drag))
1110 {
1111 // When not found a match or when dragging: extend to include
1112 // a word.
1113 if (LT_POS(curwin->w_cursor, orig_cursor))
1114 {
1115 find_start_of_word(&curwin->w_cursor);
1116 find_end_of_word(&VIsual);
1117 }
1118 else
1119 {
1120 find_start_of_word(&VIsual);
1121 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1122 curwin->w_cursor.col +=
1123 (*mb_ptr2len)(ml_get_cursor());
1124 find_end_of_word(&curwin->w_cursor);
1125 }
1126 }
1127 curwin->w_set_curswant = TRUE;
1128 }
1129 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001130 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001131 }
1132 else if (VIsual_active && !old_active)
1133 {
1134 if (mod_mask & MOD_MASK_ALT)
1135 VIsual_mode = Ctrl_V;
1136 else
1137 VIsual_mode = 'v';
1138 }
1139
1140 // If Visual mode changed show it later.
1141 if ((!VIsual_active && old_active && mode_displayed)
1142 || (VIsual_active && p_smd && msg_silent == 0
1143 && (!old_active || VIsual_mode != old_mode)))
1144 redraw_cmdline = TRUE;
1145
1146 return moved;
1147}
1148
1149 void
1150ins_mouse(int c)
1151{
1152 pos_T tpos;
1153 win_T *old_curwin = curwin;
1154
Christopher Plewright696d0a82022-11-18 17:53:34 +00001155#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001156 // When GUI is active, also move/paste when 'mouse' is empty
1157 if (!gui.in_use)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001158#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001159 if (!mouse_has(MOUSE_INSERT))
1160 return;
1161
1162 undisplay_dollar();
1163 tpos = curwin->w_cursor;
1164 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1165 {
1166 win_T *new_curwin = curwin;
1167
1168 if (curwin != old_curwin && win_valid(old_curwin))
1169 {
1170 // Mouse took us to another window. We need to go back to the
1171 // previous one to stop insert there properly.
1172 curwin = old_curwin;
1173 curbuf = curwin->w_buffer;
1174#ifdef FEAT_JOB_CHANNEL
1175 if (bt_prompt(curbuf))
1176 // Restart Insert mode when re-entering the prompt buffer.
1177 curbuf->b_prompt_insert = 'A';
1178#endif
1179 }
1180 start_arrow(curwin == old_curwin ? &tpos : NULL);
1181 if (curwin != new_curwin && win_valid(new_curwin))
1182 {
1183 curwin = new_curwin;
1184 curbuf = curwin->w_buffer;
1185 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001186 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001187 }
1188
1189 // redraw status lines (in case another window became active)
1190 redraw_statuslines();
1191}
1192
Christopher Plewright44c22092022-11-15 17:43:36 +00001193/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001194 * Common mouse wheel scrolling, shared between Insert mode and NV modes.
1195 * Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
1196 * depending on the scroll direction) or one page when Shift or Ctrl is used.
1197 * Direction is indicated by "cap->arg":
1198 * K_MOUSEUP - MSCR_UP
1199 * K_MOUSEDOWN - MSCR_DOWN
1200 * K_MOUSELEFT - MSCR_LEFT
1201 * K_MOUSERIGHT - MSCR_RIGHT
1202 * "curwin" may have been changed to the window that should be scrolled and
1203 * differ from the window that actually has focus.
1204 */
1205 static void
1206do_mousescroll(cmdarg_T *cap)
1207{
1208 int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
1209
1210#ifdef FEAT_TERMINAL
1211 if (term_use_loop())
1212 // This window is a terminal window, send the mouse event there.
1213 // Set "typed" to FALSE to avoid an endless loop.
1214 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
1215 else
1216#endif
1217 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1218 {
1219 // Vertical scrolling
1220 if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
1221 {
1222 // whole page up or down
Luuk van Baal5a2e3ec2024-03-28 10:07:29 +01001223 pagescroll(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L, FALSE);
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001224 }
1225 else
1226 {
1227 if (mouse_vert_step < 0 || shift_or_ctrl)
1228 {
1229 // whole page up or down
1230 cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
1231 }
1232 // Don't scroll more than half the window height.
1233 else if (curwin->w_height < mouse_vert_step * 2)
1234 {
1235 cap->count1 = curwin->w_height / 2;
1236 if (cap->count1 == 0)
1237 cap->count1 = 1;
1238 }
1239 else
1240 {
1241 cap->count1 = mouse_vert_step;
1242 }
1243 cap->count0 = cap->count1;
1244 nv_scroll_line(cap);
1245 }
1246
1247#ifdef FEAT_PROP_POPUP
1248 if (WIN_IS_POPUP(curwin))
1249 popup_set_firstline(curwin);
1250#endif
1251 }
1252 else
1253 {
1254 // Horizontal scrolling
1255 long step = (mouse_hor_step < 0 || shift_or_ctrl)
1256 ? curwin->w_width : mouse_hor_step;
1257 long leftcol = curwin->w_leftcol
1258 + (cap->arg == MSCR_RIGHT ? -step : step);
1259 if (leftcol < 0)
1260 leftcol = 0;
1261 do_mousescroll_horiz((long_u)leftcol);
1262 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001263 may_trigger_win_scrolled_resized();
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001264}
1265
1266/*
1267 * Insert mode implementation for scrolling in direction "dir", which is
1268 * one of the MSCR_ values.
Christopher Plewright44c22092022-11-15 17:43:36 +00001269 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001270 void
1271ins_mousescroll(int dir)
1272{
Christopher Plewright696d0a82022-11-18 17:53:34 +00001273 cmdarg_T cap;
1274 oparg_T oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001275 CLEAR_FIELD(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00001276 clear_oparg(&oa);
1277 cap.oap = &oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001278 cap.arg = dir;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001279
Christopher Plewright44c22092022-11-15 17:43:36 +00001280 switch (dir)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001281 {
Christopher Plewright44c22092022-11-15 17:43:36 +00001282 case MSCR_UP:
1283 cap.cmdchar = K_MOUSEUP;
1284 break;
1285 case MSCR_DOWN:
1286 cap.cmdchar = K_MOUSEDOWN;
1287 break;
1288 case MSCR_LEFT:
1289 cap.cmdchar = K_MOUSELEFT;
1290 break;
1291 case MSCR_RIGHT:
1292 cap.cmdchar = K_MOUSERIGHT;
1293 break;
1294 default:
1295 siemsg("Invalid ins_mousescroll() argument: %d", dir);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001296 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00001297
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001298 win_T *old_curwin = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001299 if (mouse_row >= 0 && mouse_col >= 0)
1300 {
1301 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001302 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00001303 int row = mouse_row;
1304 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001305 curwin = mouse_find_win(&row, &col, FIND_POPUP);
1306 if (curwin == NULL)
1307 {
1308 curwin = old_curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001309 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001310 }
1311 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001312 }
1313
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001314 if (curwin == old_curwin)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001315 {
1316 // Don't scroll the current window if the popup menu is visible.
1317 if (pum_visible())
1318 return;
1319
1320 undisplay_dollar();
1321 }
1322
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001323 linenr_T orig_topline = curwin->w_topline;
1324 colnr_T orig_leftcol = curwin->w_leftcol;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001325 pos_T orig_cursor = curwin->w_cursor;
1326
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001327 // Call the common mouse scroll function shared with other modes.
1328 do_mousescroll(&cap);
1329
1330 int did_scroll = (orig_topline != curwin->w_topline
1331 || orig_leftcol != curwin->w_leftcol);
1332
1333 curwin->w_redr_status = TRUE;
1334 curwin = old_curwin;
1335 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001336
1337 // If the window actually scrolled and the popup menu may overlay the
1338 // window, need to redraw it.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001339 if (did_scroll && pum_visible())
Christopher Plewright696d0a82022-11-18 17:53:34 +00001340 {
1341 // TODO: Would be more efficient to only redraw the windows that are
1342 // overlapped by the popup menu.
1343 redraw_all_later(UPD_NOT_VALID);
1344 ins_compl_show_pum();
1345 }
1346
1347 if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
1348 {
1349 start_arrow(&orig_cursor);
1350 set_can_cindent(TRUE);
1351 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001352}
1353
1354/*
1355 * Return TRUE if "c" is a mouse key.
1356 */
1357 int
1358is_mouse_key(int c)
1359{
1360 return c == K_LEFTMOUSE
1361 || c == K_LEFTMOUSE_NM
1362 || c == K_LEFTDRAG
1363 || c == K_LEFTRELEASE
1364 || c == K_LEFTRELEASE_NM
1365 || c == K_MOUSEMOVE
1366 || c == K_MIDDLEMOUSE
1367 || c == K_MIDDLEDRAG
1368 || c == K_MIDDLERELEASE
1369 || c == K_RIGHTMOUSE
1370 || c == K_RIGHTDRAG
1371 || c == K_RIGHTRELEASE
1372 || c == K_MOUSEDOWN
1373 || c == K_MOUSEUP
1374 || c == K_MOUSELEFT
1375 || c == K_MOUSERIGHT
1376 || c == K_X1MOUSE
1377 || c == K_X1DRAG
1378 || c == K_X1RELEASE
1379 || c == K_X2MOUSE
1380 || c == K_X2DRAG
1381 || c == K_X2RELEASE;
1382}
1383
1384static struct mousetable
1385{
1386 int pseudo_code; // Code for pseudo mouse event
1387 int button; // Which mouse button is it?
1388 int is_click; // Is it a mouse button click event?
1389 int is_drag; // Is it a mouse drag event?
1390} mouse_table[] =
1391{
1392 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1393#ifdef FEAT_GUI
1394 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1395#endif
1396 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1397 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1398#ifdef FEAT_GUI
1399 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1400#endif
1401 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1402 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1403 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1404 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1405 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1406 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1407 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1408 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1409 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1410 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1411 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1412 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1413 // DRAG without CLICK
1414 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1415 // RELEASE without CLICK
1416 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1417 {0, 0, 0, 0},
1418};
1419
1420/*
1421 * Look up the given mouse code to return the relevant information in the other
1422 * arguments. Return which button is down or was released.
1423 */
1424 int
1425get_mouse_button(int code, int *is_click, int *is_drag)
1426{
1427 int i;
1428
1429 for (i = 0; mouse_table[i].pseudo_code; i++)
1430 if (code == mouse_table[i].pseudo_code)
1431 {
1432 *is_click = mouse_table[i].is_click;
1433 *is_drag = mouse_table[i].is_drag;
1434 return mouse_table[i].button;
1435 }
1436 return 0; // Shouldn't get here
1437}
1438
1439/*
1440 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1441 * the given information about which mouse button is down, and whether the
1442 * mouse was clicked, dragged or released.
1443 */
1444 int
1445get_pseudo_mouse_code(
1446 int button, // eg MOUSE_LEFT
1447 int is_click,
1448 int is_drag)
1449{
1450 int i;
1451
1452 for (i = 0; mouse_table[i].pseudo_code; i++)
1453 if (button == mouse_table[i].button
1454 && is_click == mouse_table[i].is_click
1455 && is_drag == mouse_table[i].is_drag)
1456 {
1457#ifdef FEAT_GUI
1458 // Trick: a non mappable left click and release has mouse_col -1
1459 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1460 // gui_mouse_moved()
1461 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1462 {
1463 if (mouse_col < 0)
1464 mouse_col = 0;
1465 else
1466 mouse_col -= MOUSE_COLOFF;
1467 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1468 return (int)KE_LEFTMOUSE_NM;
1469 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1470 return (int)KE_LEFTRELEASE_NM;
1471 }
1472#endif
1473 return mouse_table[i].pseudo_code;
1474 }
1475 return (int)KE_IGNORE; // not recognized, ignore it
1476}
1477
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001478# define HMT_NORMAL 1
1479# define HMT_NETTERM 2
1480# define HMT_DEC 4
1481# define HMT_JSBTERM 8
1482# define HMT_PTERM 16
1483# define HMT_URXVT 32
1484# define HMT_GPM 64
1485# define HMT_SGR 128
1486# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001487static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001488
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001489 void
1490set_mouse_termcode(
1491 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1492 char_u *s)
1493{
1494 char_u name[2];
1495
1496 name[0] = n;
1497 name[1] = KE_FILLER;
1498 add_termcode(name, s, FALSE);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001499#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001500 if (n == KS_JSBTERM_MOUSE)
1501 has_mouse_termcode |= HMT_JSBTERM;
1502 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001503#endif
1504#ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001505 if (n == KS_NETTERM_MOUSE)
1506 has_mouse_termcode |= HMT_NETTERM;
1507 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001508#endif
1509#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001510 if (n == KS_DEC_MOUSE)
1511 has_mouse_termcode |= HMT_DEC;
1512 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001513#endif
1514#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001515 if (n == KS_PTERM_MOUSE)
1516 has_mouse_termcode |= HMT_PTERM;
1517 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001518#endif
1519#ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001520 if (n == KS_URXVT_MOUSE)
1521 has_mouse_termcode |= HMT_URXVT;
1522 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001523#endif
1524#ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001525 if (n == KS_GPM_MOUSE)
1526 has_mouse_termcode |= HMT_GPM;
1527 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001528#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001529 if (n == KS_SGR_MOUSE)
1530 has_mouse_termcode |= HMT_SGR;
1531 else if (n == KS_SGR_MOUSE_RELEASE)
1532 has_mouse_termcode |= HMT_SGR_REL;
1533 else
1534 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001535}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001536
Christopher Plewright696d0a82022-11-18 17:53:34 +00001537#if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001538 void
1539del_mouse_termcode(
1540 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1541{
1542 char_u name[2];
1543
1544 name[0] = n;
1545 name[1] = KE_FILLER;
1546 del_termcode(name);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001547# ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001548 if (n == KS_JSBTERM_MOUSE)
1549 has_mouse_termcode &= ~HMT_JSBTERM;
1550 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001551# endif
1552# ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001553 if (n == KS_NETTERM_MOUSE)
1554 has_mouse_termcode &= ~HMT_NETTERM;
1555 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001556# endif
1557# ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001558 if (n == KS_DEC_MOUSE)
1559 has_mouse_termcode &= ~HMT_DEC;
1560 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001561# endif
1562# ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001563 if (n == KS_PTERM_MOUSE)
1564 has_mouse_termcode &= ~HMT_PTERM;
1565 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001566# endif
1567# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001568 if (n == KS_URXVT_MOUSE)
1569 has_mouse_termcode &= ~HMT_URXVT;
1570 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001571# endif
1572# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001573 if (n == KS_GPM_MOUSE)
1574 has_mouse_termcode &= ~HMT_GPM;
1575 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001576# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001577 if (n == KS_SGR_MOUSE)
1578 has_mouse_termcode &= ~HMT_SGR;
1579 else if (n == KS_SGR_MOUSE_RELEASE)
1580 has_mouse_termcode &= ~HMT_SGR_REL;
1581 else
1582 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001583}
Christopher Plewright696d0a82022-11-18 17:53:34 +00001584#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001585
1586/*
1587 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1588 */
1589 void
1590setmouse(void)
1591{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001592 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001593
Christopher Plewright696d0a82022-11-18 17:53:34 +00001594#ifdef FEAT_MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001595 update_mouseshape(-1);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001596#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001597
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001598 // Should be outside proc, but may break MOUSESHAPE
Christopher Plewright696d0a82022-11-18 17:53:34 +00001599#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001600 // In the GUI the mouse is always enabled.
1601 if (gui.in_use)
1602 return;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001603#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001604 // be quick when mouse is off
1605 if (*p_mouse == NUL || has_mouse_termcode == 0)
1606 return;
1607
1608 // don't switch mouse on when not in raw mode (Ex mode)
1609 if (cur_tmode != TMODE_RAW)
1610 {
1611 mch_setmouse(FALSE);
1612 return;
1613 }
1614
1615 if (VIsual_active)
1616 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001617 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1618 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001619 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001620 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001621 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001622 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001623 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001624 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001625 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1626 else
1627 checkfor = MOUSE_NORMAL; // assume normal mode
1628
1629 if (mouse_has(checkfor))
1630 mch_setmouse(TRUE);
1631 else
1632 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001633}
1634
1635/*
1636 * Return TRUE if
1637 * - "c" is in 'mouse', or
1638 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1639 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1640 * normal editing mode (not at hit-return message).
1641 */
1642 int
1643mouse_has(int c)
1644{
1645 char_u *p;
1646
1647 for (p = p_mouse; *p; ++p)
1648 switch (*p)
1649 {
1650 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1651 return TRUE;
1652 break;
1653 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1654 return TRUE;
1655 break;
1656 default: if (c == *p) return TRUE; break;
1657 }
1658 return FALSE;
1659}
1660
1661/*
1662 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1663 */
1664 int
1665mouse_model_popup(void)
1666{
1667 return (p_mousem[0] == 'p');
1668}
1669
zeertzjq8e0ccb62022-11-01 18:35:27 +00001670static win_T *dragwin = NULL; // window being dragged
1671
1672/*
1673 * Reset the window being dragged. To be called when switching tab page.
1674 */
1675 void
1676reset_dragwin(void)
1677{
1678 dragwin = NULL;
1679}
1680
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001681/*
1682 * Move the cursor to the specified row and column on the screen.
1683 * Change current window if necessary. Returns an integer with the
1684 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1685 *
1686 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1687 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1688 *
1689 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1690 * if the mouse is outside the window then the text will scroll, or if the
1691 * mouse was previously on a status line, then the status line may be dragged.
1692 *
1693 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1694 * cursor is moved unless the cursor was on a status line.
1695 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1696 * IN_SEP_LINE depending on where the cursor was clicked.
1697 *
1698 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1699 * the mouse is on the status line of the same window.
1700 *
1701 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1702 * the last call.
1703 *
1704 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1705 * remembered.
1706 */
1707 int
1708jump_to_mouse(
1709 int flags,
1710 int *inclusive, // used for inclusive operator, can be NULL
1711 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1712{
1713 static int on_status_line = 0; // #lines below bottom of window
1714 static int on_sep_line = 0; // on separator right of window
1715#ifdef FEAT_MENU
1716 static int in_winbar = FALSE;
1717#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001718#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001719 static int in_popup_win = FALSE;
1720 static win_T *click_in_popup_win = NULL;
1721#endif
1722 static int prev_row = -1;
1723 static int prev_col = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001724 static int did_drag = FALSE; // drag was noticed
1725
1726 win_T *wp, *old_curwin;
1727 pos_T old_cursor;
1728 int count;
1729 int first;
1730 int row = mouse_row;
1731 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001732 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001733#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001734 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001735#endif
1736
1737 mouse_past_bottom = FALSE;
1738 mouse_past_eol = FALSE;
1739
1740 if (flags & MOUSE_RELEASED)
1741 {
1742 // On button release we may change window focus if positioned on a
1743 // status line and no dragging happened.
1744 if (dragwin != NULL && !did_drag)
1745 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1746 dragwin = NULL;
1747 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001748#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001749 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1750 popup_close_for_mouse_click(click_in_popup_win);
1751
1752 popup_dragwin = NULL;
1753 click_in_popup_win = NULL;
1754#endif
1755 }
1756
1757 if ((flags & MOUSE_DID_MOVE)
1758 && prev_row == mouse_row
1759 && prev_col == mouse_col)
1760 {
1761retnomove:
1762 // before moving the cursor for a left click which is NOT in a status
1763 // line, stop Visual mode
1764 if (on_status_line)
1765 return IN_STATUS_LINE;
1766 if (on_sep_line)
1767 return IN_SEP_LINE;
1768#ifdef FEAT_MENU
1769 if (in_winbar)
1770 {
1771 // A quick second click may arrive as a double-click, but we use it
1772 // as a second click in the WinBar.
1773 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1774 {
1775 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1776 if (wp == NULL)
1777 return IN_UNKNOWN;
1778 winbar_click(wp, col);
1779 }
1780 return IN_OTHER_WIN | MOUSE_WINBAR;
1781 }
1782#endif
1783 if (flags & MOUSE_MAY_STOP_VIS)
1784 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001785 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001786 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001787 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001788#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001789 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01001790 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001791 return IN_OTHER_WIN;
1792#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001793#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001794 // Continue a modeless selection in a popup window or dragging it.
1795 if (in_popup_win)
1796 {
1797 click_in_popup_win = NULL; // don't close it on release
1798 if (popup_dragwin != NULL)
1799 {
1800 // dragging a popup window
1801 popup_drag(popup_dragwin);
1802 return IN_UNKNOWN;
1803 }
1804 return IN_OTHER_WIN;
1805 }
1806#endif
1807 return IN_BUFFER;
1808 }
1809
1810 prev_row = mouse_row;
1811 prev_col = mouse_col;
1812
1813 if (flags & MOUSE_SETPOS)
1814 goto retnomove; // ugly goto...
1815
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001816 old_curwin = curwin;
1817 old_cursor = curwin->w_cursor;
1818
1819 if (!(flags & MOUSE_FOCUS))
1820 {
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02001821 if (row < 0 || col < 0) // check if it makes sense
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001822 return IN_UNKNOWN;
1823
1824 // find the window where the row is in and adjust "row" and "col" to be
1825 // relative to top-left of the window
1826 wp = mouse_find_win(&row, &col, FIND_POPUP);
1827 if (wp == NULL)
1828 return IN_UNKNOWN;
1829 dragwin = NULL;
1830
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001831#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001832 // Click in a popup window may start dragging or modeless selection,
1833 // but not much else.
1834 if (WIN_IS_POPUP(wp))
1835 {
1836 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001837 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001838 in_popup_win = TRUE;
1839 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1840 {
1841 return IN_UNKNOWN;
1842 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001843 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001844 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001845 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001846 {
1847 popup_dragwin = wp;
1848 popup_start_drag(wp, row, col);
1849 return IN_UNKNOWN;
1850 }
1851 // Only close on release, otherwise it's not possible to drag or do
1852 // modeless selection.
1853 else if (wp->w_popup_close == POPCLOSE_CLICK
1854 && which_button == MOUSE_LEFT)
1855 {
1856 click_in_popup_win = wp;
1857 }
1858 else if (which_button == MOUSE_LEFT)
1859 // If the click is in the scrollbar, may scroll up/down.
1860 popup_handle_scrollbar_click(wp, row, col);
1861# ifdef FEAT_CLIPBOARD
1862 return IN_OTHER_WIN;
1863# else
1864 return IN_UNKNOWN;
1865# endif
1866 }
1867 in_popup_win = FALSE;
1868 popup_dragwin = NULL;
1869#endif
1870#ifdef FEAT_MENU
1871 if (row == -1)
1872 {
1873 // A click in the window toolbar does not enter another window or
1874 // change Visual highlighting.
1875 winbar_click(wp, col);
1876 in_winbar = TRUE;
1877 return IN_OTHER_WIN | MOUSE_WINBAR;
1878 }
1879 in_winbar = FALSE;
1880#endif
1881
1882 // winpos and height may change in win_enter()!
1883 if (row >= wp->w_height) // In (or below) status line
1884 {
1885 on_status_line = row - wp->w_height + 1;
1886 dragwin = wp;
1887 }
1888 else
1889 on_status_line = 0;
1890 if (col >= wp->w_width) // In separator line
1891 {
1892 on_sep_line = col - wp->w_width + 1;
1893 dragwin = wp;
1894 }
1895 else
1896 on_sep_line = 0;
1897
1898 // The rightmost character of the status line might be a vertical
1899 // separator character if there is no connecting window to the right.
1900 if (on_status_line && on_sep_line)
1901 {
1902 if (stl_connected(wp))
1903 on_sep_line = 0;
1904 else
1905 on_status_line = 0;
1906 }
1907
1908 // Before jumping to another buffer, or moving the cursor for a left
1909 // click, stop Visual mode.
1910 if (VIsual_active
1911 && (wp->w_buffer != curwin->w_buffer
1912 || (!on_status_line && !on_sep_line
1913#ifdef FEAT_FOLDING
1914 && (
1915# ifdef FEAT_RIGHTLEFT
1916 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1917# endif
Sean Dewar988f7432023-08-16 14:17:36 +01001918 col >= wp->w_p_fdc + (wp != cmdwin_win ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001919 )
1920#endif
1921 && (flags & MOUSE_MAY_STOP_VIS))))
1922 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001923 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001924 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001925 }
Sean Dewar988f7432023-08-16 14:17:36 +01001926 if (cmdwin_type != 0 && wp != cmdwin_win)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001927 {
1928 // A click outside the command-line window: Use modeless
1929 // selection if possible. Allow dragging the status lines.
1930 on_sep_line = 0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001931#ifdef FEAT_CLIPBOARD
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001932 if (on_status_line)
1933 return IN_STATUS_LINE;
1934 return IN_OTHER_WIN;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001935#else
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001936 row = 0;
1937 col += wp->w_wincol;
Sean Dewar988f7432023-08-16 14:17:36 +01001938 wp = cmdwin_win;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001939#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001940 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001941#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1942 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1943 // terminal in popup window: don't jump to another window
1944 return IN_OTHER_WIN;
1945#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001946 // Only change window focus when not clicking on or dragging the
1947 // status line. Do change focus when releasing the mouse button
1948 // (MOUSE_FOCUS was set above if we dragged first).
1949 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1950 win_enter(wp, TRUE); // can make wp invalid!
1951
1952 if (curwin != old_curwin)
1953 {
1954#ifdef CHECK_DOUBLE_CLICK
1955 // set topline, to be able to check for double click ourselves
1956 set_mouse_topline(curwin);
1957#endif
1958#ifdef FEAT_TERMINAL
1959 // when entering a terminal window may change state
1960 term_win_entered();
1961#endif
1962 }
1963 if (on_status_line) // In (or below) status line
1964 {
1965 // Don't use start_arrow() if we're in the same window
1966 if (curwin == old_curwin)
1967 return IN_STATUS_LINE;
1968 else
1969 return IN_STATUS_LINE | CURSOR_MOVED;
1970 }
1971 if (on_sep_line) // In (or below) status line
1972 {
1973 // Don't use start_arrow() if we're in the same window
1974 if (curwin == old_curwin)
1975 return IN_SEP_LINE;
1976 else
1977 return IN_SEP_LINE | CURSOR_MOVED;
1978 }
1979
1980 curwin->w_cursor.lnum = curwin->w_topline;
1981#ifdef FEAT_GUI
1982 // remember topline, needed for double click
1983 gui_prev_topline = curwin->w_topline;
1984# ifdef FEAT_DIFF
1985 gui_prev_topfill = curwin->w_topfill;
1986# endif
1987#endif
1988 }
1989 else if (on_status_line && which_button == MOUSE_LEFT)
1990 {
1991 if (dragwin != NULL)
1992 {
1993 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001994 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001995 - on_status_line;
1996 win_drag_status_line(dragwin, count);
1997 did_drag |= count;
1998 }
1999 return IN_STATUS_LINE; // Cursor didn't move
2000 }
2001 else if (on_sep_line && which_button == MOUSE_LEFT)
2002 {
2003 if (dragwin != NULL)
2004 {
2005 // Drag the separator column
2006 count = col - dragwin->w_wincol - dragwin->w_width + 1
2007 - on_sep_line;
2008 win_drag_vsep_line(dragwin, count);
2009 did_drag |= count;
2010 }
2011 return IN_SEP_LINE; // Cursor didn't move
2012 }
2013#ifdef FEAT_MENU
2014 else if (in_winbar)
2015 {
2016 // After a click on the window toolbar don't start Visual mode.
2017 return IN_OTHER_WIN | MOUSE_WINBAR;
2018 }
2019#endif
2020 else // keep_window_focus must be TRUE
2021 {
2022 // before moving the cursor for a left click, stop Visual mode
2023 if (flags & MOUSE_MAY_STOP_VIS)
2024 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02002025 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002026 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002027 }
2028
Martin Tournoij7904fa42022-10-04 16:28:45 +01002029#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002030 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01002031 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002032 return IN_OTHER_WIN;
2033#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002034#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002035 if (in_popup_win)
2036 {
2037 if (popup_dragwin != NULL)
2038 {
2039 // dragging a popup window
2040 popup_drag(popup_dragwin);
2041 return IN_UNKNOWN;
2042 }
2043 // continue a modeless selection in a popup window
2044 click_in_popup_win = NULL;
2045 return IN_OTHER_WIN;
2046 }
2047#endif
2048
2049 row -= W_WINROW(curwin);
2050 col -= curwin->w_wincol;
2051
2052 // When clicking beyond the end of the window, scroll the screen.
2053 // Scroll by however many rows outside the window we are.
2054 if (row < 0)
2055 {
2056 count = 0;
2057 for (first = TRUE; curwin->w_topline > 1; )
2058 {
2059#ifdef FEAT_DIFF
2060 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2061 ++count;
2062 else
2063#endif
2064 count += plines(curwin->w_topline - 1);
2065 if (!first && count > -row)
2066 break;
2067 first = FALSE;
2068#ifdef FEAT_FOLDING
2069 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2070#endif
2071#ifdef FEAT_DIFF
2072 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2073 ++curwin->w_topfill;
2074 else
2075#endif
2076 {
2077 --curwin->w_topline;
2078#ifdef FEAT_DIFF
2079 curwin->w_topfill = 0;
2080#endif
2081 }
2082 }
2083#ifdef FEAT_DIFF
2084 check_topfill(curwin, FALSE);
2085#endif
2086 curwin->w_valid &=
2087 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002088 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002089 row = 0;
2090 }
2091 else if (row >= curwin->w_height)
2092 {
2093 count = 0;
2094 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
2095 {
2096#ifdef FEAT_DIFF
2097 if (curwin->w_topfill > 0)
2098 ++count;
2099 else
2100#endif
2101 count += plines(curwin->w_topline);
2102 if (!first && count > row - curwin->w_height + 1)
2103 break;
2104 first = FALSE;
2105#ifdef FEAT_FOLDING
2106 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
2107 && curwin->w_topline == curbuf->b_ml.ml_line_count)
2108 break;
2109#endif
2110#ifdef FEAT_DIFF
2111 if (curwin->w_topfill > 0)
2112 --curwin->w_topfill;
2113 else
2114#endif
2115 {
2116 ++curwin->w_topline;
2117#ifdef FEAT_DIFF
2118 curwin->w_topfill =
2119 diff_check_fill(curwin, curwin->w_topline);
2120#endif
2121 }
2122 }
2123#ifdef FEAT_DIFF
2124 check_topfill(curwin, FALSE);
2125#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002126 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002127 curwin->w_valid &=
2128 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2129 row = curwin->w_height - 1;
2130 }
2131 else if (row == 0)
2132 {
2133 // When dragging the mouse, while the text has been scrolled up as
2134 // far as it goes, moving the mouse in the top line should scroll
2135 // the text down (done later when recomputing w_topline).
2136 if (mouse_dragging > 0
2137 && curwin->w_cursor.lnum
2138 == curwin->w_buffer->b_ml.ml_line_count
2139 && curwin->w_cursor.lnum == curwin->w_topline)
2140 curwin->w_valid &= ~(VALID_TOPLINE);
2141 }
2142 }
2143
zeertzjqec149242023-12-19 20:28:31 +01002144 if (prev_row >= W_WINROW(curwin)
2145 && prev_row < W_WINROW(curwin) + curwin->w_height
2146 && prev_col >= curwin->w_wincol && prev_col < W_ENDCOL(curwin)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002147 && ScreenLines != NULL)
2148 {
2149 int off = LineOffset[prev_row] + prev_col;
2150
2151 // Only use ScreenCols[] after the window was redrawn. Mainly matters
2152 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01002153 // Do not use when 'virtualedit' is active.
zeertzjqe500ae82023-08-17 22:35:26 +02002154 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002155 col_from_screen = ScreenCols[off];
2156#ifdef FEAT_FOLDING
2157 // Remember the character under the mouse, it might be a '-' or '+' in
2158 // the fold column.
2159 mouse_char = ScreenLines[off];
2160#endif
2161 }
2162
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002163#ifdef FEAT_FOLDING
2164 // Check for position outside of the fold column.
2165 if (
2166# ifdef FEAT_RIGHTLEFT
2167 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
2168# endif
Sean Dewar988f7432023-08-16 14:17:36 +01002169 col >= curwin->w_p_fdc + (cmdwin_win != curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002170 )
2171 mouse_char = ' ';
2172#endif
2173
2174 // compute the position in the buffer line from the posn on the screen
2175 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
2176 mouse_past_bottom = TRUE;
2177
2178 // Start Visual mode before coladvance(), for when 'sel' != "old"
2179 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2180 {
2181 check_visual_highlight();
2182 VIsual = old_cursor;
2183 VIsual_active = TRUE;
2184 VIsual_reselect = TRUE;
2185 // if 'selectmode' contains "mouse", start Select mode
2186 may_start_select('o');
2187 setmouse();
2188 if (p_smd && msg_silent == 0)
2189 redraw_cmdline = TRUE; // show visual mode later
2190 }
2191
zeertzjqd0c1b772024-03-16 15:03:33 +01002192 if (col_from_screen >= 0)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002193 {
zeertzjqe500ae82023-08-17 22:35:26 +02002194 // Use the virtual column from ScreenCols[], it is accurate also after
2195 // concealed characters.
2196 col = col_from_screen;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002197 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002198
zeertzjqe500ae82023-08-17 22:35:26 +02002199 curwin->w_curswant = col;
2200 curwin->w_set_curswant = FALSE; // May still have been TRUE
2201 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2202 {
2203 if (inclusive != NULL)
2204 *inclusive = TRUE;
2205 mouse_past_eol = TRUE;
2206 }
2207 else if (inclusive != NULL)
2208 *inclusive = FALSE;
2209
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002210 count = IN_BUFFER;
2211 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2212 || curwin->w_cursor.col != old_cursor.col)
2213 count |= CURSOR_MOVED; // Cursor has moved
2214
Christopher Plewright696d0a82022-11-18 17:53:34 +00002215#ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002216 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002217 count |= MOUSE_FOLD_OPEN;
2218 else if (mouse_char != ' ')
2219 count |= MOUSE_FOLD_CLOSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002220#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002221
2222 return count;
2223}
2224
2225/*
Christopher Plewright44c22092022-11-15 17:43:36 +00002226 * Make a horizontal scroll to "leftcol".
2227 * Return TRUE if the cursor moved, FALSE otherwise.
2228 */
2229 int
2230do_mousescroll_horiz(long_u leftcol)
2231{
2232 if (curwin->w_p_wrap)
Christian Brabandtee17b6f2023-09-09 11:23:50 +02002233 return FALSE; // no horizontal scrolling when wrapping
Christopher Plewright44c22092022-11-15 17:43:36 +00002234
2235 if (curwin->w_leftcol == (colnr_T)leftcol)
2236 return FALSE; // already there
2237
Christopher Plewright44c22092022-11-15 17:43:36 +00002238 // When the line of the cursor is too short, move the cursor to the
2239 // longest visible line.
2240 if (
2241#ifdef FEAT_GUI
2242 (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
2243#endif
2244 !virtual_active()
2245 && (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
2246 {
2247 curwin->w_cursor.lnum = ui_find_longest_lnum();
2248 curwin->w_cursor.col = 0;
2249 }
2250
Bram Moolenaar0c34d562022-11-18 14:07:20 +00002251 return set_leftcol((colnr_T)leftcol);
Christopher Plewright44c22092022-11-15 17:43:36 +00002252}
2253
2254/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002255 * Normal and Visual modes implementation for scrolling in direction
2256 * "cap->arg", which is one of the MSCR_ values.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002257 */
2258 void
Christopher Plewright696d0a82022-11-18 17:53:34 +00002259nv_mousescroll(cmdarg_T *cap)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002260{
Christopher Plewright696d0a82022-11-18 17:53:34 +00002261 win_T *old_curwin = curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002262
2263 if (mouse_row >= 0 && mouse_col >= 0)
2264 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002265 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002266 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00002267 int row = mouse_row;
2268 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002269 curwin = mouse_find_win(&row, &col, FIND_POPUP);
2270 if (curwin == NULL)
2271 {
2272 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002273 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002274 }
2275
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002276#ifdef FEAT_PROP_POPUP
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002277 if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
2278 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002279 // cannot scroll this popup window
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002280 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002281 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002282 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002283#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002284 curbuf = curwin->w_buffer;
2285 }
Christopher Plewright44c22092022-11-15 17:43:36 +00002286
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002287 // Call the common mouse scroll function shared with other modes.
2288 do_mousescroll(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00002289
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002290 curwin->w_redr_status = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002291 curwin = old_curwin;
2292 curbuf = curwin->w_buffer;
2293}
2294
2295/*
2296 * Mouse clicks and drags.
2297 */
2298 void
2299nv_mouse(cmdarg_T *cap)
2300{
2301 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2302}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002303
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002304static int held_button = MOUSE_RELEASE;
2305
2306 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00002307reset_held_button(void)
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002308{
2309 held_button = MOUSE_RELEASE;
2310}
2311
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002312/*
2313 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2314 * modifiers found in typebuf in 'modifiers'.
2315 */
2316 int
2317check_termcode_mouse(
2318 char_u *tp,
2319 int *slen,
2320 char_u *key_name,
2321 char_u *modifiers_start,
2322 int idx,
2323 int *modifiers)
2324{
2325 int j;
2326 char_u *p;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002327#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002328 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2329 char_u bytes[6];
2330 int num_bytes;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002331#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002332 int mouse_code = 0; // init for GCC
2333 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002334 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002335 int wheel_code = 0;
2336 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002337 static int orig_num_clicks = 1;
2338 static int orig_mouse_code = 0x0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002339#ifdef CHECK_DOUBLE_CLICK
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002340 static int orig_mouse_col = 0;
2341 static int orig_mouse_row = 0;
2342 static struct timeval orig_mouse_time = {0, 0};
2343 // time of previous mouse click
2344 struct timeval mouse_time; // time of current mouse click
2345 long timediff; // elapsed time in msec
Christopher Plewright696d0a82022-11-18 17:53:34 +00002346#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002347
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002348 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002349
Christopher Plewright696d0a82022-11-18 17:53:34 +00002350#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002351 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2352 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002353# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002354 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002355# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002356 )
2357 {
2358 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002359 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002360 * 0x20 = left button down
2361 * 0x21 = middle button down
2362 * 0x22 = right button down
2363 * 0x23 = any button release
2364 * 0x60 = button 4 down (scroll wheel down)
2365 * 0x61 = button 5 down (scroll wheel up)
2366 * add 0x04 for SHIFT
2367 * add 0x08 for ALT
2368 * add 0x10 for CTRL
2369 * add 0x20 for mouse drag (0x40 is drag with left button)
2370 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2371 * 0x43 (drag + release) is also move
2372 * c == column + ' ' + 1 == column + 33
2373 * r == row + ' ' + 1 == row + 33
2374 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002375 * The coordinates are passed on through global variables. Ugly, but
2376 * this avoids trouble with mouse clicks at an unexpected moment and
2377 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002378 */
2379 for (;;)
2380 {
Christopher Plewright36446bb2022-11-23 22:28:08 +00002381 // For the GUI and for MS-Windows two bytes each are used for row
2382 // and column. Allows for more than 223 columns.
2383# if defined(FEAT_GUI) || defined(MSWIN)
2384 if (TRUE
2385# if defined(FEAT_GUI) && !defined(MSWIN)
2386 && gui.in_use
2387# endif
2388 )
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002389 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002390 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2391 if (num_bytes == -1) // not enough coordinates
2392 return -1;
2393 mouse_code = bytes[0];
2394 mouse_col = 128 * (bytes[1] - ' ' - 1)
2395 + bytes[2] - ' ' - 1;
2396 mouse_row = 128 * (bytes[3] - ' ' - 1)
2397 + bytes[4] - ' ' - 1;
2398 }
2399 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002400# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002401 {
2402 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2403 if (num_bytes == -1) // not enough coordinates
2404 return -1;
2405 mouse_code = bytes[0];
2406 mouse_col = bytes[1] - ' ' - 1;
2407 mouse_row = bytes[2] - ' ' - 1;
2408 }
2409 *slen += num_bytes;
2410
Bram Moolenaar13c04632020-07-12 13:47:42 +02002411 // If the following bytes is also a mouse code and it has the same
2412 // code, dump this one and get the next. This makes dragging a
2413 // whole lot faster.
Christopher Plewright696d0a82022-11-18 17:53:34 +00002414# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002415 if (gui.in_use)
2416 j = 3;
2417 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002418# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002419 j = get_termcode_len(idx);
2420 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2421 && tp[*slen + j] == mouse_code
2422 && tp[*slen + j + 1] != NUL
2423 && tp[*slen + j + 2] != NUL
Christopher Plewright696d0a82022-11-18 17:53:34 +00002424# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002425 && (!gui.in_use
2426 || (tp[*slen + j + 3] != NUL
2427 && tp[*slen + j + 4] != NUL))
Christopher Plewright696d0a82022-11-18 17:53:34 +00002428# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002429 )
2430 *slen += j;
2431 else
2432 break;
2433 }
2434 }
2435
2436 if (key_name[0] == KS_URXVT_MOUSE
2437 || key_name[0] == KS_SGR_MOUSE
2438 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2439 {
2440 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002441 // Almost identical to xterm mouse mode, except the values are decimal
2442 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002443 //
2444 // \033[%d;%d;%dM
2445 // ^-- row
2446 // ^----- column
2447 // ^-------- code
2448 //
2449 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002450 // Almost identical to xterm mouse mode, except the values are decimal
2451 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002452 //
2453 // \033[<%d;%d;%dM
2454 // ^-- row
2455 // ^----- column
2456 // ^-------- code
2457 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002458 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002459 // ^-- row
2460 // ^----- column
2461 // ^-------- code
2462 p = modifiers_start;
2463 if (p == NULL)
2464 return -1;
2465
2466 mouse_code = getdigits(&p);
2467 if (*p++ != ';')
2468 return -1;
2469
2470 // when mouse reporting is SGR, add 32 to mouse code
2471 if (key_name[0] == KS_SGR_MOUSE
2472 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2473 mouse_code += 32;
2474
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002475 mouse_col = getdigits(&p) - 1;
2476 if (*p++ != ';')
2477 return -1;
2478
2479 mouse_row = getdigits(&p) - 1;
2480
Bram Moolenaar13c04632020-07-12 13:47:42 +02002481 // The modifiers were the mouse coordinates, not the modifier keys
2482 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002483 *modifiers = 0;
2484 }
2485
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002486 if (key_name[0] == KS_SGR_MOUSE
2487 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2488 {
2489 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002490 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002491 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002492 // This is used below to set held_button.
2493 mouse_code |= MOUSE_RELEASE;
2494 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002495 }
2496 else
2497 {
2498 release_is_ambiguous = TRUE;
2499 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2500 is_release = TRUE;
2501 }
2502
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002503 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002504# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002505 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002506# endif
2507# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002508 || key_name[0] == KS_URXVT_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002509# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002510 || key_name[0] == KS_SGR_MOUSE
2511 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2512 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002513# if !defined(MSWIN)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002514 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002515 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002516 * Recognize the xterm mouse wheel, but not in the GUI, the
2517 * Linux console with GPM and the MS-DOS or Win32 console
2518 * (multi-clicks use >= 0x60).
2519 */
2520 if (mouse_code >= MOUSEWHEEL_LOW
Christopher Plewright696d0a82022-11-18 17:53:34 +00002521# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002522 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002523# endif
2524# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002525 && key_name[0] != KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002526# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002527 )
2528 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002529# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002530 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2531 // mouse-move event, using MOUSE_DRAG works
2532 mouse_code = MOUSE_DRAG;
2533 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002534# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002535 // Keep the mouse_code before it's changed, so that we
2536 // remember that it was a mouse wheel click.
2537 wheel_code = mouse_code;
2538 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002539# ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002540 else if (held_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002541# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002542 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002543# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002544 && (mouse_code == 0x23 || mouse_code == 0x24
2545 || mouse_code == 0x40 || mouse_code == 0x41))
2546 {
2547 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2548 // And 0x40 and 0x41 are used by some xterm emulator.
2549 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002550 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002551 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002552# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002553
Christopher Plewright696d0a82022-11-18 17:53:34 +00002554# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002555 else if (use_xterm_mouse() > 1)
2556 {
2557 if (mouse_code & MOUSE_DRAG_XTERM)
2558 mouse_code |= MOUSE_DRAG;
2559 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002560# endif
2561# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002562 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2563 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002564 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002565 stop_xterm_trace();
2566 else
2567 start_xterm_trace(mouse_code);
2568 }
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002569# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002570# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002571 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002572#endif // !UNIX || FEAT_MOUSE_XTERM
2573#ifdef FEAT_MOUSE_NET
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002574 if (key_name[0] == KS_NETTERM_MOUSE)
2575 {
2576 int mc, mr;
2577
2578 // expect a rather limited sequence like: balancing {
2579 // \033}6,45\r
2580 // '6' is the row, 45 is the column
2581 p = tp + *slen;
2582 mr = getdigits(&p);
2583 if (*p++ != ',')
2584 return -1;
2585 mc = getdigits(&p);
2586 if (*p++ != '\r')
2587 return -1;
2588
2589 mouse_col = mc - 1;
2590 mouse_row = mr - 1;
2591 mouse_code = MOUSE_LEFT;
2592 *slen += (int)(p - (tp + *slen));
2593 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002594#endif // FEAT_MOUSE_NET
2595#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002596 if (key_name[0] == KS_JSBTERM_MOUSE)
2597 {
2598 int mult, val, iter, button, status;
2599
2600 /*
2601 * JSBTERM Input Model
2602 * \033[0~zw uniq escape sequence
2603 * (L-x) Left button pressed - not pressed x not reporting
2604 * (M-x) Middle button pressed - not pressed x not reporting
2605 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002606 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2607 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002608 * ### X cursor position padded to 3 digits
2609 * ### Y cursor position padded to 3 digits
2610 * (s-x) SHIFT key pressed - not pressed x not reporting
2611 * (c-x) CTRL key pressed - not pressed x not reporting
2612 * \033\\ terminating sequence
2613 */
2614 p = tp + *slen;
2615 button = mouse_code = 0;
2616 switch (*p++)
2617 {
2618 case 'L': button = 1; break;
2619 case '-': break;
2620 case 'x': break; // ignore sequence
2621 default: return -1; // Unknown Result
2622 }
2623 switch (*p++)
2624 {
2625 case 'M': button |= 2; break;
2626 case '-': break;
2627 case 'x': break; // ignore sequence
2628 default: return -1; // Unknown Result
2629 }
2630 switch (*p++)
2631 {
2632 case 'R': button |= 4; break;
2633 case '-': break;
2634 case 'x': break; // ignore sequence
2635 default: return -1; // Unknown Result
2636 }
2637 status = *p++;
2638 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2639 mult /= 10, p++)
2640 if (*p >= '0' && *p <= '9')
2641 val += (*p - '0') * mult;
2642 else
2643 return -1;
2644 mouse_col = val;
2645 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2646 mult /= 10, p++)
2647 if (*p >= '0' && *p <= '9')
2648 val += (*p - '0') * mult;
2649 else
2650 return -1;
2651 mouse_row = val;
2652 switch (*p++)
2653 {
2654 case 's': button |= 8; break; // SHIFT key Pressed
2655 case '-': break; // Not Pressed
2656 case 'x': break; // Not Reporting
2657 default: return -1; // Unknown Result
2658 }
2659 switch (*p++)
2660 {
2661 case 'c': button |= 16; break; // CTRL key Pressed
2662 case '-': break; // Not Pressed
2663 case 'x': break; // Not Reporting
2664 default: return -1; // Unknown Result
2665 }
2666 if (*p++ != '\033')
2667 return -1;
2668 if (*p++ != '\\')
2669 return -1;
2670 switch (status)
2671 {
2672 case 'D': // Double Click
2673 case 'S': // Single Click
2674 if (button & 1) mouse_code |= MOUSE_LEFT;
2675 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2676 if (button & 4) mouse_code |= MOUSE_RIGHT;
2677 if (button & 8) mouse_code |= MOUSE_SHIFT;
2678 if (button & 16) mouse_code |= MOUSE_CTRL;
2679 break;
2680 case 'm': // Mouse move
2681 if (button & 1) mouse_code |= MOUSE_LEFT;
2682 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2683 if (button & 4) mouse_code |= MOUSE_RIGHT;
2684 if (button & 8) mouse_code |= MOUSE_SHIFT;
2685 if (button & 16) mouse_code |= MOUSE_CTRL;
2686 if ((button & 7) != 0)
2687 {
2688 held_button = mouse_code;
2689 mouse_code |= MOUSE_DRAG;
2690 }
2691 is_drag = TRUE;
2692 showmode();
2693 break;
2694 case 'd': // Button Down
2695 if (button & 1) mouse_code |= MOUSE_LEFT;
2696 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2697 if (button & 4) mouse_code |= MOUSE_RIGHT;
2698 if (button & 8) mouse_code |= MOUSE_SHIFT;
2699 if (button & 16) mouse_code |= MOUSE_CTRL;
2700 break;
2701 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002702 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002703 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002704 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002705 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002706 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002707 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002708 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002709 if (button & 8)
2710 mouse_code |= MOUSE_SHIFT;
2711 if (button & 16)
2712 mouse_code |= MOUSE_CTRL;
2713 break;
2714 default: return -1; // Unknown Result
2715 }
2716
2717 *slen += (p - (tp + *slen));
2718 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002719#endif // FEAT_MOUSE_JSB
2720#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002721 if (key_name[0] == KS_DEC_MOUSE)
2722 {
2723 /*
2724 * The DEC Locator Input Model
2725 * Netterm delivers the code sequence:
2726 * \033[2;4;24;80&w (left button down)
2727 * \033[3;0;24;80&w (left button up)
2728 * \033[6;1;24;80&w (right button down)
2729 * \033[7;0;24;80&w (right button up)
2730 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2731 * Pe is the event code
2732 * Pb is the button code
2733 * Pr is the row coordinate
2734 * Pc is the column coordinate
2735 * Pp is the third coordinate (page number)
2736 * Pe, the event code indicates what event caused this report
2737 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002738 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002739 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002740 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002741 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002742 * 2 - left button down
2743 * 3 - left button up
2744 * 4 - middle button down
2745 * 5 - middle button up
2746 * 6 - right button down
2747 * 7 - right button up
2748 * 8 - fourth button down
2749 * 9 - fourth button up
2750 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002751 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2752 * down if any. The state of the four buttons on the locator
2753 * correspond to the low four bits of the decimal value, "1" means
2754 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002755 * 0 - no buttons down,
2756 * 1 - right,
2757 * 2 - middle,
2758 * 4 - left,
2759 * 8 - fourth
2760 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002761 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2762 * position is undefined (outside the terminal window for example).
2763 * Pc is the column coordinate of the locator position in the page,
2764 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2765 * position is undefined (outside the terminal window for example).
2766 * Pp is the page coordinate of the locator position encoded as an
2767 * ASCII decimal value. The page coordinate may be omitted if the
2768 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002769 */
2770 int Pe, Pb, Pr, Pc;
2771
2772 p = tp + *slen;
2773
2774 // get event status
2775 Pe = getdigits(&p);
2776 if (*p++ != ';')
2777 return -1;
2778
2779 // get button status
2780 Pb = getdigits(&p);
2781 if (*p++ != ';')
2782 return -1;
2783
2784 // get row status
2785 Pr = getdigits(&p);
2786 if (*p++ != ';')
2787 return -1;
2788
2789 // get column status
2790 Pc = getdigits(&p);
2791
2792 // the page parameter is optional
2793 if (*p == ';')
2794 {
2795 p++;
2796 (void)getdigits(&p);
2797 }
2798 if (*p++ != '&')
2799 return -1;
2800 if (*p++ != 'w')
2801 return -1;
2802
2803 mouse_code = 0;
2804 switch (Pe)
2805 {
2806 case 0: return -1; // position request while unavailable
2807 case 1: // a response to a locator position request includes
2808 // the status of all buttons
2809 Pb &= 7; // mask off and ignore fourth button
2810 if (Pb & 4)
2811 mouse_code = MOUSE_LEFT;
2812 if (Pb & 2)
2813 mouse_code = MOUSE_MIDDLE;
2814 if (Pb & 1)
2815 mouse_code = MOUSE_RIGHT;
2816 if (Pb)
2817 {
2818 held_button = mouse_code;
2819 mouse_code |= MOUSE_DRAG;
2820 WantQueryMouse = TRUE;
2821 }
2822 is_drag = TRUE;
2823 showmode();
2824 break;
2825 case 2: mouse_code = MOUSE_LEFT;
2826 WantQueryMouse = TRUE;
2827 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002828 case 3: mouse_code = MOUSE_LEFT;
2829 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002830 break;
2831 case 4: mouse_code = MOUSE_MIDDLE;
2832 WantQueryMouse = TRUE;
2833 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002834 case 5: mouse_code = MOUSE_MIDDLE;
2835 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002836 break;
2837 case 6: mouse_code = MOUSE_RIGHT;
2838 WantQueryMouse = TRUE;
2839 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002840 case 7: mouse_code = MOUSE_RIGHT;
2841 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002842 break;
2843 case 8: return -1; // fourth button down
2844 case 9: return -1; // fourth button up
2845 case 10: return -1; // mouse outside of filter rectangle
2846 default: return -1; // should never occur
2847 }
2848
2849 mouse_col = Pc - 1;
2850 mouse_row = Pr - 1;
2851
2852 *slen += (int)(p - (tp + *slen));
2853 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002854#endif // FEAT_MOUSE_DEC
2855#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002856 if (key_name[0] == KS_PTERM_MOUSE)
2857 {
2858 int button, num_clicks, action;
2859
2860 p = tp + *slen;
2861
2862 action = getdigits(&p);
2863 if (*p++ != ';')
2864 return -1;
2865
2866 mouse_row = getdigits(&p);
2867 if (*p++ != ';')
2868 return -1;
2869 mouse_col = getdigits(&p);
2870 if (*p++ != ';')
2871 return -1;
2872
2873 button = getdigits(&p);
2874 mouse_code = 0;
2875
2876 switch (button)
2877 {
2878 case 4: mouse_code = MOUSE_LEFT; break;
2879 case 1: mouse_code = MOUSE_RIGHT; break;
2880 case 2: mouse_code = MOUSE_MIDDLE; break;
2881 default: return -1;
2882 }
2883
2884 switch (action)
2885 {
2886 case 31: // Initial press
2887 if (*p++ != ';')
2888 return -1;
2889
2890 num_clicks = getdigits(&p); // Not used
2891 break;
2892
2893 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002894 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002895 break;
2896
2897 case 33: // Drag
2898 held_button = mouse_code;
2899 mouse_code |= MOUSE_DRAG;
2900 break;
2901
2902 default:
2903 return -1;
2904 }
2905
2906 if (*p++ != 't')
2907 return -1;
2908
2909 *slen += (p - (tp + *slen));
2910 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002911#endif // FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002912
2913 // Interpret the mouse code
2914 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002915 if (is_release)
2916 current_button |= MOUSE_RELEASE;
2917
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002918 if (current_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002919#ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002920 && wheel_code == 0
Christopher Plewright696d0a82022-11-18 17:53:34 +00002921#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002922 )
2923 {
2924 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002925 * If we get a mouse drag or release event when there is no mouse
2926 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2927 * below.
2928 * (can happen when you hold down two buttons and then let them go, or
2929 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002930 */
2931 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2932 is_drag = TRUE;
2933 current_button = held_button;
2934 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002935 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002936 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002937 if (wheel_code == 0)
2938 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002939#ifdef CHECK_DOUBLE_CLICK
2940# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002941 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002942 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2943 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002944 */
Christopher Plewright696d0a82022-11-18 17:53:34 +00002945# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002946 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002947# else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002948 if (key_name[0] != KS_GPM_MOUSE)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002949# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002950# else
2951# ifdef FEAT_GUI
2952 if (!gui.in_use)
2953# endif
2954# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002955 {
2956 /*
2957 * Compute the time elapsed since the previous mouse click.
2958 */
2959 gettimeofday(&mouse_time, NULL);
2960 if (orig_mouse_time.tv_sec == 0)
2961 {
2962 /*
2963 * Avoid computing the difference between mouse_time
2964 * and orig_mouse_time for the first click, as the
2965 * difference would be huge and would cause
2966 * multiplication overflow.
2967 */
2968 timediff = p_mouset;
2969 }
2970 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002971 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002972 orig_mouse_time = mouse_time;
2973 if (mouse_code == orig_mouse_code
2974 && timediff < p_mouset
2975 && orig_num_clicks != 4
2976 && orig_mouse_col == mouse_col
2977 && orig_mouse_row == mouse_row
2978 && (is_mouse_topline(curwin)
2979 // Double click in tab pages line also works
2980 // when window contents changes.
2981 || (mouse_row == 0 && firstwin->w_winrow > 0))
2982 )
2983 ++orig_num_clicks;
2984 else
2985 orig_num_clicks = 1;
2986 orig_mouse_col = mouse_col;
2987 orig_mouse_row = mouse_row;
2988 set_mouse_topline(curwin);
2989 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002990# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002991 else
2992 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002993# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002994#else
2995 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2996#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002997 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002998 }
2999 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003000 }
3001 if (!is_drag)
3002 held_button = mouse_code & MOUSE_CLICK_MASK;
3003
3004 /*
3005 * Translate the actual mouse event into a pseudo mouse event.
3006 * First work out what modifiers are to be used.
3007 */
3008 if (orig_mouse_code & MOUSE_SHIFT)
3009 *modifiers |= MOD_MASK_SHIFT;
3010 if (orig_mouse_code & MOUSE_CTRL)
3011 *modifiers |= MOD_MASK_CTRL;
3012 if (orig_mouse_code & MOUSE_ALT)
3013 *modifiers |= MOD_MASK_ALT;
3014 if (orig_num_clicks == 2)
3015 *modifiers |= MOD_MASK_2CLICK;
3016 else if (orig_num_clicks == 3)
3017 *modifiers |= MOD_MASK_3CLICK;
3018 else if (orig_num_clicks == 4)
3019 *modifiers |= MOD_MASK_4CLICK;
3020
Bram Moolenaar13c04632020-07-12 13:47:42 +02003021 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
3022 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003023 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003024 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003025 {
3026 if (wheel_code & MOUSE_CTRL)
3027 *modifiers |= MOD_MASK_CTRL;
3028 if (wheel_code & MOUSE_ALT)
3029 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003030
3031 if (wheel_code & 1 && wheel_code & 2)
3032 key_name[1] = (int)KE_MOUSELEFT;
3033 else if (wheel_code & 2)
3034 key_name[1] = (int)KE_MOUSERIGHT;
3035 else if (wheel_code & 1)
3036 key_name[1] = (int)KE_MOUSEUP;
3037 else
3038 key_name[1] = (int)KE_MOUSEDOWN;
3039
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003040 held_button = MOUSE_RELEASE;
3041 }
3042 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02003043 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003044
Bram Moolenaar13c04632020-07-12 13:47:42 +02003045
3046 // Make sure the mouse position is valid. Some terminals may return weird
3047 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003048 if (mouse_col >= Columns)
3049 mouse_col = Columns - 1;
3050 if (mouse_row >= Rows)
3051 mouse_row = Rows - 1;
3052
3053 return 0;
3054}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003055
3056// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003057
3058/*
3059 * Compute the buffer line position from the screen position "rowp" / "colp" in
3060 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02003061 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
3062 * caches the plines_win() result from a previous call. Entry is zero if not
3063 * computed yet. There must be no text or setting changes since the entry is
3064 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003065 * Returns TRUE if the position is below the last line.
3066 */
3067 int
3068mouse_comp_pos(
3069 win_T *win,
3070 int *rowp,
3071 int *colp,
3072 linenr_T *lnump,
3073 int *plines_cache)
3074{
3075 int col = *colp;
3076 int row = *rowp;
3077 linenr_T lnum;
3078 int retval = FALSE;
3079 int off;
3080 int count;
3081
3082#ifdef FEAT_RIGHTLEFT
3083 if (win->w_p_rl)
3084 col = win->w_width - 1 - col;
3085#endif
3086
3087 lnum = win->w_topline;
3088
3089 while (row > 0)
3090 {
3091 int cache_idx = lnum - win->w_topline;
3092
Bram Moolenaar452143c2020-07-15 17:38:21 +02003093 // Only "Rows" lines are cached, with folding we'll run out of entries
3094 // and use the slow way.
3095 if (plines_cache != NULL && cache_idx < Rows
3096 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003097 count = plines_cache[cache_idx];
3098 else
3099 {
3100#ifdef FEAT_DIFF
3101 // Don't include filler lines in "count"
3102 if (win->w_p_diff
3103# ifdef FEAT_FOLDING
3104 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3105# endif
3106 )
3107 {
3108 if (lnum == win->w_topline)
3109 row -= win->w_topfill;
3110 else
3111 row -= diff_check_fill(win, lnum);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003112 count = plines_win_nofill(win, lnum, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003113 }
3114 else
3115#endif
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003116 count = plines_win(win, lnum, FALSE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02003117 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003118 plines_cache[cache_idx] = count;
3119 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003120
3121 if (win->w_skipcol > 0 && lnum == win->w_topline)
3122 {
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003123 int width1 = win->w_width - win_col_off(win);
zeertzjq031a7452024-05-11 11:23:37 +02003124
3125 if (width1 > 0)
3126 {
3127 int skip_lines = 0;
3128
3129 // Adjust for 'smoothscroll' clipping the top screen lines.
3130 // A similar formula is used in curs_columns().
3131 if (win->w_skipcol > width1)
3132 skip_lines = (win->w_skipcol - width1)
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003133 / (width1 + win_col_off2(win)) + 1;
zeertzjq031a7452024-05-11 11:23:37 +02003134 else if (win->w_skipcol > 0)
3135 skip_lines = 1;
3136
3137 count -= skip_lines;
3138 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003139 }
3140
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003141 if (count > row)
3142 break; // Position is in this buffer line.
3143#ifdef FEAT_FOLDING
3144 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3145#endif
3146 if (lnum == win->w_buffer->b_ml.ml_line_count)
3147 {
3148 retval = TRUE;
3149 break; // past end of file
3150 }
3151 row -= count;
3152 ++lnum;
3153 }
3154
3155 if (!retval)
3156 {
3157 // Compute the column without wrapping.
3158 off = win_col_off(win) - win_col_off2(win);
3159 if (col < off)
3160 col = off;
3161 col += row * (win->w_width - off);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003162
3163 // Add skip column for the topline.
3164 if (lnum == win->w_topline)
3165 col += win->w_skipcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003166 }
3167
3168 if (!win->w_p_wrap)
3169 col += win->w_leftcol;
3170
3171 // skip line number and fold column in front of the line
3172 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003173 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003174 {
3175#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003176 // if mouse is clicked on the gutter, then inform the netbeans server
3177 if (*colp < win_col_off(win))
3178 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003179#endif
3180 col = 0;
3181 }
3182
3183 *colp = col;
3184 *rowp = row;
3185 *lnump = lnum;
3186 return retval;
3187}
3188
3189/*
3190 * Find the window at screen position "*rowp" and "*colp". The positions are
3191 * updated to become relative to the top-left of the window.
3192 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3193 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3194 * windows.
3195 * Returns NULL when something is wrong.
3196 */
3197 win_T *
3198mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3199{
3200 frame_T *fp;
3201 win_T *wp;
3202
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003203#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003204 win_T *pwp = NULL;
3205
3206 if (popup != IGNORE_POPUP)
3207 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003208 popup_reset_handled(POPUP_HANDLED_1);
3209 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003210 {
3211 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3212 && *colp >= wp->w_wincol
3213 && *colp < wp->w_wincol + popup_width(wp))
3214 pwp = wp;
3215 }
3216 if (pwp != NULL)
3217 {
3218 if (popup == FAIL_POPUP)
3219 return NULL;
3220 *rowp -= pwp->w_winrow;
3221 *colp -= pwp->w_wincol;
3222 return pwp;
3223 }
3224 }
3225#endif
3226
3227 fp = topframe;
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02003228
3229 if (*colp < firstwin->w_wincol
3230 || *colp >= firstwin->w_wincol + fp->fr_width
3231 || *rowp < firstwin->w_winrow)
3232 return NULL;
3233
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003234 *rowp -= firstwin->w_winrow;
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +02003235 *colp -= firstwin->w_wincol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003236 for (;;)
3237 {
3238 if (fp->fr_layout == FR_LEAF)
3239 break;
3240 if (fp->fr_layout == FR_ROW)
3241 {
3242 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3243 {
3244 if (*colp < fp->fr_width)
3245 break;
3246 *colp -= fp->fr_width;
3247 }
3248 }
3249 else // fr_layout == FR_COL
3250 {
3251 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3252 {
3253 if (*rowp < fp->fr_height)
3254 break;
3255 *rowp -= fp->fr_height;
3256 }
3257 }
3258 }
3259 // When using a timer that closes a window the window might not actually
3260 // exist.
3261 FOR_ALL_WINDOWS(wp)
3262 if (wp == fp->fr_win)
3263 {
3264#ifdef FEAT_MENU
3265 *rowp -= wp->w_winbar_height;
3266#endif
3267 return wp;
3268 }
3269 return NULL;
3270}
3271
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003272#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003273 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003274/*
3275 * Convert a virtual (screen) column to a character column.
zeertzjqb583eda2023-10-14 11:32:28 +02003276 * The first column is zero.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003277 */
3278 int
zeertzjqf5a94d52023-10-15 10:03:30 +02003279vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003280{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003281 char_u *line;
3282 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003283
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003284 // try to advance to the specified column
3285 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3286 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3287 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003288 {
zeertzjqb583eda2023-10-14 11:32:28 +02003289 int size = win_lbr_chartabsize(&cts, NULL);
3290 if (cts.cts_vcol + size > vcol)
3291 break;
3292 cts.cts_vcol += size;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003293 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003294 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003295 clear_chartabsize_arg(&cts);
3296
zeertzjqf5a94d52023-10-15 10:03:30 +02003297 if (coladdp != NULL)
3298 *coladdp = vcol - cts.cts_vcol;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003299 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003300}
3301#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003302
3303#if defined(FEAT_EVAL) || defined(PROTO)
Christopher Plewright696d0a82022-11-18 17:53:34 +00003304/*
3305 * "getmousepos()" function.
3306 */
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003307 void
3308f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3309{
3310 dict_T *d;
3311 win_T *wp;
3312 int row = mouse_row;
3313 int col = mouse_col;
3314 varnumber_T winid = 0;
3315 varnumber_T winrow = 0;
3316 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003317 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003318 varnumber_T column = 0;
zeertzjqf5a94d52023-10-15 10:03:30 +02003319 colnr_T coladd = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003320
Bram Moolenaar93a10962022-06-16 11:42:09 +01003321 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003322 return;
3323 d = rettv->vval.v_dict;
3324
3325 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3326 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3327
3328 wp = mouse_find_win(&row, &col, FIND_POPUP);
3329 if (wp != NULL)
3330 {
3331 int top_off = 0;
3332 int left_off = 0;
3333 int height = wp->w_height + wp->w_status_height;
3334
Christopher Plewright696d0a82022-11-18 17:53:34 +00003335# ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003336 if (WIN_IS_POPUP(wp))
3337 {
3338 top_off = popup_top_extra(wp);
3339 left_off = popup_left_extra(wp);
3340 height = popup_height(wp);
3341 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00003342# endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003343 if (row < height)
3344 {
3345 winid = wp->w_id;
3346 winrow = row + 1;
3347 wincol = col + 1;
3348 row -= top_off;
3349 col -= left_off;
3350 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3351 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003352 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
zeertzjqf5a94d52023-10-15 10:03:30 +02003353 col = vcol2col(wp, lnum, col, &coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003354 column = col + 1;
3355 }
3356 }
3357 }
3358 dict_add_number(d, "winid", winid);
3359 dict_add_number(d, "winrow", winrow);
3360 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003361 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003362 dict_add_number(d, "column", column);
zeertzjqf5a94d52023-10-15 10:03:30 +02003363 dict_add_number(d, "coladd", coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003364}
3365#endif