blob: 65540e555a0f2e1d361e29326f35d8c47ee76788 [file] [log] [blame]
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * mouse.c: mouse handling functions
12 */
13
14#include "vim.h"
15
LemonBoyc27747e2022-05-07 12:25:40 +010016/*
17 * Horiziontal and vertical steps used when scrolling.
18 * When negative scroll by a whole page.
19 */
20static long mouse_hor_step = 6;
21static long mouse_vert_step = 3;
22
23 void
24mouse_set_vert_scroll_step(long step)
25{
26 mouse_vert_step = step;
27}
28
29 void
30mouse_set_hor_scroll_step(long step)
31{
32 mouse_hor_step = step;
33}
34
Bram Moolenaar85c35022019-11-22 22:21:59 +010035#ifdef CHECK_DOUBLE_CLICK
36/*
37 * Return the duration from t1 to t2 in milliseconds.
38 */
39 static long
40time_diff_ms(struct timeval *t1, struct timeval *t2)
41{
42 // This handles wrapping of tv_usec correctly without any special case.
43 // Example of 2 pairs (tv_sec, tv_usec) with a duration of 5 ms:
44 // t1 = (1, 998000) t2 = (2, 3000) gives:
45 // (2 - 1) * 1000 + (3000 - 998000) / 1000 -> 5 ms.
46 return (t2->tv_sec - t1->tv_sec) * 1000
47 + (t2->tv_usec - t1->tv_usec) / 1000;
48}
49#endif
50
Bram Moolenaarb20b9e12019-09-21 20:48:04 +020051/*
52 * Get class of a character for selection: same class means same word.
53 * 0: blank
54 * 1: punctuation groups
55 * 2: normal word character
56 * >2: multi-byte word character.
57 */
58 static int
59get_mouse_class(char_u *p)
60{
61 int c;
62
63 if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
64 return mb_get_class(p);
65
66 c = *p;
67 if (c == ' ' || c == '\t')
68 return 0;
69
70 if (vim_iswordc(c))
71 return 2;
72
73 // There are a few special cases where we want certain combinations of
74 // characters to be considered as a single word. These are things like
75 // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
76 // character is in its own class.
77 if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
78 return 1;
79 return c;
80}
81
82/*
83 * Move "pos" back to the start of the word it's in.
84 */
85 static void
86find_start_of_word(pos_T *pos)
87{
88 char_u *line;
89 int cclass;
90 int col;
91
92 line = ml_get(pos->lnum);
93 cclass = get_mouse_class(line + pos->col);
94
95 while (pos->col > 0)
96 {
97 col = pos->col - 1;
98 col -= (*mb_head_off)(line, line + col);
99 if (get_mouse_class(line + col) != cclass)
100 break;
101 pos->col = col;
102 }
103}
104
105/*
106 * Move "pos" forward to the end of the word it's in.
107 * When 'selection' is "exclusive", the position is just after the word.
108 */
109 static void
110find_end_of_word(pos_T *pos)
111{
112 char_u *line;
113 int cclass;
114 int col;
115
116 line = ml_get(pos->lnum);
117 if (*p_sel == 'e' && pos->col > 0)
118 {
119 --pos->col;
120 pos->col -= (*mb_head_off)(line, line + pos->col);
121 }
122 cclass = get_mouse_class(line + pos->col);
123 while (line[pos->col] != NUL)
124 {
125 col = pos->col + (*mb_ptr2len)(line + pos->col);
126 if (get_mouse_class(line + col) != cclass)
127 {
128 if (*p_sel == 'e')
129 pos->col = col;
130 break;
131 }
132 pos->col = col;
133 }
134}
135
Bram Moolenaar910c3782019-09-22 14:11:50 +0200136#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100137 || defined(FEAT_GUI_MSWIN) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200138 || defined(FEAT_GUI_PHOTON) \
Bram Moolenaar910c3782019-09-22 14:11:50 +0200139 || defined(FEAT_TERM_POPUP_MENU)
140# define USE_POPUP_SETPOS
141# define NEED_VCOL2COL
142
143/*
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100144 * Translate window coordinates to buffer position without any side effects.
145 * Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
146 * The column is one for the first column.
Bram Moolenaar910c3782019-09-22 14:11:50 +0200147 */
148 static int
149get_fpos_of_mouse(pos_T *mpos)
150{
151 win_T *wp;
152 int row = mouse_row;
153 int col = mouse_col;
154
155 if (row < 0 || col < 0) // check if it makes sense
156 return IN_UNKNOWN;
157
158 // find the window where the row is in
159 wp = mouse_find_win(&row, &col, FAIL_POPUP);
160 if (wp == NULL)
161 return IN_UNKNOWN;
162 // winpos and height may change in win_enter()!
163 if (row >= wp->w_height) // In (or below) status line
164 return IN_STATUS_LINE;
165 if (col >= wp->w_width) // In vertical separator line
166 return IN_SEP_LINE;
167
168 if (wp != curwin)
169 return IN_UNKNOWN;
170
171 // compute the position in the buffer line from the posn on the screen
172 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
173 return IN_STATUS_LINE; // past bottom
174
zeertzjqf5a94d52023-10-15 10:03:30 +0200175 mpos->col = vcol2col(wp, mpos->lnum, col, &mpos->coladd);
Bram Moolenaar910c3782019-09-22 14:11:50 +0200176 return IN_BUFFER;
177}
178#endif
179
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200180/*
181 * Do the appropriate action for the current mouse click in the current mode.
182 * Not used for Command-line mode.
183 *
184 * Normal and Visual Mode:
185 * event modi- position visual change action
186 * fier cursor window
187 * left press - yes end yes
188 * left press C yes end yes "^]" (2)
189 * left press S yes end (popup: extend) yes "*" (2)
190 * left drag - yes start if moved no
191 * left relse - yes start if moved no
192 * middle press - yes if not active no put register
193 * middle press - yes if active no yank and put
194 * right press - yes start or extend yes
195 * right press S yes no change yes "#" (2)
196 * right drag - yes extend no
197 * right relse - yes extend no
198 *
199 * Insert or Replace Mode:
200 * event modi- position visual change action
201 * fier cursor window
202 * left press - yes (cannot be active) yes
203 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
204 * left press S yes (cannot be active) yes "CTRL-O*" (2)
205 * left drag - yes start or extend (1) no CTRL-O (1)
206 * left relse - yes start or extend (1) no CTRL-O (1)
207 * middle press - no (cannot be active) no put register
208 * right press - yes start or extend yes CTRL-O
209 * right press S yes (cannot be active) yes "CTRL-O#" (2)
210 *
211 * (1) only if mouse pointer moved since press
212 * (2) only if click is in same buffer
213 *
214 * Return TRUE if start_arrow() should be called for edit mode.
215 */
216 int
217do_mouse(
218 oparg_T *oap, // operator argument, can be NULL
219 int c, // K_LEFTMOUSE, etc
220 int dir, // Direction to 'put' if necessary
221 long count,
222 int fixindent) // PUT_FIXINDENT if fixing indent necessary
223{
224 static int do_always = FALSE; // ignore 'mouse' setting next time
zeertzjq8e0ccb62022-11-01 18:35:27 +0000225 static int got_click = FALSE; // got a click some time back
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200226
227 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
228 int is_click = FALSE; // If FALSE it's a drag or release event
229 int is_drag = FALSE; // If TRUE it's a drag event
230 int jump_flags = 0; // flags for jump_to_mouse()
231 pos_T start_visual;
232 int moved; // Has cursor moved?
233 int in_status_line; // mouse in status line
234 static int in_tab_line = FALSE; // mouse clicked in tab line
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200235#if defined(FEAT_TABPANEL)
236 static int in_tabpanel = FALSE; // mouse clicked in tabpanel
237#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200238 int in_sep_line; // mouse in vertical separator line
239 int c1, c2;
240#if defined(FEAT_FOLDING)
241 pos_T save_cursor;
242#endif
243 win_T *old_curwin = curwin;
244 static pos_T orig_cursor;
245 colnr_T leftcol, rightcol;
246 pos_T end_visual;
247 int diff;
248 int old_active = VIsual_active;
249 int old_mode = VIsual_mode;
250 int regname;
251
252#if defined(FEAT_FOLDING)
253 save_cursor = curwin->w_cursor;
254#endif
255
256 // When GUI is active, always recognize mouse events, otherwise:
257 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
258 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
259 // - For command line and insert mode 'mouse' is checked before calling
260 // do_mouse().
261 if (do_always)
262 do_always = FALSE;
263 else
264#ifdef FEAT_GUI
265 if (!gui.in_use)
266#endif
267 {
268 if (VIsual_active)
269 {
270 if (!mouse_has(MOUSE_VISUAL))
271 return FALSE;
272 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100273 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200274 return FALSE;
275 }
276
277 for (;;)
278 {
279 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
280 if (is_drag)
281 {
282 // If the next character is the same mouse event then use that
283 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100284 // Note: Since characters added to the stuff buffer in the code
285 // below need to come before the next character, do not do this
286 // when the current character was stuffed.
287 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200288 {
289 int nc;
290 int save_mouse_row = mouse_row;
291 int save_mouse_col = mouse_col;
292
293 // Need to get the character, peeking doesn't get the actual
294 // one.
295 nc = safe_vgetc();
296 if (c == nc)
297 continue;
298 vungetc(nc);
299 mouse_row = save_mouse_row;
300 mouse_col = save_mouse_col;
301 }
302 }
303 break;
304 }
305
306 if (c == K_MOUSEMOVE)
307 {
308 // Mouse moved without a button pressed.
309#ifdef FEAT_BEVAL_TERM
310 ui_may_remove_balloon();
311 if (p_bevalterm)
312 {
313 profile_setlimit(p_bdlay, &bevalexpr_due);
314 bevalexpr_due_set = TRUE;
315 }
316#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100317#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200318 popup_handle_mouse_moved();
319#endif
320 return FALSE;
321 }
322
323#ifdef FEAT_MOUSESHAPE
324 // May have stopped dragging the status or separator line. The pointer is
325 // most likely still on the status or separator line.
326 if (!is_drag && drag_status_line)
327 {
328 drag_status_line = FALSE;
329 update_mouseshape(SHAPE_IDX_STATUS);
330 }
331 if (!is_drag && drag_sep_line)
332 {
333 drag_sep_line = FALSE;
334 update_mouseshape(SHAPE_IDX_VSEP);
335 }
336#endif
337
338 // Ignore drag and release events if we didn't get a click.
339 if (is_click)
zeertzjq8e0ccb62022-11-01 18:35:27 +0000340 got_click = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200341 else
342 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000343 if (!got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200344 return FALSE;
zeertzjq8e0ccb62022-11-01 18:35:27 +0000345 if (!is_drag) // release, reset got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200346 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000347 got_click = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200348 if (in_tab_line
349#if defined(FEAT_TABPANEL)
350 || in_tabpanel
351#endif
352 )
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200353 {
354 in_tab_line = FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200355#if defined(FEAT_TABPANEL)
356 in_tabpanel = FALSE;
357#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200358 return FALSE;
359 }
360 }
361 }
362
363 // CTRL right mouse button does CTRL-T
364 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
365 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100366 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200367 stuffcharReadbuff(Ctrl_O);
368 if (count > 1)
369 stuffnumReadbuff(count);
370 stuffcharReadbuff(Ctrl_T);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000371 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200372 return FALSE;
373 }
374
375 // CTRL only works with left mouse button
376 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
377 return FALSE;
378
379 // When a modifier is down, ignore drag and release events, as well as
380 // multiple clicks and the middle mouse button.
381 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
382 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
383 | MOD_MASK_META))
384 && (!is_click
385 || (mod_mask & MOD_MASK_MULTI_CLICK)
386 || which_button == MOUSE_MIDDLE)
387 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
388 && mouse_model_popup()
389 && which_button == MOUSE_LEFT)
390 && !((mod_mask & MOD_MASK_ALT)
391 && !mouse_model_popup()
392 && which_button == MOUSE_RIGHT)
393 )
394 return FALSE;
395
396 // If the button press was used as the movement command for an operator
397 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
398 // drag/release events.
399 if (!is_click && which_button == MOUSE_MIDDLE)
400 return FALSE;
401
402 if (oap != NULL)
403 regname = oap->regname;
404 else
405 regname = 0;
406
407 // Middle mouse button does a 'put' of the selected text
408 if (which_button == MOUSE_MIDDLE)
409 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100410 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200411 {
412 // If an operator was pending, we don't know what the user wanted
413 // to do. Go back to normal mode: Clear the operator and beep().
414 if (oap != NULL && oap->op_type != OP_NOP)
415 {
416 clearopbeep(oap);
417 return FALSE;
418 }
419
420 // If visual was active, yank the highlighted text and put it
421 // before the mouse pointer position.
422 // In Select mode replace the highlighted text with the clipboard.
423 if (VIsual_active)
424 {
425 if (VIsual_select)
426 {
427 stuffcharReadbuff(Ctrl_G);
428 stuffReadbuff((char_u *)"\"+p");
429 }
430 else
431 {
432 stuffcharReadbuff('y');
433 stuffcharReadbuff(K_MIDDLEMOUSE);
434 }
435 do_always = TRUE; // ignore 'mouse' setting next time
436 return FALSE;
437 }
438 // The rest is below jump_to_mouse()
439 }
440
Bram Moolenaar24959102022-05-07 20:01:16 +0100441 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200442 return FALSE;
443
444 // Middle click in insert mode doesn't move the mouse, just insert the
445 // contents of a register. '.' register is special, can't insert that
446 // with do_put().
447 // Also paste at the cursor if the current mode isn't in 'mouse' (only
448 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100449 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200450 {
451 if (regname == '.')
452 insert_reg(regname, TRUE);
453 else
454 {
455#ifdef FEAT_CLIPBOARD
456 if (clip_star.available && regname == 0)
457 regname = '*';
458#endif
459 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
460 insert_reg(regname, TRUE);
461 else
462 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200463 do_put(regname, NULL, BACKWARD, 1L,
464 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200465
466 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
467 AppendCharToRedobuff(Ctrl_R);
468 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
469 AppendCharToRedobuff(regname == 0 ? '"' : regname);
470 }
471 }
472 return FALSE;
473 }
474 }
475
476 // When dragging or button-up stay in the same window.
477 if (!is_click)
478 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
479
480 start_visual.lnum = 0;
481
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200482 // Check for clicking in the tab page line.
483#if defined(FEAT_TABPANEL)
484 if (mouse_col < TPL_LCOL(NULL))
485 {
486 if (is_drag)
487 {
488 if (in_tabpanel)
489 {
490 c1 = get_tabpagenr_on_tabpanel();
491 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
492 ? c1 - 1 : c1);
493 }
494 return FALSE;
495 }
496
497 // click in a tab selects that tab page
498 if (is_click
499# ifdef FEAT_CMDWIN
500 && cmdwin_type == 0
501# endif
502 && mouse_col < Columns)
503 {
504 in_tabpanel = TRUE;
505 c1 = get_tabpagenr_on_tabpanel();
506 if (c1 >= 0)
507 {
508 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
509 {
510 // double click opens new page
511 end_visual_mode();
512 tabpage_new();
513 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
514 }
515 else
516 {
517 // Go to specified tab page, or next one if not clicking
518 // on a label.
519 goto_tabpage(c1);
520
521 // It's like clicking on the status line of a window.
522 if (curwin != old_curwin)
523 end_visual_mode();
524 }
525 }
526 else
527 {
528 tabpage_T *tp;
529
530 // Close the current or specified tab page.
531 if (c1 == -999)
532 tp = curtab;
533 else
534 tp = find_tabpage(-c1);
535 if (tp == curtab)
536 {
537 if (first_tabpage->tp_next != NULL)
538 tabpage_close(FALSE);
539 }
540 else if (tp != NULL)
541 tabpage_close_other(tp, FALSE);
542 }
543 }
544 return TRUE;
545 }
546 else if (is_drag && in_tabpanel)
547 {
548 c1 = get_tabpagenr_on_tabpanel();
549 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
550 return FALSE;
551 }
552#endif
553
Bram Moolenaar80525752022-08-24 19:27:45 +0100554 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200555 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100556 // Check for clicking in the tab page line.
557 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200558 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100559 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200560 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100561 if (in_tab_line)
562 {
563 c1 = TabPageIdxs[mouse_col];
564 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100565 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100566 }
567 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200568 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100569
570 // click in a tab selects that tab page
Martin Tournoij7904fa42022-10-04 16:28:45 +0100571 if (is_click && cmdwin_type == 0 && mouse_col < Columns)
Bram Moolenaar80525752022-08-24 19:27:45 +0100572 {
573 in_tab_line = TRUE;
574 c1 = TabPageIdxs[mouse_col];
575 if (c1 >= 0)
576 {
577 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
578 {
579 // double click opens new page
580 end_visual_mode_keep_button();
581 tabpage_new();
582 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
583 }
584 else
585 {
586 // Go to specified tab page, or next one if not clicking
587 // on a label.
588 goto_tabpage(c1);
589
590 // It's like clicking on the status line of a window.
591 if (curwin != old_curwin)
592 end_visual_mode_keep_button();
593 }
594 }
595 else
596 {
597 tabpage_T *tp;
598
599 // Close the current or specified tab page.
600 if (c1 == -999)
601 tp = curtab;
602 else
603 tp = find_tabpage(-c1);
604 if (tp == curtab)
605 {
606 if (first_tabpage->tp_next != NULL)
607 tabpage_close(FALSE);
608 }
609 else if (tp != NULL)
610 tabpage_close_other(tp, FALSE);
611 }
612 }
613 return TRUE;
614 }
615 else if (is_drag && in_tab_line)
616 {
617 c1 = TabPageIdxs[mouse_col];
618 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200619 return FALSE;
620 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200621 }
622
623 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
624 // right button up -> pop-up menu
625 // shift-left button -> right button
626 // alt-left button -> alt-right button
627 if (mouse_model_popup())
628 {
629 if (which_button == MOUSE_RIGHT
630 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
631 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200632#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200633# ifdef FEAT_GUI
634 if (gui.in_use)
635 {
636# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200637 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200638 if (!is_click)
639 // Ignore right button release events, only shows the popup
640 // menu on the button down event.
641 return FALSE;
642# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100643# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200644 if (is_click || is_drag)
645 // Ignore right button down and drag mouse events. Windows
646 // only shows the popup menu on the button up event.
647 return FALSE;
648# endif
649 }
650# endif
651# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
652 else
653# endif
654# if defined(FEAT_TERM_POPUP_MENU)
655 if (!is_click)
656 // Ignore right button release events, only shows the popup
657 // menu on the button down event.
658 return FALSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +0000659# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200660
661 jump_flags = 0;
662 if (STRCMP(p_mousem, "popup_setpos") == 0)
663 {
664 // First set the cursor position before showing the popup
665 // menu.
666 if (VIsual_active)
667 {
668 pos_T m_pos;
669
670 // set MOUSE_MAY_STOP_VIS if we are outside the
671 // selection or the current window (might have false
672 // negative here)
673 if (mouse_row < curwin->w_winrow
674 || mouse_row
675 > (curwin->w_winrow + curwin->w_height))
676 jump_flags = MOUSE_MAY_STOP_VIS;
677 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
678 jump_flags = MOUSE_MAY_STOP_VIS;
679 else
680 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100681 if (VIsual_mode == 'V')
682 {
683 if ((curwin->w_cursor.lnum <= VIsual.lnum
684 && (m_pos.lnum < curwin->w_cursor.lnum
685 || VIsual.lnum < m_pos.lnum))
686 || (VIsual.lnum < curwin->w_cursor.lnum
687 && (m_pos.lnum < VIsual.lnum
688 || curwin->w_cursor.lnum < m_pos.lnum)))
689 {
690 jump_flags = MOUSE_MAY_STOP_VIS;
691 }
692 }
693 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200694 && (LT_POS(m_pos, curwin->w_cursor)
695 || LT_POS(VIsual, m_pos)))
696 || (LT_POS(VIsual, curwin->w_cursor)
697 && (LT_POS(m_pos, VIsual)
698 || LT_POS(curwin->w_cursor, m_pos))))
699 {
700 jump_flags = MOUSE_MAY_STOP_VIS;
701 }
702 else if (VIsual_mode == Ctrl_V)
703 {
704 getvcols(curwin, &curwin->w_cursor, &VIsual,
705 &leftcol, &rightcol);
706 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
707 if (m_pos.col < leftcol || m_pos.col > rightcol)
708 jump_flags = MOUSE_MAY_STOP_VIS;
709 }
710 }
711 }
712 else
713 jump_flags = MOUSE_MAY_STOP_VIS;
714 }
715 if (jump_flags)
716 {
717 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100718 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200719 setcursor();
720 out_flush(); // Update before showing popup menu
721 }
722# ifdef FEAT_MENU
723 show_popupmenu();
zeertzjq8e0ccb62022-11-01 18:35:27 +0000724 got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200725# endif
726 return (jump_flags & CURSOR_MOVED) != 0;
727#else
728 return FALSE;
729#endif
730 }
731 if (which_button == MOUSE_LEFT
732 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
733 {
734 which_button = MOUSE_RIGHT;
735 mod_mask &= ~MOD_MASK_SHIFT;
736 }
737 }
738
Bram Moolenaar24959102022-05-07 20:01:16 +0100739 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200740 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
741 {
742 if (which_button == MOUSE_LEFT)
743 {
744 if (is_click)
745 {
746 // stop Visual mode for a left click in a window, but not when
747 // on a status line
748 if (VIsual_active)
749 jump_flags |= MOUSE_MAY_STOP_VIS;
750 }
751 else if (mouse_has(MOUSE_VISUAL))
752 jump_flags |= MOUSE_MAY_VIS;
753 }
754 else if (which_button == MOUSE_RIGHT)
755 {
756 if (is_click && VIsual_active)
757 {
758 // Remember the start and end of visual before moving the
759 // cursor.
760 if (LT_POS(curwin->w_cursor, VIsual))
761 {
762 start_visual = curwin->w_cursor;
763 end_visual = VIsual;
764 }
765 else
766 {
767 start_visual = VIsual;
768 end_visual = curwin->w_cursor;
769 }
770 }
771 jump_flags |= MOUSE_FOCUS;
772 if (mouse_has(MOUSE_VISUAL))
773 jump_flags |= MOUSE_MAY_VIS;
774 }
775 }
776
777 // If an operator is pending, ignore all drags and releases until the
778 // next mouse click.
779 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
780 {
zeertzjq8e0ccb62022-11-01 18:35:27 +0000781 got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200782 oap->motion_type = MCHAR;
783 }
784
785 // When releasing the button let jump_to_mouse() know.
786 if (!is_click && !is_drag)
787 jump_flags |= MOUSE_RELEASED;
788
789 // JUMP!
790 jump_flags = jump_to_mouse(jump_flags,
791 oap == NULL ? NULL : &(oap->inclusive), which_button);
792
793#ifdef FEAT_MENU
794 // A click in the window toolbar has no side effects.
795 if (jump_flags & MOUSE_WINBAR)
796 return FALSE;
797#endif
798 moved = (jump_flags & CURSOR_MOVED);
799 in_status_line = (jump_flags & IN_STATUS_LINE);
800 in_sep_line = (jump_flags & IN_SEP_LINE);
801
802#ifdef FEAT_NETBEANS_INTG
803 if (isNetbeansBuffer(curbuf)
804 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
805 {
806 int key = KEY2TERMCAP1(c);
807
808 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
809 || key == (int)KE_RIGHTRELEASE)
810 netbeans_button_release(which_button);
811 }
812#endif
813
814 // When jumping to another window, clear a pending operator. That's a bit
815 // friendlier than beeping and not jumping to that window.
816 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
817 clearop(oap);
818
819#ifdef FEAT_FOLDING
820 if (mod_mask == 0
821 && !is_drag
822 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
823 && which_button == MOUSE_LEFT)
824 {
825 // open or close a fold at this line
826 if (jump_flags & MOUSE_FOLD_OPEN)
827 openFold(curwin->w_cursor.lnum, 1L);
828 else
829 closeFold(curwin->w_cursor.lnum, 1L);
830 // don't move the cursor if still in the same window
831 if (curwin == old_curwin)
832 curwin->w_cursor = save_cursor;
833 }
834#endif
835
Martin Tournoij7904fa42022-10-04 16:28:45 +0100836#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200837 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
838 {
839 clip_modeless(which_button, is_click, is_drag);
840 return FALSE;
841 }
842#endif
843
844 // Set global flag that we are extending the Visual area with mouse
845 // dragging; temporarily minimize 'scrolloff'.
846 if (VIsual_active && is_drag && get_scrolloff_value())
847 {
848 // In the very first line, allow scrolling one line
849 if (mouse_row == 0)
850 mouse_dragging = 2;
851 else
852 mouse_dragging = 1;
853 }
854
855 // When dragging the mouse above the window, scroll down.
856 if (is_drag && mouse_row < 0 && !in_status_line)
857 {
858 scroll_redraw(FALSE, 1L);
859 mouse_row = 0;
860 }
861
862 if (start_visual.lnum) // right click in visual mode
863 {
864 // When ALT is pressed make Visual mode blockwise.
865 if (mod_mask & MOD_MASK_ALT)
866 VIsual_mode = Ctrl_V;
867
868 // In Visual-block mode, divide the area in four, pick up the corner
869 // that is in the quarter that the cursor is in.
870 if (VIsual_mode == Ctrl_V)
871 {
872 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
873 if (curwin->w_curswant > (leftcol + rightcol) / 2)
874 end_visual.col = leftcol;
875 else
876 end_visual.col = rightcol;
877 if (curwin->w_cursor.lnum >=
878 (start_visual.lnum + end_visual.lnum) / 2)
879 end_visual.lnum = start_visual.lnum;
880
881 // move VIsual to the right column
882 start_visual = curwin->w_cursor; // save the cursor pos
883 curwin->w_cursor = end_visual;
884 coladvance(end_visual.col);
885 VIsual = curwin->w_cursor;
886 curwin->w_cursor = start_visual; // restore the cursor
887 }
888 else
889 {
890 // If the click is before the start of visual, change the start.
891 // If the click is after the end of visual, change the end. If
892 // the click is inside the visual, change the closest side.
893 if (LT_POS(curwin->w_cursor, start_visual))
894 VIsual = end_visual;
895 else if (LT_POS(end_visual, curwin->w_cursor))
896 VIsual = start_visual;
897 else
898 {
899 // In the same line, compare column number
900 if (end_visual.lnum == start_visual.lnum)
901 {
902 if (curwin->w_cursor.col - start_visual.col >
903 end_visual.col - curwin->w_cursor.col)
904 VIsual = start_visual;
905 else
906 VIsual = end_visual;
907 }
908
909 // In different lines, compare line number
910 else
911 {
912 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
913 (end_visual.lnum - curwin->w_cursor.lnum);
914
915 if (diff > 0) // closest to end
916 VIsual = start_visual;
917 else if (diff < 0) // closest to start
918 VIsual = end_visual;
919 else // in the middle line
920 {
921 if (curwin->w_cursor.col <
922 (start_visual.col + end_visual.col) / 2)
923 VIsual = end_visual;
924 else
925 VIsual = start_visual;
926 }
927 }
928 }
929 }
930 }
931 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100932 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200933 stuffcharReadbuff(Ctrl_O);
934
935 // Middle mouse click: Put text before cursor.
936 if (which_button == MOUSE_MIDDLE)
937 {
938#ifdef FEAT_CLIPBOARD
939 if (clip_star.available && regname == 0)
940 regname = '*';
941#endif
942 if (yank_register_mline(regname))
943 {
944 if (mouse_past_bottom)
945 dir = FORWARD;
946 }
947 else if (mouse_past_eol)
948 dir = FORWARD;
949
950 if (fixindent)
951 {
952 c1 = (dir == BACKWARD) ? '[' : ']';
953 c2 = 'p';
954 }
955 else
956 {
957 c1 = (dir == FORWARD) ? 'p' : 'P';
958 c2 = NUL;
959 }
960 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
961
962 // Remember where the paste started, so in edit() Insstart can be set
963 // to this position
964 if (restart_edit != 0)
965 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200966 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200967 }
968
969#if defined(FEAT_QUICKFIX)
970 // Ctrl-Mouse click or double click in a quickfix window jumps to the
971 // error under the mouse pointer.
972 else if (((mod_mask & MOD_MASK_CTRL)
973 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
974 && bt_quickfix(curbuf))
975 {
976 if (curwin->w_llist_ref == NULL) // quickfix window
977 do_cmdline_cmd((char_u *)".cc");
978 else // location list window
979 do_cmdline_cmd((char_u *)".ll");
zeertzjq8e0ccb62022-11-01 18:35:27 +0000980 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200981 }
982#endif
983
984 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
985 // under the mouse pointer.
986 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
987 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
988 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100989 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200990 stuffcharReadbuff(Ctrl_O);
991 stuffcharReadbuff(Ctrl_RSB);
zeertzjq8e0ccb62022-11-01 18:35:27 +0000992 got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200993 }
994
995 // Shift-Mouse click searches for the next occurrence of the word under
996 // the mouse pointer
997 else if ((mod_mask & MOD_MASK_SHIFT))
998 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100999 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001000 stuffcharReadbuff(Ctrl_O);
1001 if (which_button == MOUSE_LEFT)
1002 stuffcharReadbuff('*');
1003 else // MOUSE_RIGHT
1004 stuffcharReadbuff('#');
1005 }
1006
1007 // Handle double clicks, unless on status line
1008 else if (in_status_line)
1009 {
1010#ifdef FEAT_MOUSESHAPE
1011 if ((is_drag || is_click) && !drag_status_line)
1012 {
1013 drag_status_line = TRUE;
1014 update_mouseshape(-1);
1015 }
1016#endif
1017 }
1018 else if (in_sep_line)
1019 {
1020#ifdef FEAT_MOUSESHAPE
1021 if ((is_drag || is_click) && !drag_sep_line)
1022 {
1023 drag_sep_line = TRUE;
1024 update_mouseshape(-1);
1025 }
1026#endif
1027 }
Bram Moolenaar24959102022-05-07 20:01:16 +01001028 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
1029 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001030 && mouse_has(MOUSE_VISUAL))
1031 {
1032 if (is_click || !VIsual_active)
1033 {
1034 if (VIsual_active)
1035 orig_cursor = VIsual;
1036 else
1037 {
1038 check_visual_highlight();
1039 VIsual = curwin->w_cursor;
1040 orig_cursor = VIsual;
1041 VIsual_active = TRUE;
1042 VIsual_reselect = TRUE;
1043 // start Select mode if 'selectmode' contains "mouse"
1044 may_start_select('o');
1045 setmouse();
1046 }
1047 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1048 {
1049 // Double click with ALT pressed makes it blockwise.
1050 if (mod_mask & MOD_MASK_ALT)
1051 VIsual_mode = Ctrl_V;
1052 else
1053 VIsual_mode = 'v';
1054 }
1055 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
1056 VIsual_mode = 'V';
1057 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
1058 VIsual_mode = Ctrl_V;
1059#ifdef FEAT_CLIPBOARD
1060 // Make sure the clipboard gets updated. Needed because start and
1061 // end may still be the same, and the selection needs to be owned
1062 clip_star.vmode = NUL;
1063#endif
1064 }
1065 // A double click selects a word or a block.
1066 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
1067 {
1068 pos_T *pos = NULL;
1069 int gc;
1070
1071 if (is_click)
1072 {
1073 // If the character under the cursor (skipping white space) is
1074 // not a word character, try finding a match and select a (),
1075 // {}, [], #if/#endif, etc. block.
1076 end_visual = curwin->w_cursor;
1077 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
1078 inc(&end_visual);
1079 if (oap != NULL)
1080 oap->motion_type = MCHAR;
1081 if (oap != NULL
1082 && VIsual_mode == 'v'
1083 && !vim_iswordc(gchar_pos(&end_visual))
1084 && EQUAL_POS(curwin->w_cursor, VIsual)
1085 && (pos = findmatch(oap, NUL)) != NULL)
1086 {
1087 curwin->w_cursor = *pos;
1088 if (oap->motion_type == MLINE)
1089 VIsual_mode = 'V';
1090 else if (*p_sel == 'e')
1091 {
1092 if (LT_POS(curwin->w_cursor, VIsual))
1093 ++VIsual.col;
1094 else
1095 ++curwin->w_cursor.col;
1096 }
1097 }
1098 }
1099
1100 if (pos == NULL && (is_click || is_drag))
1101 {
1102 // When not found a match or when dragging: extend to include
1103 // a word.
1104 if (LT_POS(curwin->w_cursor, orig_cursor))
1105 {
1106 find_start_of_word(&curwin->w_cursor);
1107 find_end_of_word(&VIsual);
1108 }
1109 else
1110 {
1111 find_start_of_word(&VIsual);
1112 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1113 curwin->w_cursor.col +=
1114 (*mb_ptr2len)(ml_get_cursor());
1115 find_end_of_word(&curwin->w_cursor);
1116 }
1117 }
1118 curwin->w_set_curswant = TRUE;
1119 }
1120 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001121 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001122 }
1123 else if (VIsual_active && !old_active)
1124 {
1125 if (mod_mask & MOD_MASK_ALT)
1126 VIsual_mode = Ctrl_V;
1127 else
1128 VIsual_mode = 'v';
1129 }
1130
1131 // If Visual mode changed show it later.
1132 if ((!VIsual_active && old_active && mode_displayed)
1133 || (VIsual_active && p_smd && msg_silent == 0
1134 && (!old_active || VIsual_mode != old_mode)))
1135 redraw_cmdline = TRUE;
1136
1137 return moved;
1138}
1139
1140 void
1141ins_mouse(int c)
1142{
1143 pos_T tpos;
1144 win_T *old_curwin = curwin;
1145
Christopher Plewright696d0a82022-11-18 17:53:34 +00001146#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001147 // When GUI is active, also move/paste when 'mouse' is empty
1148 if (!gui.in_use)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001149#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001150 if (!mouse_has(MOUSE_INSERT))
1151 return;
1152
1153 undisplay_dollar();
1154 tpos = curwin->w_cursor;
1155 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1156 {
1157 win_T *new_curwin = curwin;
1158
1159 if (curwin != old_curwin && win_valid(old_curwin))
1160 {
1161 // Mouse took us to another window. We need to go back to the
1162 // previous one to stop insert there properly.
1163 curwin = old_curwin;
1164 curbuf = curwin->w_buffer;
1165#ifdef FEAT_JOB_CHANNEL
1166 if (bt_prompt(curbuf))
1167 // Restart Insert mode when re-entering the prompt buffer.
1168 curbuf->b_prompt_insert = 'A';
1169#endif
1170 }
1171 start_arrow(curwin == old_curwin ? &tpos : NULL);
1172 if (curwin != new_curwin && win_valid(new_curwin))
1173 {
1174 curwin = new_curwin;
1175 curbuf = curwin->w_buffer;
1176 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001177 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001178 }
1179
1180 // redraw status lines (in case another window became active)
1181 redraw_statuslines();
1182}
1183
Christopher Plewright44c22092022-11-15 17:43:36 +00001184/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001185 * Common mouse wheel scrolling, shared between Insert mode and NV modes.
1186 * Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
1187 * depending on the scroll direction) or one page when Shift or Ctrl is used.
1188 * Direction is indicated by "cap->arg":
1189 * K_MOUSEUP - MSCR_UP
1190 * K_MOUSEDOWN - MSCR_DOWN
1191 * K_MOUSELEFT - MSCR_LEFT
1192 * K_MOUSERIGHT - MSCR_RIGHT
1193 * "curwin" may have been changed to the window that should be scrolled and
1194 * differ from the window that actually has focus.
1195 */
1196 static void
1197do_mousescroll(cmdarg_T *cap)
1198{
1199 int shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
1200
1201#ifdef FEAT_TERMINAL
1202 if (term_use_loop())
1203 // This window is a terminal window, send the mouse event there.
1204 // Set "typed" to FALSE to avoid an endless loop.
1205 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
1206 else
1207#endif
1208 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1209 {
1210 // Vertical scrolling
1211 if (!(State & MODE_INSERT) && (mouse_vert_step < 0 || shift_or_ctrl))
1212 {
1213 // whole page up or down
Luuk van Baal5a2e3ec2024-03-28 10:07:29 +01001214 pagescroll(cap->arg == MSCR_UP ? FORWARD : BACKWARD, 1L, FALSE);
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001215 }
1216 else
1217 {
1218 if (mouse_vert_step < 0 || shift_or_ctrl)
1219 {
1220 // whole page up or down
1221 cap->count1 = (long)(curwin->w_botline - curwin->w_topline);
1222 }
1223 // Don't scroll more than half the window height.
1224 else if (curwin->w_height < mouse_vert_step * 2)
1225 {
1226 cap->count1 = curwin->w_height / 2;
1227 if (cap->count1 == 0)
1228 cap->count1 = 1;
1229 }
1230 else
1231 {
1232 cap->count1 = mouse_vert_step;
1233 }
1234 cap->count0 = cap->count1;
1235 nv_scroll_line(cap);
1236 }
1237
1238#ifdef FEAT_PROP_POPUP
1239 if (WIN_IS_POPUP(curwin))
1240 popup_set_firstline(curwin);
1241#endif
1242 }
1243 else
1244 {
1245 // Horizontal scrolling
1246 long step = (mouse_hor_step < 0 || shift_or_ctrl)
1247 ? curwin->w_width : mouse_hor_step;
1248 long leftcol = curwin->w_leftcol
1249 + (cap->arg == MSCR_RIGHT ? -step : step);
1250 if (leftcol < 0)
1251 leftcol = 0;
1252 do_mousescroll_horiz((long_u)leftcol);
1253 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001254 may_trigger_win_scrolled_resized();
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001255}
1256
1257/*
1258 * Insert mode implementation for scrolling in direction "dir", which is
1259 * one of the MSCR_ values.
Christopher Plewright44c22092022-11-15 17:43:36 +00001260 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001261 void
1262ins_mousescroll(int dir)
1263{
Christopher Plewright696d0a82022-11-18 17:53:34 +00001264 cmdarg_T cap;
1265 oparg_T oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001266 CLEAR_FIELD(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00001267 clear_oparg(&oa);
1268 cap.oap = &oa;
Christopher Plewright44c22092022-11-15 17:43:36 +00001269 cap.arg = dir;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001270
Christopher Plewright44c22092022-11-15 17:43:36 +00001271 switch (dir)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001272 {
Christopher Plewright44c22092022-11-15 17:43:36 +00001273 case MSCR_UP:
1274 cap.cmdchar = K_MOUSEUP;
1275 break;
1276 case MSCR_DOWN:
1277 cap.cmdchar = K_MOUSEDOWN;
1278 break;
1279 case MSCR_LEFT:
1280 cap.cmdchar = K_MOUSELEFT;
1281 break;
1282 case MSCR_RIGHT:
1283 cap.cmdchar = K_MOUSERIGHT;
1284 break;
1285 default:
1286 siemsg("Invalid ins_mousescroll() argument: %d", dir);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001287 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00001288
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001289 win_T *old_curwin = curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001290 if (mouse_row >= 0 && mouse_col >= 0)
1291 {
1292 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001293 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00001294 int row = mouse_row;
1295 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001296 curwin = mouse_find_win(&row, &col, FIND_POPUP);
1297 if (curwin == NULL)
1298 {
1299 curwin = old_curwin;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001300 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001301 }
1302 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001303 }
1304
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001305 if (curwin == old_curwin)
Christopher Plewright696d0a82022-11-18 17:53:34 +00001306 {
1307 // Don't scroll the current window if the popup menu is visible.
1308 if (pum_visible())
1309 return;
1310
1311 undisplay_dollar();
1312 }
1313
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001314 linenr_T orig_topline = curwin->w_topline;
1315 colnr_T orig_leftcol = curwin->w_leftcol;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001316 pos_T orig_cursor = curwin->w_cursor;
1317
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001318 // Call the common mouse scroll function shared with other modes.
1319 do_mousescroll(&cap);
1320
1321 int did_scroll = (orig_topline != curwin->w_topline
1322 || orig_leftcol != curwin->w_leftcol);
1323
1324 curwin->w_redr_status = TRUE;
1325 curwin = old_curwin;
1326 curbuf = curwin->w_buffer;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001327
1328 // If the window actually scrolled and the popup menu may overlay the
1329 // window, need to redraw it.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00001330 if (did_scroll && pum_visible())
Christopher Plewright696d0a82022-11-18 17:53:34 +00001331 {
1332 // TODO: Would be more efficient to only redraw the windows that are
1333 // overlapped by the popup menu.
1334 redraw_all_later(UPD_NOT_VALID);
1335 ins_compl_show_pum();
1336 }
1337
1338 if (!EQUAL_POS(curwin->w_cursor, orig_cursor))
1339 {
1340 start_arrow(&orig_cursor);
1341 set_can_cindent(TRUE);
1342 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001343}
1344
1345/*
1346 * Return TRUE if "c" is a mouse key.
1347 */
1348 int
1349is_mouse_key(int c)
1350{
1351 return c == K_LEFTMOUSE
1352 || c == K_LEFTMOUSE_NM
1353 || c == K_LEFTDRAG
1354 || c == K_LEFTRELEASE
1355 || c == K_LEFTRELEASE_NM
1356 || c == K_MOUSEMOVE
1357 || c == K_MIDDLEMOUSE
1358 || c == K_MIDDLEDRAG
1359 || c == K_MIDDLERELEASE
1360 || c == K_RIGHTMOUSE
1361 || c == K_RIGHTDRAG
1362 || c == K_RIGHTRELEASE
1363 || c == K_MOUSEDOWN
1364 || c == K_MOUSEUP
1365 || c == K_MOUSELEFT
1366 || c == K_MOUSERIGHT
1367 || c == K_X1MOUSE
1368 || c == K_X1DRAG
1369 || c == K_X1RELEASE
1370 || c == K_X2MOUSE
1371 || c == K_X2DRAG
1372 || c == K_X2RELEASE;
1373}
1374
1375static struct mousetable
1376{
1377 int pseudo_code; // Code for pseudo mouse event
1378 int button; // Which mouse button is it?
1379 int is_click; // Is it a mouse button click event?
1380 int is_drag; // Is it a mouse drag event?
1381} mouse_table[] =
1382{
1383 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1384#ifdef FEAT_GUI
1385 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1386#endif
1387 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1388 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1389#ifdef FEAT_GUI
1390 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1391#endif
1392 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1393 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1394 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1395 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1396 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1397 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1398 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1399 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1400 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1401 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1402 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1403 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1404 // DRAG without CLICK
1405 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1406 // RELEASE without CLICK
1407 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1408 {0, 0, 0, 0},
1409};
1410
1411/*
1412 * Look up the given mouse code to return the relevant information in the other
1413 * arguments. Return which button is down or was released.
1414 */
1415 int
1416get_mouse_button(int code, int *is_click, int *is_drag)
1417{
1418 int i;
1419
1420 for (i = 0; mouse_table[i].pseudo_code; i++)
1421 if (code == mouse_table[i].pseudo_code)
1422 {
1423 *is_click = mouse_table[i].is_click;
1424 *is_drag = mouse_table[i].is_drag;
1425 return mouse_table[i].button;
1426 }
1427 return 0; // Shouldn't get here
1428}
1429
1430/*
1431 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1432 * the given information about which mouse button is down, and whether the
1433 * mouse was clicked, dragged or released.
1434 */
1435 int
1436get_pseudo_mouse_code(
1437 int button, // eg MOUSE_LEFT
1438 int is_click,
1439 int is_drag)
1440{
1441 int i;
1442
1443 for (i = 0; mouse_table[i].pseudo_code; i++)
1444 if (button == mouse_table[i].button
1445 && is_click == mouse_table[i].is_click
1446 && is_drag == mouse_table[i].is_drag)
1447 {
1448#ifdef FEAT_GUI
1449 // Trick: a non mappable left click and release has mouse_col -1
1450 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1451 // gui_mouse_moved()
1452 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1453 {
1454 if (mouse_col < 0)
1455 mouse_col = 0;
1456 else
1457 mouse_col -= MOUSE_COLOFF;
1458 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1459 return (int)KE_LEFTMOUSE_NM;
1460 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1461 return (int)KE_LEFTRELEASE_NM;
1462 }
1463#endif
1464 return mouse_table[i].pseudo_code;
1465 }
1466 return (int)KE_IGNORE; // not recognized, ignore it
1467}
1468
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001469# define HMT_NORMAL 1
1470# define HMT_NETTERM 2
1471# define HMT_DEC 4
1472# define HMT_JSBTERM 8
1473# define HMT_PTERM 16
1474# define HMT_URXVT 32
1475# define HMT_GPM 64
1476# define HMT_SGR 128
1477# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001478static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001479
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001480 void
1481set_mouse_termcode(
1482 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1483 char_u *s)
1484{
1485 char_u name[2];
1486
1487 name[0] = n;
1488 name[1] = KE_FILLER;
1489 add_termcode(name, s, FALSE);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001490#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001491 if (n == KS_JSBTERM_MOUSE)
1492 has_mouse_termcode |= HMT_JSBTERM;
1493 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001494#endif
1495#ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001496 if (n == KS_NETTERM_MOUSE)
1497 has_mouse_termcode |= HMT_NETTERM;
1498 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001499#endif
1500#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001501 if (n == KS_DEC_MOUSE)
1502 has_mouse_termcode |= HMT_DEC;
1503 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001504#endif
1505#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001506 if (n == KS_PTERM_MOUSE)
1507 has_mouse_termcode |= HMT_PTERM;
1508 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001509#endif
1510#ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001511 if (n == KS_URXVT_MOUSE)
1512 has_mouse_termcode |= HMT_URXVT;
1513 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001514#endif
1515#ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001516 if (n == KS_GPM_MOUSE)
1517 has_mouse_termcode |= HMT_GPM;
1518 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001519#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001520 if (n == KS_SGR_MOUSE)
1521 has_mouse_termcode |= HMT_SGR;
1522 else if (n == KS_SGR_MOUSE_RELEASE)
1523 has_mouse_termcode |= HMT_SGR_REL;
1524 else
1525 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001526}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001527
Christopher Plewright696d0a82022-11-18 17:53:34 +00001528#if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001529 void
1530del_mouse_termcode(
1531 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1532{
1533 char_u name[2];
1534
1535 name[0] = n;
1536 name[1] = KE_FILLER;
1537 del_termcode(name);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001538# ifdef FEAT_MOUSE_JSB
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001539 if (n == KS_JSBTERM_MOUSE)
1540 has_mouse_termcode &= ~HMT_JSBTERM;
1541 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001542# endif
1543# ifdef FEAT_MOUSE_NET
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001544 if (n == KS_NETTERM_MOUSE)
1545 has_mouse_termcode &= ~HMT_NETTERM;
1546 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001547# endif
1548# ifdef FEAT_MOUSE_DEC
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001549 if (n == KS_DEC_MOUSE)
1550 has_mouse_termcode &= ~HMT_DEC;
1551 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001552# endif
1553# ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001554 if (n == KS_PTERM_MOUSE)
1555 has_mouse_termcode &= ~HMT_PTERM;
1556 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001557# endif
1558# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001559 if (n == KS_URXVT_MOUSE)
1560 has_mouse_termcode &= ~HMT_URXVT;
1561 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001562# endif
1563# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001564 if (n == KS_GPM_MOUSE)
1565 has_mouse_termcode &= ~HMT_GPM;
1566 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00001567# endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001568 if (n == KS_SGR_MOUSE)
1569 has_mouse_termcode &= ~HMT_SGR;
1570 else if (n == KS_SGR_MOUSE_RELEASE)
1571 has_mouse_termcode &= ~HMT_SGR_REL;
1572 else
1573 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001574}
Christopher Plewright696d0a82022-11-18 17:53:34 +00001575#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001576
1577/*
1578 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1579 */
1580 void
1581setmouse(void)
1582{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001583 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001584
Christopher Plewright696d0a82022-11-18 17:53:34 +00001585#ifdef FEAT_MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001586 update_mouseshape(-1);
Christopher Plewright696d0a82022-11-18 17:53:34 +00001587#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001588
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001589 // Should be outside proc, but may break MOUSESHAPE
Christopher Plewright696d0a82022-11-18 17:53:34 +00001590#ifdef FEAT_GUI
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001591 // In the GUI the mouse is always enabled.
1592 if (gui.in_use)
1593 return;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001594#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001595 // be quick when mouse is off
1596 if (*p_mouse == NUL || has_mouse_termcode == 0)
1597 return;
1598
1599 // don't switch mouse on when not in raw mode (Ex mode)
1600 if (cur_tmode != TMODE_RAW)
1601 {
1602 mch_setmouse(FALSE);
1603 return;
1604 }
1605
1606 if (VIsual_active)
1607 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001608 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1609 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001610 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001611 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001612 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001613 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001614 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001615 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001616 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1617 else
1618 checkfor = MOUSE_NORMAL; // assume normal mode
1619
1620 if (mouse_has(checkfor))
1621 mch_setmouse(TRUE);
1622 else
1623 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001624}
1625
1626/*
1627 * Return TRUE if
1628 * - "c" is in 'mouse', or
1629 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1630 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1631 * normal editing mode (not at hit-return message).
1632 */
1633 int
1634mouse_has(int c)
1635{
1636 char_u *p;
1637
1638 for (p = p_mouse; *p; ++p)
1639 switch (*p)
1640 {
1641 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1642 return TRUE;
1643 break;
1644 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1645 return TRUE;
1646 break;
1647 default: if (c == *p) return TRUE; break;
1648 }
1649 return FALSE;
1650}
1651
1652/*
1653 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1654 */
1655 int
1656mouse_model_popup(void)
1657{
1658 return (p_mousem[0] == 'p');
1659}
1660
zeertzjq8e0ccb62022-11-01 18:35:27 +00001661static win_T *dragwin = NULL; // window being dragged
1662
1663/*
1664 * Reset the window being dragged. To be called when switching tab page.
1665 */
1666 void
1667reset_dragwin(void)
1668{
1669 dragwin = NULL;
1670}
1671
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001672/*
1673 * Move the cursor to the specified row and column on the screen.
1674 * Change current window if necessary. Returns an integer with the
1675 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1676 *
1677 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1678 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1679 *
1680 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1681 * if the mouse is outside the window then the text will scroll, or if the
1682 * mouse was previously on a status line, then the status line may be dragged.
1683 *
1684 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1685 * cursor is moved unless the cursor was on a status line.
1686 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1687 * IN_SEP_LINE depending on where the cursor was clicked.
1688 *
1689 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1690 * the mouse is on the status line of the same window.
1691 *
1692 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1693 * the last call.
1694 *
1695 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1696 * remembered.
1697 */
1698 int
1699jump_to_mouse(
1700 int flags,
1701 int *inclusive, // used for inclusive operator, can be NULL
1702 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1703{
1704 static int on_status_line = 0; // #lines below bottom of window
1705 static int on_sep_line = 0; // on separator right of window
1706#ifdef FEAT_MENU
1707 static int in_winbar = FALSE;
1708#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001709#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001710 static int in_popup_win = FALSE;
1711 static win_T *click_in_popup_win = NULL;
1712#endif
1713 static int prev_row = -1;
1714 static int prev_col = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001715 static int did_drag = FALSE; // drag was noticed
1716
1717 win_T *wp, *old_curwin;
1718 pos_T old_cursor;
1719 int count;
1720 int first;
1721 int row = mouse_row;
1722 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001723 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001724#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001725 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001726#endif
1727
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001728 col -= TPL_LCOL(NULL);
1729 if (col < 0)
1730 return IN_TABPANEL;
1731
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001732 mouse_past_bottom = FALSE;
1733 mouse_past_eol = FALSE;
1734
1735 if (flags & MOUSE_RELEASED)
1736 {
1737 // On button release we may change window focus if positioned on a
1738 // status line and no dragging happened.
1739 if (dragwin != NULL && !did_drag)
1740 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1741 dragwin = NULL;
1742 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001743#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001744 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1745 popup_close_for_mouse_click(click_in_popup_win);
1746
1747 popup_dragwin = NULL;
1748 click_in_popup_win = NULL;
1749#endif
1750 }
1751
1752 if ((flags & MOUSE_DID_MOVE)
1753 && prev_row == mouse_row
1754 && prev_col == mouse_col)
1755 {
1756retnomove:
1757 // before moving the cursor for a left click which is NOT in a status
1758 // line, stop Visual mode
1759 if (on_status_line)
1760 return IN_STATUS_LINE;
1761 if (on_sep_line)
1762 return IN_SEP_LINE;
1763#ifdef FEAT_MENU
1764 if (in_winbar)
1765 {
1766 // A quick second click may arrive as a double-click, but we use it
1767 // as a second click in the WinBar.
1768 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1769 {
1770 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1771 if (wp == NULL)
1772 return IN_UNKNOWN;
1773 winbar_click(wp, col);
1774 }
1775 return IN_OTHER_WIN | MOUSE_WINBAR;
1776 }
1777#endif
1778 if (flags & MOUSE_MAY_STOP_VIS)
1779 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001780 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001781 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001782 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001783#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001784 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01001785 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001786 return IN_OTHER_WIN;
1787#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001788#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001789 // Continue a modeless selection in a popup window or dragging it.
1790 if (in_popup_win)
1791 {
1792 click_in_popup_win = NULL; // don't close it on release
1793 if (popup_dragwin != NULL)
1794 {
1795 // dragging a popup window
1796 popup_drag(popup_dragwin);
1797 return IN_UNKNOWN;
1798 }
1799 return IN_OTHER_WIN;
1800 }
1801#endif
1802 return IN_BUFFER;
1803 }
1804
1805 prev_row = mouse_row;
1806 prev_col = mouse_col;
1807
1808 if (flags & MOUSE_SETPOS)
1809 goto retnomove; // ugly goto...
1810
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001811 old_curwin = curwin;
1812 old_cursor = curwin->w_cursor;
1813
1814 if (!(flags & MOUSE_FOCUS))
1815 {
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001816 if (row < 0 || col + TPL_LCOL(NULL) < 0) // check if it makes sense
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001817 return IN_UNKNOWN;
1818
1819 // find the window where the row is in and adjust "row" and "col" to be
1820 // relative to top-left of the window
1821 wp = mouse_find_win(&row, &col, FIND_POPUP);
1822 if (wp == NULL)
1823 return IN_UNKNOWN;
1824 dragwin = NULL;
1825
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001826#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001827 // Click in a popup window may start dragging or modeless selection,
1828 // but not much else.
1829 if (WIN_IS_POPUP(wp))
1830 {
1831 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001832 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001833 in_popup_win = TRUE;
1834 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1835 {
1836 return IN_UNKNOWN;
1837 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001838 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001839 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001840 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001841 {
1842 popup_dragwin = wp;
1843 popup_start_drag(wp, row, col);
1844 return IN_UNKNOWN;
1845 }
1846 // Only close on release, otherwise it's not possible to drag or do
1847 // modeless selection.
1848 else if (wp->w_popup_close == POPCLOSE_CLICK
1849 && which_button == MOUSE_LEFT)
1850 {
1851 click_in_popup_win = wp;
1852 }
1853 else if (which_button == MOUSE_LEFT)
1854 // If the click is in the scrollbar, may scroll up/down.
1855 popup_handle_scrollbar_click(wp, row, col);
1856# ifdef FEAT_CLIPBOARD
1857 return IN_OTHER_WIN;
1858# else
1859 return IN_UNKNOWN;
1860# endif
1861 }
1862 in_popup_win = FALSE;
1863 popup_dragwin = NULL;
1864#endif
1865#ifdef FEAT_MENU
1866 if (row == -1)
1867 {
1868 // A click in the window toolbar does not enter another window or
1869 // change Visual highlighting.
1870 winbar_click(wp, col);
1871 in_winbar = TRUE;
1872 return IN_OTHER_WIN | MOUSE_WINBAR;
1873 }
1874 in_winbar = FALSE;
1875#endif
1876
1877 // winpos and height may change in win_enter()!
1878 if (row >= wp->w_height) // In (or below) status line
1879 {
1880 on_status_line = row - wp->w_height + 1;
1881 dragwin = wp;
1882 }
1883 else
1884 on_status_line = 0;
1885 if (col >= wp->w_width) // In separator line
1886 {
1887 on_sep_line = col - wp->w_width + 1;
1888 dragwin = wp;
1889 }
1890 else
1891 on_sep_line = 0;
1892
1893 // The rightmost character of the status line might be a vertical
1894 // separator character if there is no connecting window to the right.
1895 if (on_status_line && on_sep_line)
1896 {
1897 if (stl_connected(wp))
1898 on_sep_line = 0;
1899 else
1900 on_status_line = 0;
1901 }
1902
1903 // Before jumping to another buffer, or moving the cursor for a left
1904 // click, stop Visual mode.
1905 if (VIsual_active
1906 && (wp->w_buffer != curwin->w_buffer
1907 || (!on_status_line && !on_sep_line
1908#ifdef FEAT_FOLDING
1909 && (
1910# ifdef FEAT_RIGHTLEFT
1911 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1912# endif
Sean Dewar988f7432023-08-16 14:17:36 +01001913 col >= wp->w_p_fdc + (wp != cmdwin_win ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001914 )
1915#endif
1916 && (flags & MOUSE_MAY_STOP_VIS))))
1917 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001918 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001919 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001920 }
Sean Dewar988f7432023-08-16 14:17:36 +01001921 if (cmdwin_type != 0 && wp != cmdwin_win)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001922 {
1923 // A click outside the command-line window: Use modeless
1924 // selection if possible. Allow dragging the status lines.
1925 on_sep_line = 0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001926#ifdef FEAT_CLIPBOARD
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001927 if (on_status_line)
1928 return IN_STATUS_LINE;
1929 return IN_OTHER_WIN;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001930#else
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001931 row = 0;
1932 col += wp->w_wincol;
Sean Dewar988f7432023-08-16 14:17:36 +01001933 wp = cmdwin_win;
Christopher Plewright696d0a82022-11-18 17:53:34 +00001934#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001935 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001936#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1937 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1938 // terminal in popup window: don't jump to another window
1939 return IN_OTHER_WIN;
1940#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001941 // Only change window focus when not clicking on or dragging the
1942 // status line. Do change focus when releasing the mouse button
1943 // (MOUSE_FOCUS was set above if we dragged first).
1944 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1945 win_enter(wp, TRUE); // can make wp invalid!
1946
1947 if (curwin != old_curwin)
1948 {
1949#ifdef CHECK_DOUBLE_CLICK
1950 // set topline, to be able to check for double click ourselves
1951 set_mouse_topline(curwin);
1952#endif
1953#ifdef FEAT_TERMINAL
1954 // when entering a terminal window may change state
1955 term_win_entered();
1956#endif
1957 }
1958 if (on_status_line) // In (or below) status line
1959 {
1960 // Don't use start_arrow() if we're in the same window
1961 if (curwin == old_curwin)
1962 return IN_STATUS_LINE;
1963 else
1964 return IN_STATUS_LINE | CURSOR_MOVED;
1965 }
1966 if (on_sep_line) // In (or below) status line
1967 {
1968 // Don't use start_arrow() if we're in the same window
1969 if (curwin == old_curwin)
1970 return IN_SEP_LINE;
1971 else
1972 return IN_SEP_LINE | CURSOR_MOVED;
1973 }
1974
1975 curwin->w_cursor.lnum = curwin->w_topline;
1976#ifdef FEAT_GUI
1977 // remember topline, needed for double click
1978 gui_prev_topline = curwin->w_topline;
1979# ifdef FEAT_DIFF
1980 gui_prev_topfill = curwin->w_topfill;
1981# endif
1982#endif
1983 }
1984 else if (on_status_line && which_button == MOUSE_LEFT)
1985 {
1986 if (dragwin != NULL)
1987 {
1988 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001989 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001990 - on_status_line;
1991 win_drag_status_line(dragwin, count);
1992 did_drag |= count;
1993 }
1994 return IN_STATUS_LINE; // Cursor didn't move
1995 }
1996 else if (on_sep_line && which_button == MOUSE_LEFT)
1997 {
1998 if (dragwin != NULL)
1999 {
2000 // Drag the separator column
2001 count = col - dragwin->w_wincol - dragwin->w_width + 1
2002 - on_sep_line;
2003 win_drag_vsep_line(dragwin, count);
2004 did_drag |= count;
2005 }
2006 return IN_SEP_LINE; // Cursor didn't move
2007 }
2008#ifdef FEAT_MENU
2009 else if (in_winbar)
2010 {
2011 // After a click on the window toolbar don't start Visual mode.
2012 return IN_OTHER_WIN | MOUSE_WINBAR;
2013 }
2014#endif
2015 else // keep_window_focus must be TRUE
2016 {
2017 // before moving the cursor for a left click, stop Visual mode
2018 if (flags & MOUSE_MAY_STOP_VIS)
2019 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02002020 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002021 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002022 }
2023
Martin Tournoij7904fa42022-10-04 16:28:45 +01002024#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002025 // Continue a modeless selection in another window.
Sean Dewar988f7432023-08-16 14:17:36 +01002026 if (cmdwin_type != 0 && row < cmdwin_win->w_winrow)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002027 return IN_OTHER_WIN;
2028#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002029#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002030 if (in_popup_win)
2031 {
2032 if (popup_dragwin != NULL)
2033 {
2034 // dragging a popup window
2035 popup_drag(popup_dragwin);
2036 return IN_UNKNOWN;
2037 }
2038 // continue a modeless selection in a popup window
2039 click_in_popup_win = NULL;
2040 return IN_OTHER_WIN;
2041 }
2042#endif
2043
2044 row -= W_WINROW(curwin);
2045 col -= curwin->w_wincol;
2046
2047 // When clicking beyond the end of the window, scroll the screen.
2048 // Scroll by however many rows outside the window we are.
2049 if (row < 0)
2050 {
2051 count = 0;
2052 for (first = TRUE; curwin->w_topline > 1; )
2053 {
2054#ifdef FEAT_DIFF
2055 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2056 ++count;
2057 else
2058#endif
2059 count += plines(curwin->w_topline - 1);
2060 if (!first && count > -row)
2061 break;
2062 first = FALSE;
2063#ifdef FEAT_FOLDING
2064 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2065#endif
2066#ifdef FEAT_DIFF
2067 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
2068 ++curwin->w_topfill;
2069 else
2070#endif
2071 {
2072 --curwin->w_topline;
2073#ifdef FEAT_DIFF
2074 curwin->w_topfill = 0;
2075#endif
2076 }
2077 }
2078#ifdef FEAT_DIFF
2079 check_topfill(curwin, FALSE);
2080#endif
2081 curwin->w_valid &=
2082 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002083 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002084 row = 0;
2085 }
2086 else if (row >= curwin->w_height)
2087 {
2088 count = 0;
2089 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
2090 {
2091#ifdef FEAT_DIFF
2092 if (curwin->w_topfill > 0)
2093 ++count;
2094 else
2095#endif
2096 count += plines(curwin->w_topline);
2097 if (!first && count > row - curwin->w_height + 1)
2098 break;
2099 first = FALSE;
2100#ifdef FEAT_FOLDING
2101 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
2102 && curwin->w_topline == curbuf->b_ml.ml_line_count)
2103 break;
2104#endif
2105#ifdef FEAT_DIFF
2106 if (curwin->w_topfill > 0)
2107 --curwin->w_topfill;
2108 else
2109#endif
2110 {
2111 ++curwin->w_topline;
2112#ifdef FEAT_DIFF
2113 curwin->w_topfill =
2114 diff_check_fill(curwin, curwin->w_topline);
2115#endif
2116 }
2117 }
2118#ifdef FEAT_DIFF
2119 check_topfill(curwin, FALSE);
2120#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002121 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002122 curwin->w_valid &=
2123 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2124 row = curwin->w_height - 1;
2125 }
2126 else if (row == 0)
2127 {
2128 // When dragging the mouse, while the text has been scrolled up as
2129 // far as it goes, moving the mouse in the top line should scroll
2130 // the text down (done later when recomputing w_topline).
2131 if (mouse_dragging > 0
2132 && curwin->w_cursor.lnum
2133 == curwin->w_buffer->b_ml.ml_line_count
2134 && curwin->w_cursor.lnum == curwin->w_topline)
2135 curwin->w_valid &= ~(VALID_TOPLINE);
2136 }
2137 }
2138
zeertzjqec149242023-12-19 20:28:31 +01002139 if (prev_row >= W_WINROW(curwin)
2140 && prev_row < W_WINROW(curwin) + curwin->w_height
2141 && prev_col >= curwin->w_wincol && prev_col < W_ENDCOL(curwin)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002142 && ScreenLines != NULL)
2143 {
2144 int off = LineOffset[prev_row] + prev_col;
2145
2146 // Only use ScreenCols[] after the window was redrawn. Mainly matters
2147 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01002148 // Do not use when 'virtualedit' is active.
zeertzjqe500ae82023-08-17 22:35:26 +02002149 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002150 col_from_screen = ScreenCols[off];
2151#ifdef FEAT_FOLDING
2152 // Remember the character under the mouse, it might be a '-' or '+' in
2153 // the fold column.
2154 mouse_char = ScreenLines[off];
2155#endif
2156 }
2157
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002158#ifdef FEAT_FOLDING
2159 // Check for position outside of the fold column.
2160 if (
2161# ifdef FEAT_RIGHTLEFT
2162 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
2163# endif
Sean Dewar988f7432023-08-16 14:17:36 +01002164 col >= curwin->w_p_fdc + (cmdwin_win != curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002165 )
2166 mouse_char = ' ';
2167#endif
2168
2169 // compute the position in the buffer line from the posn on the screen
2170 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
2171 mouse_past_bottom = TRUE;
2172
2173 // Start Visual mode before coladvance(), for when 'sel' != "old"
2174 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2175 {
2176 check_visual_highlight();
2177 VIsual = old_cursor;
2178 VIsual_active = TRUE;
2179 VIsual_reselect = TRUE;
2180 // if 'selectmode' contains "mouse", start Select mode
2181 may_start_select('o');
2182 setmouse();
2183 if (p_smd && msg_silent == 0)
2184 redraw_cmdline = TRUE; // show visual mode later
2185 }
2186
zeertzjqd0c1b772024-03-16 15:03:33 +01002187 if (col_from_screen >= 0)
Bram Moolenaarb9081882022-07-09 04:56:24 +01002188 {
zeertzjqe500ae82023-08-17 22:35:26 +02002189 // Use the virtual column from ScreenCols[], it is accurate also after
2190 // concealed characters.
2191 col = col_from_screen;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002192 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002193
zeertzjqe500ae82023-08-17 22:35:26 +02002194 curwin->w_curswant = col;
2195 curwin->w_set_curswant = FALSE; // May still have been TRUE
2196 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2197 {
2198 if (inclusive != NULL)
2199 *inclusive = TRUE;
2200 mouse_past_eol = TRUE;
2201 }
2202 else if (inclusive != NULL)
2203 *inclusive = FALSE;
2204
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002205 count = IN_BUFFER;
2206 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2207 || curwin->w_cursor.col != old_cursor.col)
2208 count |= CURSOR_MOVED; // Cursor has moved
2209
Christopher Plewright696d0a82022-11-18 17:53:34 +00002210#ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002211 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002212 count |= MOUSE_FOLD_OPEN;
2213 else if (mouse_char != ' ')
2214 count |= MOUSE_FOLD_CLOSE;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002215#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002216
2217 return count;
2218}
2219
2220/*
Christopher Plewright44c22092022-11-15 17:43:36 +00002221 * Make a horizontal scroll to "leftcol".
2222 * Return TRUE if the cursor moved, FALSE otherwise.
2223 */
2224 int
2225do_mousescroll_horiz(long_u leftcol)
2226{
2227 if (curwin->w_p_wrap)
Christian Brabandtee17b6f2023-09-09 11:23:50 +02002228 return FALSE; // no horizontal scrolling when wrapping
Christopher Plewright44c22092022-11-15 17:43:36 +00002229
2230 if (curwin->w_leftcol == (colnr_T)leftcol)
2231 return FALSE; // already there
2232
Christopher Plewright44c22092022-11-15 17:43:36 +00002233 // When the line of the cursor is too short, move the cursor to the
2234 // longest visible line.
2235 if (
2236#ifdef FEAT_GUI
2237 (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) &&
2238#endif
2239 !virtual_active()
2240 && (long)leftcol > scroll_line_len(curwin->w_cursor.lnum))
2241 {
2242 curwin->w_cursor.lnum = ui_find_longest_lnum();
2243 curwin->w_cursor.col = 0;
2244 }
2245
Bram Moolenaar0c34d562022-11-18 14:07:20 +00002246 return set_leftcol((colnr_T)leftcol);
Christopher Plewright44c22092022-11-15 17:43:36 +00002247}
2248
2249/*
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002250 * Normal and Visual modes implementation for scrolling in direction
2251 * "cap->arg", which is one of the MSCR_ values.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002252 */
2253 void
Christopher Plewright696d0a82022-11-18 17:53:34 +00002254nv_mousescroll(cmdarg_T *cap)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002255{
Christopher Plewright696d0a82022-11-18 17:53:34 +00002256 win_T *old_curwin = curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002257
2258 if (mouse_row >= 0 && mouse_col >= 0)
2259 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002260 // Find the window at the mouse pointer coordinates.
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002261 // NOTE: Must restore "curwin" to "old_curwin" before returning!
Christopher Plewright696d0a82022-11-18 17:53:34 +00002262 int row = mouse_row;
2263 int col = mouse_col;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002264 curwin = mouse_find_win(&row, &col, FIND_POPUP);
2265 if (curwin == NULL)
2266 {
2267 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002268 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002269 }
2270
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002271#ifdef FEAT_PROP_POPUP
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002272 if (WIN_IS_POPUP(curwin) && !curwin->w_has_scrollbar)
2273 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002274 // cannot scroll this popup window
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002275 curwin = old_curwin;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002276 return;
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002277 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002278#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002279 curbuf = curwin->w_buffer;
2280 }
Christopher Plewright44c22092022-11-15 17:43:36 +00002281
Christopher Plewrightff95ce02022-11-19 10:47:49 +00002282 // Call the common mouse scroll function shared with other modes.
2283 do_mousescroll(cap);
Christopher Plewright44c22092022-11-15 17:43:36 +00002284
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002285 curwin->w_redr_status = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002286 curwin = old_curwin;
2287 curbuf = curwin->w_buffer;
2288}
2289
2290/*
2291 * Mouse clicks and drags.
2292 */
2293 void
2294nv_mouse(cmdarg_T *cap)
2295{
2296 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2297}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002298
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002299static int held_button = MOUSE_RELEASE;
2300
2301 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00002302reset_held_button(void)
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002303{
2304 held_button = MOUSE_RELEASE;
2305}
2306
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002307/*
2308 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2309 * modifiers found in typebuf in 'modifiers'.
2310 */
2311 int
2312check_termcode_mouse(
2313 char_u *tp,
2314 int *slen,
2315 char_u *key_name,
2316 char_u *modifiers_start,
2317 int idx,
2318 int *modifiers)
2319{
2320 int j;
2321 char_u *p;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002322#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002323 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2324 char_u bytes[6];
2325 int num_bytes;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002326#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002327 int mouse_code = 0; // init for GCC
2328 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002329 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002330 int wheel_code = 0;
2331 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002332 static int orig_num_clicks = 1;
2333 static int orig_mouse_code = 0x0;
Christopher Plewright696d0a82022-11-18 17:53:34 +00002334#ifdef CHECK_DOUBLE_CLICK
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002335 static int orig_mouse_col = 0;
2336 static int orig_mouse_row = 0;
2337 static struct timeval orig_mouse_time = {0, 0};
2338 // time of previous mouse click
2339 struct timeval mouse_time; // time of current mouse click
2340 long timediff; // elapsed time in msec
Christopher Plewright696d0a82022-11-18 17:53:34 +00002341#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002342
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002343 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002344
Christopher Plewright696d0a82022-11-18 17:53:34 +00002345#if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002346 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2347 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002348# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002349 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002350# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002351 )
2352 {
2353 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002354 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002355 * 0x20 = left button down
2356 * 0x21 = middle button down
2357 * 0x22 = right button down
2358 * 0x23 = any button release
2359 * 0x60 = button 4 down (scroll wheel down)
2360 * 0x61 = button 5 down (scroll wheel up)
2361 * add 0x04 for SHIFT
2362 * add 0x08 for ALT
2363 * add 0x10 for CTRL
2364 * add 0x20 for mouse drag (0x40 is drag with left button)
2365 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2366 * 0x43 (drag + release) is also move
2367 * c == column + ' ' + 1 == column + 33
2368 * r == row + ' ' + 1 == row + 33
2369 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002370 * The coordinates are passed on through global variables. Ugly, but
2371 * this avoids trouble with mouse clicks at an unexpected moment and
2372 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002373 */
2374 for (;;)
2375 {
Christopher Plewright36446bb2022-11-23 22:28:08 +00002376 // For the GUI and for MS-Windows two bytes each are used for row
2377 // and column. Allows for more than 223 columns.
2378# if defined(FEAT_GUI) || defined(MSWIN)
2379 if (TRUE
2380# if defined(FEAT_GUI) && !defined(MSWIN)
2381 && gui.in_use
2382# endif
2383 )
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002384 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002385 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2386 if (num_bytes == -1) // not enough coordinates
2387 return -1;
2388 mouse_code = bytes[0];
2389 mouse_col = 128 * (bytes[1] - ' ' - 1)
2390 + bytes[2] - ' ' - 1;
2391 mouse_row = 128 * (bytes[3] - ' ' - 1)
2392 + bytes[4] - ' ' - 1;
2393 }
2394 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002395# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002396 {
2397 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2398 if (num_bytes == -1) // not enough coordinates
2399 return -1;
2400 mouse_code = bytes[0];
2401 mouse_col = bytes[1] - ' ' - 1;
2402 mouse_row = bytes[2] - ' ' - 1;
2403 }
2404 *slen += num_bytes;
2405
Bram Moolenaar13c04632020-07-12 13:47:42 +02002406 // If the following bytes is also a mouse code and it has the same
2407 // code, dump this one and get the next. This makes dragging a
2408 // whole lot faster.
Christopher Plewright696d0a82022-11-18 17:53:34 +00002409# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002410 if (gui.in_use)
2411 j = 3;
2412 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002413# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002414 j = get_termcode_len(idx);
2415 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2416 && tp[*slen + j] == mouse_code
2417 && tp[*slen + j + 1] != NUL
2418 && tp[*slen + j + 2] != NUL
Christopher Plewright696d0a82022-11-18 17:53:34 +00002419# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002420 && (!gui.in_use
2421 || (tp[*slen + j + 3] != NUL
2422 && tp[*slen + j + 4] != NUL))
Christopher Plewright696d0a82022-11-18 17:53:34 +00002423# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002424 )
2425 *slen += j;
2426 else
2427 break;
2428 }
2429 }
2430
2431 if (key_name[0] == KS_URXVT_MOUSE
2432 || key_name[0] == KS_SGR_MOUSE
2433 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2434 {
2435 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002436 // Almost identical to xterm mouse mode, except the values are decimal
2437 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002438 //
2439 // \033[%d;%d;%dM
2440 // ^-- row
2441 // ^----- column
2442 // ^-------- code
2443 //
2444 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002445 // Almost identical to xterm mouse mode, except the values are decimal
2446 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002447 //
2448 // \033[<%d;%d;%dM
2449 // ^-- row
2450 // ^----- column
2451 // ^-------- code
2452 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002453 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002454 // ^-- row
2455 // ^----- column
2456 // ^-------- code
2457 p = modifiers_start;
2458 if (p == NULL)
2459 return -1;
2460
2461 mouse_code = getdigits(&p);
2462 if (*p++ != ';')
2463 return -1;
2464
2465 // when mouse reporting is SGR, add 32 to mouse code
2466 if (key_name[0] == KS_SGR_MOUSE
2467 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2468 mouse_code += 32;
2469
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002470 mouse_col = getdigits(&p) - 1;
2471 if (*p++ != ';')
2472 return -1;
2473
2474 mouse_row = getdigits(&p) - 1;
2475
Bram Moolenaar13c04632020-07-12 13:47:42 +02002476 // The modifiers were the mouse coordinates, not the modifier keys
2477 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002478 *modifiers = 0;
2479 }
2480
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002481 if (key_name[0] == KS_SGR_MOUSE
2482 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2483 {
2484 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002485 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002486 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002487 // This is used below to set held_button.
2488 mouse_code |= MOUSE_RELEASE;
2489 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002490 }
2491 else
2492 {
2493 release_is_ambiguous = TRUE;
2494 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2495 is_release = TRUE;
2496 }
2497
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002498 if (key_name[0] == KS_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002499# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002500 || key_name[0] == KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002501# endif
2502# ifdef FEAT_MOUSE_URXVT
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002503 || key_name[0] == KS_URXVT_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002504# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002505 || key_name[0] == KS_SGR_MOUSE
2506 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2507 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002508# if !defined(MSWIN)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002509 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002510 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002511 * Recognize the xterm mouse wheel, but not in the GUI, the
2512 * Linux console with GPM and the MS-DOS or Win32 console
2513 * (multi-clicks use >= 0x60).
2514 */
2515 if (mouse_code >= MOUSEWHEEL_LOW
Christopher Plewright696d0a82022-11-18 17:53:34 +00002516# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002517 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002518# endif
2519# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002520 && key_name[0] != KS_GPM_MOUSE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002521# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002522 )
2523 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002524# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002525 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2526 // mouse-move event, using MOUSE_DRAG works
2527 mouse_code = MOUSE_DRAG;
2528 else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002529# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002530 // Keep the mouse_code before it's changed, so that we
2531 // remember that it was a mouse wheel click.
2532 wheel_code = mouse_code;
2533 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002534# ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002535 else if (held_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002536# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002537 && !gui.in_use
Christopher Plewright696d0a82022-11-18 17:53:34 +00002538# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002539 && (mouse_code == 0x23 || mouse_code == 0x24
2540 || mouse_code == 0x40 || mouse_code == 0x41))
2541 {
2542 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2543 // And 0x40 and 0x41 are used by some xterm emulator.
2544 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002545 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002546 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002547# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002548
Christopher Plewright696d0a82022-11-18 17:53:34 +00002549# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002550 else if (use_xterm_mouse() > 1)
2551 {
2552 if (mouse_code & MOUSE_DRAG_XTERM)
2553 mouse_code |= MOUSE_DRAG;
2554 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002555# endif
2556# ifdef FEAT_XCLIPBOARD
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002557 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2558 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002559 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002560 stop_xterm_trace();
2561 else
2562 start_xterm_trace(mouse_code);
2563 }
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002564# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002565# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002566 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002567#endif // !UNIX || FEAT_MOUSE_XTERM
2568#ifdef FEAT_MOUSE_NET
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002569 if (key_name[0] == KS_NETTERM_MOUSE)
2570 {
2571 int mc, mr;
2572
2573 // expect a rather limited sequence like: balancing {
2574 // \033}6,45\r
2575 // '6' is the row, 45 is the column
2576 p = tp + *slen;
2577 mr = getdigits(&p);
2578 if (*p++ != ',')
2579 return -1;
2580 mc = getdigits(&p);
2581 if (*p++ != '\r')
2582 return -1;
2583
2584 mouse_col = mc - 1;
2585 mouse_row = mr - 1;
2586 mouse_code = MOUSE_LEFT;
2587 *slen += (int)(p - (tp + *slen));
2588 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002589#endif // FEAT_MOUSE_NET
2590#ifdef FEAT_MOUSE_JSB
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002591 if (key_name[0] == KS_JSBTERM_MOUSE)
2592 {
2593 int mult, val, iter, button, status;
2594
2595 /*
2596 * JSBTERM Input Model
2597 * \033[0~zw uniq escape sequence
2598 * (L-x) Left button pressed - not pressed x not reporting
2599 * (M-x) Middle button pressed - not pressed x not reporting
2600 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002601 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2602 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002603 * ### X cursor position padded to 3 digits
2604 * ### Y cursor position padded to 3 digits
2605 * (s-x) SHIFT key pressed - not pressed x not reporting
2606 * (c-x) CTRL key pressed - not pressed x not reporting
2607 * \033\\ terminating sequence
2608 */
2609 p = tp + *slen;
2610 button = mouse_code = 0;
2611 switch (*p++)
2612 {
2613 case 'L': button = 1; break;
2614 case '-': break;
2615 case 'x': break; // ignore sequence
2616 default: return -1; // Unknown Result
2617 }
2618 switch (*p++)
2619 {
2620 case 'M': button |= 2; break;
2621 case '-': break;
2622 case 'x': break; // ignore sequence
2623 default: return -1; // Unknown Result
2624 }
2625 switch (*p++)
2626 {
2627 case 'R': button |= 4; break;
2628 case '-': break;
2629 case 'x': break; // ignore sequence
2630 default: return -1; // Unknown Result
2631 }
2632 status = *p++;
2633 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2634 mult /= 10, p++)
2635 if (*p >= '0' && *p <= '9')
2636 val += (*p - '0') * mult;
2637 else
2638 return -1;
2639 mouse_col = val;
2640 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2641 mult /= 10, p++)
2642 if (*p >= '0' && *p <= '9')
2643 val += (*p - '0') * mult;
2644 else
2645 return -1;
2646 mouse_row = val;
2647 switch (*p++)
2648 {
2649 case 's': button |= 8; break; // SHIFT key Pressed
2650 case '-': break; // Not Pressed
2651 case 'x': break; // Not Reporting
2652 default: return -1; // Unknown Result
2653 }
2654 switch (*p++)
2655 {
2656 case 'c': button |= 16; break; // CTRL key Pressed
2657 case '-': break; // Not Pressed
2658 case 'x': break; // Not Reporting
2659 default: return -1; // Unknown Result
2660 }
2661 if (*p++ != '\033')
2662 return -1;
2663 if (*p++ != '\\')
2664 return -1;
2665 switch (status)
2666 {
2667 case 'D': // Double Click
2668 case 'S': // Single Click
2669 if (button & 1) mouse_code |= MOUSE_LEFT;
2670 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2671 if (button & 4) mouse_code |= MOUSE_RIGHT;
2672 if (button & 8) mouse_code |= MOUSE_SHIFT;
2673 if (button & 16) mouse_code |= MOUSE_CTRL;
2674 break;
2675 case 'm': // Mouse move
2676 if (button & 1) mouse_code |= MOUSE_LEFT;
2677 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2678 if (button & 4) mouse_code |= MOUSE_RIGHT;
2679 if (button & 8) mouse_code |= MOUSE_SHIFT;
2680 if (button & 16) mouse_code |= MOUSE_CTRL;
2681 if ((button & 7) != 0)
2682 {
2683 held_button = mouse_code;
2684 mouse_code |= MOUSE_DRAG;
2685 }
2686 is_drag = TRUE;
2687 showmode();
2688 break;
2689 case 'd': // Button Down
2690 if (button & 1) mouse_code |= MOUSE_LEFT;
2691 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2692 if (button & 4) mouse_code |= MOUSE_RIGHT;
2693 if (button & 8) mouse_code |= MOUSE_SHIFT;
2694 if (button & 16) mouse_code |= MOUSE_CTRL;
2695 break;
2696 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002697 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002698 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002699 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002700 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002701 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002702 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002703 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002704 if (button & 8)
2705 mouse_code |= MOUSE_SHIFT;
2706 if (button & 16)
2707 mouse_code |= MOUSE_CTRL;
2708 break;
2709 default: return -1; // Unknown Result
2710 }
2711
2712 *slen += (p - (tp + *slen));
2713 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002714#endif // FEAT_MOUSE_JSB
2715#ifdef FEAT_MOUSE_DEC
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002716 if (key_name[0] == KS_DEC_MOUSE)
2717 {
2718 /*
2719 * The DEC Locator Input Model
2720 * Netterm delivers the code sequence:
2721 * \033[2;4;24;80&w (left button down)
2722 * \033[3;0;24;80&w (left button up)
2723 * \033[6;1;24;80&w (right button down)
2724 * \033[7;0;24;80&w (right button up)
2725 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2726 * Pe is the event code
2727 * Pb is the button code
2728 * Pr is the row coordinate
2729 * Pc is the column coordinate
2730 * Pp is the third coordinate (page number)
2731 * Pe, the event code indicates what event caused this report
2732 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002733 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002734 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002735 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002736 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002737 * 2 - left button down
2738 * 3 - left button up
2739 * 4 - middle button down
2740 * 5 - middle button up
2741 * 6 - right button down
2742 * 7 - right button up
2743 * 8 - fourth button down
2744 * 9 - fourth button up
2745 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002746 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2747 * down if any. The state of the four buttons on the locator
2748 * correspond to the low four bits of the decimal value, "1" means
2749 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002750 * 0 - no buttons down,
2751 * 1 - right,
2752 * 2 - middle,
2753 * 4 - left,
2754 * 8 - fourth
2755 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002756 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2757 * position is undefined (outside the terminal window for example).
2758 * Pc is the column coordinate of the locator position in the page,
2759 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2760 * position is undefined (outside the terminal window for example).
2761 * Pp is the page coordinate of the locator position encoded as an
2762 * ASCII decimal value. The page coordinate may be omitted if the
2763 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002764 */
2765 int Pe, Pb, Pr, Pc;
2766
2767 p = tp + *slen;
2768
2769 // get event status
2770 Pe = getdigits(&p);
2771 if (*p++ != ';')
2772 return -1;
2773
2774 // get button status
2775 Pb = getdigits(&p);
2776 if (*p++ != ';')
2777 return -1;
2778
2779 // get row status
2780 Pr = getdigits(&p);
2781 if (*p++ != ';')
2782 return -1;
2783
2784 // get column status
2785 Pc = getdigits(&p);
2786
2787 // the page parameter is optional
2788 if (*p == ';')
2789 {
2790 p++;
2791 (void)getdigits(&p);
2792 }
2793 if (*p++ != '&')
2794 return -1;
2795 if (*p++ != 'w')
2796 return -1;
2797
2798 mouse_code = 0;
2799 switch (Pe)
2800 {
2801 case 0: return -1; // position request while unavailable
2802 case 1: // a response to a locator position request includes
2803 // the status of all buttons
2804 Pb &= 7; // mask off and ignore fourth button
2805 if (Pb & 4)
2806 mouse_code = MOUSE_LEFT;
2807 if (Pb & 2)
2808 mouse_code = MOUSE_MIDDLE;
2809 if (Pb & 1)
2810 mouse_code = MOUSE_RIGHT;
2811 if (Pb)
2812 {
2813 held_button = mouse_code;
2814 mouse_code |= MOUSE_DRAG;
2815 WantQueryMouse = TRUE;
2816 }
2817 is_drag = TRUE;
2818 showmode();
2819 break;
2820 case 2: mouse_code = MOUSE_LEFT;
2821 WantQueryMouse = TRUE;
2822 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002823 case 3: mouse_code = MOUSE_LEFT;
2824 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002825 break;
2826 case 4: mouse_code = MOUSE_MIDDLE;
2827 WantQueryMouse = TRUE;
2828 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002829 case 5: mouse_code = MOUSE_MIDDLE;
2830 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002831 break;
2832 case 6: mouse_code = MOUSE_RIGHT;
2833 WantQueryMouse = TRUE;
2834 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002835 case 7: mouse_code = MOUSE_RIGHT;
2836 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002837 break;
2838 case 8: return -1; // fourth button down
2839 case 9: return -1; // fourth button up
2840 case 10: return -1; // mouse outside of filter rectangle
2841 default: return -1; // should never occur
2842 }
2843
2844 mouse_col = Pc - 1;
2845 mouse_row = Pr - 1;
2846
2847 *slen += (int)(p - (tp + *slen));
2848 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002849#endif // FEAT_MOUSE_DEC
2850#ifdef FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002851 if (key_name[0] == KS_PTERM_MOUSE)
2852 {
2853 int button, num_clicks, action;
2854
2855 p = tp + *slen;
2856
2857 action = getdigits(&p);
2858 if (*p++ != ';')
2859 return -1;
2860
2861 mouse_row = getdigits(&p);
2862 if (*p++ != ';')
2863 return -1;
2864 mouse_col = getdigits(&p);
2865 if (*p++ != ';')
2866 return -1;
2867
2868 button = getdigits(&p);
2869 mouse_code = 0;
2870
2871 switch (button)
2872 {
2873 case 4: mouse_code = MOUSE_LEFT; break;
2874 case 1: mouse_code = MOUSE_RIGHT; break;
2875 case 2: mouse_code = MOUSE_MIDDLE; break;
2876 default: return -1;
2877 }
2878
2879 switch (action)
2880 {
2881 case 31: // Initial press
2882 if (*p++ != ';')
2883 return -1;
2884
2885 num_clicks = getdigits(&p); // Not used
2886 break;
2887
2888 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002889 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002890 break;
2891
2892 case 33: // Drag
2893 held_button = mouse_code;
2894 mouse_code |= MOUSE_DRAG;
2895 break;
2896
2897 default:
2898 return -1;
2899 }
2900
2901 if (*p++ != 't')
2902 return -1;
2903
2904 *slen += (p - (tp + *slen));
2905 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002906#endif // FEAT_MOUSE_PTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002907
2908 // Interpret the mouse code
2909 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002910 if (is_release)
2911 current_button |= MOUSE_RELEASE;
2912
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002913 if (current_button == MOUSE_RELEASE
Christopher Plewright696d0a82022-11-18 17:53:34 +00002914#ifdef FEAT_MOUSE_XTERM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002915 && wheel_code == 0
Christopher Plewright696d0a82022-11-18 17:53:34 +00002916#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002917 )
2918 {
2919 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002920 * If we get a mouse drag or release event when there is no mouse
2921 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2922 * below.
2923 * (can happen when you hold down two buttons and then let them go, or
2924 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002925 */
2926 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2927 is_drag = TRUE;
2928 current_button = held_button;
2929 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002930 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002931 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002932 if (wheel_code == 0)
2933 {
Christopher Plewright696d0a82022-11-18 17:53:34 +00002934#ifdef CHECK_DOUBLE_CLICK
2935# ifdef FEAT_MOUSE_GPM
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002936 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002937 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2938 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002939 */
Christopher Plewright696d0a82022-11-18 17:53:34 +00002940# ifdef FEAT_GUI
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002941 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002942# else
Christopher Plewright696d0a82022-11-18 17:53:34 +00002943 if (key_name[0] != KS_GPM_MOUSE)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002944# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002945# else
2946# ifdef FEAT_GUI
2947 if (!gui.in_use)
2948# endif
2949# endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002950 {
2951 /*
2952 * Compute the time elapsed since the previous mouse click.
2953 */
2954 gettimeofday(&mouse_time, NULL);
2955 if (orig_mouse_time.tv_sec == 0)
2956 {
2957 /*
2958 * Avoid computing the difference between mouse_time
2959 * and orig_mouse_time for the first click, as the
2960 * difference would be huge and would cause
2961 * multiplication overflow.
2962 */
2963 timediff = p_mouset;
2964 }
2965 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002966 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002967 orig_mouse_time = mouse_time;
2968 if (mouse_code == orig_mouse_code
2969 && timediff < p_mouset
2970 && orig_num_clicks != 4
2971 && orig_mouse_col == mouse_col
2972 && orig_mouse_row == mouse_row
2973 && (is_mouse_topline(curwin)
2974 // Double click in tab pages line also works
2975 // when window contents changes.
2976 || (mouse_row == 0 && firstwin->w_winrow > 0))
2977 )
2978 ++orig_num_clicks;
2979 else
2980 orig_num_clicks = 1;
2981 orig_mouse_col = mouse_col;
2982 orig_mouse_row = mouse_row;
2983 set_mouse_topline(curwin);
2984 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00002985# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002986 else
2987 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002988# endif
Christopher Plewright696d0a82022-11-18 17:53:34 +00002989#else
2990 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2991#endif
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002992 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002993 }
2994 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002995 }
2996 if (!is_drag)
2997 held_button = mouse_code & MOUSE_CLICK_MASK;
2998
2999 /*
3000 * Translate the actual mouse event into a pseudo mouse event.
3001 * First work out what modifiers are to be used.
3002 */
3003 if (orig_mouse_code & MOUSE_SHIFT)
3004 *modifiers |= MOD_MASK_SHIFT;
3005 if (orig_mouse_code & MOUSE_CTRL)
3006 *modifiers |= MOD_MASK_CTRL;
3007 if (orig_mouse_code & MOUSE_ALT)
3008 *modifiers |= MOD_MASK_ALT;
3009 if (orig_num_clicks == 2)
3010 *modifiers |= MOD_MASK_2CLICK;
3011 else if (orig_num_clicks == 3)
3012 *modifiers |= MOD_MASK_3CLICK;
3013 else if (orig_num_clicks == 4)
3014 *modifiers |= MOD_MASK_4CLICK;
3015
Bram Moolenaar13c04632020-07-12 13:47:42 +02003016 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
3017 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003018 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003019 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003020 {
3021 if (wheel_code & MOUSE_CTRL)
3022 *modifiers |= MOD_MASK_CTRL;
3023 if (wheel_code & MOUSE_ALT)
3024 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02003025
3026 if (wheel_code & 1 && wheel_code & 2)
3027 key_name[1] = (int)KE_MOUSELEFT;
3028 else if (wheel_code & 2)
3029 key_name[1] = (int)KE_MOUSERIGHT;
3030 else if (wheel_code & 1)
3031 key_name[1] = (int)KE_MOUSEUP;
3032 else
3033 key_name[1] = (int)KE_MOUSEDOWN;
3034
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003035 held_button = MOUSE_RELEASE;
3036 }
3037 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02003038 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003039
Bram Moolenaar13c04632020-07-12 13:47:42 +02003040
3041 // Make sure the mouse position is valid. Some terminals may return weird
3042 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02003043 if (mouse_col >= Columns)
3044 mouse_col = Columns - 1;
3045 if (mouse_row >= Rows)
3046 mouse_row = Rows - 1;
3047
3048 return 0;
3049}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003050
3051// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003052
3053/*
3054 * Compute the buffer line position from the screen position "rowp" / "colp" in
3055 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02003056 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
3057 * caches the plines_win() result from a previous call. Entry is zero if not
3058 * computed yet. There must be no text or setting changes since the entry is
3059 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003060 * Returns TRUE if the position is below the last line.
3061 */
3062 int
3063mouse_comp_pos(
3064 win_T *win,
3065 int *rowp,
3066 int *colp,
3067 linenr_T *lnump,
3068 int *plines_cache)
3069{
3070 int col = *colp;
3071 int row = *rowp;
3072 linenr_T lnum;
3073 int retval = FALSE;
3074 int off;
3075 int count;
3076
3077#ifdef FEAT_RIGHTLEFT
3078 if (win->w_p_rl)
3079 col = win->w_width - 1 - col;
3080#endif
3081
3082 lnum = win->w_topline;
3083
3084 while (row > 0)
3085 {
3086 int cache_idx = lnum - win->w_topline;
3087
Bram Moolenaar452143c2020-07-15 17:38:21 +02003088 // Only "Rows" lines are cached, with folding we'll run out of entries
3089 // and use the slow way.
3090 if (plines_cache != NULL && cache_idx < Rows
3091 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003092 count = plines_cache[cache_idx];
3093 else
3094 {
3095#ifdef FEAT_DIFF
3096 // Don't include filler lines in "count"
3097 if (win->w_p_diff
3098# ifdef FEAT_FOLDING
3099 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3100# endif
3101 )
3102 {
3103 if (lnum == win->w_topline)
3104 row -= win->w_topfill;
3105 else
3106 row -= diff_check_fill(win, lnum);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003107 count = plines_win_nofill(win, lnum, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003108 }
3109 else
3110#endif
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003111 count = plines_win(win, lnum, FALSE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02003112 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003113 plines_cache[cache_idx] = count;
3114 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003115
3116 if (win->w_skipcol > 0 && lnum == win->w_topline)
3117 {
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003118 int width1 = win->w_width - win_col_off(win);
zeertzjq031a7452024-05-11 11:23:37 +02003119
3120 if (width1 > 0)
3121 {
3122 int skip_lines = 0;
3123
3124 // Adjust for 'smoothscroll' clipping the top screen lines.
3125 // A similar formula is used in curs_columns().
3126 if (win->w_skipcol > width1)
3127 skip_lines = (win->w_skipcol - width1)
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003128 / (width1 + win_col_off2(win)) + 1;
zeertzjq031a7452024-05-11 11:23:37 +02003129 else if (win->w_skipcol > 0)
3130 skip_lines = 1;
3131
3132 count -= skip_lines;
3133 }
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003134 }
3135
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003136 if (count > row)
3137 break; // Position is in this buffer line.
3138#ifdef FEAT_FOLDING
3139 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3140#endif
3141 if (lnum == win->w_buffer->b_ml.ml_line_count)
3142 {
3143 retval = TRUE;
3144 break; // past end of file
3145 }
3146 row -= count;
3147 ++lnum;
3148 }
3149
3150 if (!retval)
3151 {
3152 // Compute the column without wrapping.
3153 off = win_col_off(win) - win_col_off2(win);
3154 if (col < off)
3155 col = off;
3156 col += row * (win->w_width - off);
Yee Cheng Chine6392b12022-11-19 14:31:08 +00003157
3158 // Add skip column for the topline.
3159 if (lnum == win->w_topline)
3160 col += win->w_skipcol;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003161 }
3162
3163 if (!win->w_p_wrap)
3164 col += win->w_leftcol;
3165
3166 // skip line number and fold column in front of the line
3167 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003168 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003169 {
3170#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003171 // if mouse is clicked on the gutter, then inform the netbeans server
3172 if (*colp < win_col_off(win))
3173 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003174#endif
3175 col = 0;
3176 }
3177
3178 *colp = col;
3179 *rowp = row;
3180 *lnump = lnum;
3181 return retval;
3182}
3183
3184/*
3185 * Find the window at screen position "*rowp" and "*colp". The positions are
3186 * updated to become relative to the top-left of the window.
3187 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3188 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3189 * windows.
3190 * Returns NULL when something is wrong.
3191 */
3192 win_T *
3193mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3194{
3195 frame_T *fp;
3196 win_T *wp;
3197
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003198#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003199 win_T *pwp = NULL;
3200
3201 if (popup != IGNORE_POPUP)
3202 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003203 popup_reset_handled(POPUP_HANDLED_1);
3204 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003205 {
3206 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3207 && *colp >= wp->w_wincol
3208 && *colp < wp->w_wincol + popup_width(wp))
3209 pwp = wp;
3210 }
3211 if (pwp != NULL)
3212 {
3213 if (popup == FAIL_POPUP)
3214 return NULL;
3215 *rowp -= pwp->w_winrow;
3216 *colp -= pwp->w_wincol;
3217 return pwp;
3218 }
3219 }
3220#endif
3221
3222 fp = topframe;
3223 *rowp -= firstwin->w_winrow;
3224 for (;;)
3225 {
3226 if (fp->fr_layout == FR_LEAF)
3227 break;
3228 if (fp->fr_layout == FR_ROW)
3229 {
3230 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3231 {
3232 if (*colp < fp->fr_width)
3233 break;
3234 *colp -= fp->fr_width;
3235 }
3236 }
3237 else // fr_layout == FR_COL
3238 {
3239 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3240 {
3241 if (*rowp < fp->fr_height)
3242 break;
3243 *rowp -= fp->fr_height;
3244 }
3245 }
3246 }
3247 // When using a timer that closes a window the window might not actually
3248 // exist.
3249 FOR_ALL_WINDOWS(wp)
3250 if (wp == fp->fr_win)
3251 {
3252#ifdef FEAT_MENU
3253 *rowp -= wp->w_winbar_height;
3254#endif
3255 return wp;
3256 }
3257 return NULL;
3258}
3259
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003260#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003261 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003262/*
3263 * Convert a virtual (screen) column to a character column.
zeertzjqb583eda2023-10-14 11:32:28 +02003264 * The first column is zero.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003265 */
3266 int
zeertzjqf5a94d52023-10-15 10:03:30 +02003267vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003268{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003269 char_u *line;
3270 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003271
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003272 // try to advance to the specified column
3273 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3274 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3275 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003276 {
zeertzjqb583eda2023-10-14 11:32:28 +02003277 int size = win_lbr_chartabsize(&cts, NULL);
3278 if (cts.cts_vcol + size > vcol)
3279 break;
3280 cts.cts_vcol += size;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003281 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003282 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003283 clear_chartabsize_arg(&cts);
3284
zeertzjqf5a94d52023-10-15 10:03:30 +02003285 if (coladdp != NULL)
3286 *coladdp = vcol - cts.cts_vcol;
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003287 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003288}
3289#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003290
3291#if defined(FEAT_EVAL) || defined(PROTO)
Christopher Plewright696d0a82022-11-18 17:53:34 +00003292/*
3293 * "getmousepos()" function.
3294 */
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003295 void
3296f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3297{
3298 dict_T *d;
3299 win_T *wp;
3300 int row = mouse_row;
3301 int col = mouse_col;
3302 varnumber_T winid = 0;
3303 varnumber_T winrow = 0;
3304 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003305 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003306 varnumber_T column = 0;
zeertzjqf5a94d52023-10-15 10:03:30 +02003307 colnr_T coladd = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003308
Bram Moolenaar93a10962022-06-16 11:42:09 +01003309 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003310 return;
3311 d = rettv->vval.v_dict;
3312
3313 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3314 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3315
3316 wp = mouse_find_win(&row, &col, FIND_POPUP);
3317 if (wp != NULL)
3318 {
3319 int top_off = 0;
3320 int left_off = 0;
3321 int height = wp->w_height + wp->w_status_height;
3322
Christopher Plewright696d0a82022-11-18 17:53:34 +00003323# ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003324 if (WIN_IS_POPUP(wp))
3325 {
3326 top_off = popup_top_extra(wp);
3327 left_off = popup_left_extra(wp);
3328 height = popup_height(wp);
3329 }
Christopher Plewright696d0a82022-11-18 17:53:34 +00003330# endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003331 if (row < height)
3332 {
3333 winid = wp->w_id;
3334 winrow = row + 1;
3335 wincol = col + 1;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003336 wincol -= TPL_LCOL(NULL);
3337 if (wincol < 0)
3338 wincol = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003339 row -= top_off;
3340 col -= left_off;
3341 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3342 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003343 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
zeertzjqf5a94d52023-10-15 10:03:30 +02003344 col = vcol2col(wp, lnum, col, &coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003345 column = col + 1;
3346 }
3347 }
3348 }
3349 dict_add_number(d, "winid", winid);
3350 dict_add_number(d, "winrow", winrow);
3351 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003352 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003353 dict_add_number(d, "column", column);
zeertzjqf5a94d52023-10-15 10:03:30 +02003354 dict_add_number(d, "coladd", coladd);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003355}
3356#endif