blob: e92a73c033a90f77a3127d13ee5cfbf0948cc102 [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/*
144 * Translate window coordinates to buffer position without any side effects
145 */
146 static int
147get_fpos_of_mouse(pos_T *mpos)
148{
149 win_T *wp;
150 int row = mouse_row;
151 int col = mouse_col;
152
153 if (row < 0 || col < 0) // check if it makes sense
154 return IN_UNKNOWN;
155
156 // find the window where the row is in
157 wp = mouse_find_win(&row, &col, FAIL_POPUP);
158 if (wp == NULL)
159 return IN_UNKNOWN;
160 // winpos and height may change in win_enter()!
161 if (row >= wp->w_height) // In (or below) status line
162 return IN_STATUS_LINE;
163 if (col >= wp->w_width) // In vertical separator line
164 return IN_SEP_LINE;
165
166 if (wp != curwin)
167 return IN_UNKNOWN;
168
169 // compute the position in the buffer line from the posn on the screen
170 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
171 return IN_STATUS_LINE; // past bottom
172
173 mpos->col = vcol2col(wp, mpos->lnum, col);
174
175 if (mpos->col > 0)
176 --mpos->col;
177 mpos->coladd = 0;
178 return IN_BUFFER;
179}
180#endif
181
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200182/*
183 * Do the appropriate action for the current mouse click in the current mode.
184 * Not used for Command-line mode.
185 *
186 * Normal and Visual Mode:
187 * event modi- position visual change action
188 * fier cursor window
189 * left press - yes end yes
190 * left press C yes end yes "^]" (2)
191 * left press S yes end (popup: extend) yes "*" (2)
192 * left drag - yes start if moved no
193 * left relse - yes start if moved no
194 * middle press - yes if not active no put register
195 * middle press - yes if active no yank and put
196 * right press - yes start or extend yes
197 * right press S yes no change yes "#" (2)
198 * right drag - yes extend no
199 * right relse - yes extend no
200 *
201 * Insert or Replace Mode:
202 * event modi- position visual change action
203 * fier cursor window
204 * left press - yes (cannot be active) yes
205 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
206 * left press S yes (cannot be active) yes "CTRL-O*" (2)
207 * left drag - yes start or extend (1) no CTRL-O (1)
208 * left relse - yes start or extend (1) no CTRL-O (1)
209 * middle press - no (cannot be active) no put register
210 * right press - yes start or extend yes CTRL-O
211 * right press S yes (cannot be active) yes "CTRL-O#" (2)
212 *
213 * (1) only if mouse pointer moved since press
214 * (2) only if click is in same buffer
215 *
216 * Return TRUE if start_arrow() should be called for edit mode.
217 */
218 int
219do_mouse(
220 oparg_T *oap, // operator argument, can be NULL
221 int c, // K_LEFTMOUSE, etc
222 int dir, // Direction to 'put' if necessary
223 long count,
224 int fixindent) // PUT_FIXINDENT if fixing indent necessary
225{
226 static int do_always = FALSE; // ignore 'mouse' setting next time
227 static int got_click = FALSE; // got a click some time back
228
229 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
230 int is_click = FALSE; // If FALSE it's a drag or release event
231 int is_drag = FALSE; // If TRUE it's a drag event
232 int jump_flags = 0; // flags for jump_to_mouse()
233 pos_T start_visual;
234 int moved; // Has cursor moved?
235 int in_status_line; // mouse in status line
236 static int in_tab_line = FALSE; // mouse clicked in tab line
237 int in_sep_line; // mouse in vertical separator line
238 int c1, c2;
239#if defined(FEAT_FOLDING)
240 pos_T save_cursor;
241#endif
242 win_T *old_curwin = curwin;
243 static pos_T orig_cursor;
244 colnr_T leftcol, rightcol;
245 pos_T end_visual;
246 int diff;
247 int old_active = VIsual_active;
248 int old_mode = VIsual_mode;
249 int regname;
250
251#if defined(FEAT_FOLDING)
252 save_cursor = curwin->w_cursor;
253#endif
254
255 // When GUI is active, always recognize mouse events, otherwise:
256 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
257 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
258 // - For command line and insert mode 'mouse' is checked before calling
259 // do_mouse().
260 if (do_always)
261 do_always = FALSE;
262 else
263#ifdef FEAT_GUI
264 if (!gui.in_use)
265#endif
266 {
267 if (VIsual_active)
268 {
269 if (!mouse_has(MOUSE_VISUAL))
270 return FALSE;
271 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100272 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200273 return FALSE;
274 }
275
276 for (;;)
277 {
278 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
279 if (is_drag)
280 {
281 // If the next character is the same mouse event then use that
282 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100283 // Note: Since characters added to the stuff buffer in the code
284 // below need to come before the next character, do not do this
285 // when the current character was stuffed.
286 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200287 {
288 int nc;
289 int save_mouse_row = mouse_row;
290 int save_mouse_col = mouse_col;
291
292 // Need to get the character, peeking doesn't get the actual
293 // one.
294 nc = safe_vgetc();
295 if (c == nc)
296 continue;
297 vungetc(nc);
298 mouse_row = save_mouse_row;
299 mouse_col = save_mouse_col;
300 }
301 }
302 break;
303 }
304
305 if (c == K_MOUSEMOVE)
306 {
307 // Mouse moved without a button pressed.
308#ifdef FEAT_BEVAL_TERM
309 ui_may_remove_balloon();
310 if (p_bevalterm)
311 {
312 profile_setlimit(p_bdlay, &bevalexpr_due);
313 bevalexpr_due_set = TRUE;
314 }
315#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100316#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200317 popup_handle_mouse_moved();
318#endif
319 return FALSE;
320 }
321
322#ifdef FEAT_MOUSESHAPE
323 // May have stopped dragging the status or separator line. The pointer is
324 // most likely still on the status or separator line.
325 if (!is_drag && drag_status_line)
326 {
327 drag_status_line = FALSE;
328 update_mouseshape(SHAPE_IDX_STATUS);
329 }
330 if (!is_drag && drag_sep_line)
331 {
332 drag_sep_line = FALSE;
333 update_mouseshape(SHAPE_IDX_VSEP);
334 }
335#endif
336
337 // Ignore drag and release events if we didn't get a click.
338 if (is_click)
339 got_click = TRUE;
340 else
341 {
342 if (!got_click) // didn't get click, ignore
343 return FALSE;
344 if (!is_drag) // release, reset got_click
345 {
346 got_click = FALSE;
347 if (in_tab_line)
348 {
349 in_tab_line = FALSE;
350 return FALSE;
351 }
352 }
353 }
354
355 // CTRL right mouse button does CTRL-T
356 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
357 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100358 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200359 stuffcharReadbuff(Ctrl_O);
360 if (count > 1)
361 stuffnumReadbuff(count);
362 stuffcharReadbuff(Ctrl_T);
363 got_click = FALSE; // ignore drag&release now
364 return FALSE;
365 }
366
367 // CTRL only works with left mouse button
368 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
369 return FALSE;
370
371 // When a modifier is down, ignore drag and release events, as well as
372 // multiple clicks and the middle mouse button.
373 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
374 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
375 | MOD_MASK_META))
376 && (!is_click
377 || (mod_mask & MOD_MASK_MULTI_CLICK)
378 || which_button == MOUSE_MIDDLE)
379 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
380 && mouse_model_popup()
381 && which_button == MOUSE_LEFT)
382 && !((mod_mask & MOD_MASK_ALT)
383 && !mouse_model_popup()
384 && which_button == MOUSE_RIGHT)
385 )
386 return FALSE;
387
388 // If the button press was used as the movement command for an operator
389 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
390 // drag/release events.
391 if (!is_click && which_button == MOUSE_MIDDLE)
392 return FALSE;
393
394 if (oap != NULL)
395 regname = oap->regname;
396 else
397 regname = 0;
398
399 // Middle mouse button does a 'put' of the selected text
400 if (which_button == MOUSE_MIDDLE)
401 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100402 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200403 {
404 // If an operator was pending, we don't know what the user wanted
405 // to do. Go back to normal mode: Clear the operator and beep().
406 if (oap != NULL && oap->op_type != OP_NOP)
407 {
408 clearopbeep(oap);
409 return FALSE;
410 }
411
412 // If visual was active, yank the highlighted text and put it
413 // before the mouse pointer position.
414 // In Select mode replace the highlighted text with the clipboard.
415 if (VIsual_active)
416 {
417 if (VIsual_select)
418 {
419 stuffcharReadbuff(Ctrl_G);
420 stuffReadbuff((char_u *)"\"+p");
421 }
422 else
423 {
424 stuffcharReadbuff('y');
425 stuffcharReadbuff(K_MIDDLEMOUSE);
426 }
427 do_always = TRUE; // ignore 'mouse' setting next time
428 return FALSE;
429 }
430 // The rest is below jump_to_mouse()
431 }
432
Bram Moolenaar24959102022-05-07 20:01:16 +0100433 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200434 return FALSE;
435
436 // Middle click in insert mode doesn't move the mouse, just insert the
437 // contents of a register. '.' register is special, can't insert that
438 // with do_put().
439 // Also paste at the cursor if the current mode isn't in 'mouse' (only
440 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100441 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200442 {
443 if (regname == '.')
444 insert_reg(regname, TRUE);
445 else
446 {
447#ifdef FEAT_CLIPBOARD
448 if (clip_star.available && regname == 0)
449 regname = '*';
450#endif
451 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
452 insert_reg(regname, TRUE);
453 else
454 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200455 do_put(regname, NULL, BACKWARD, 1L,
456 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200457
458 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
459 AppendCharToRedobuff(Ctrl_R);
460 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
461 AppendCharToRedobuff(regname == 0 ? '"' : regname);
462 }
463 }
464 return FALSE;
465 }
466 }
467
468 // When dragging or button-up stay in the same window.
469 if (!is_click)
470 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
471
472 start_visual.lnum = 0;
473
474 // Check for clicking in the tab page line.
475 if (mouse_row == 0 && firstwin->w_winrow > 0)
476 {
477 if (is_drag)
478 {
479 if (in_tab_line)
480 {
481 c1 = TabPageIdxs[mouse_col];
482 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
483 ? c1 - 1 : c1);
484 }
485 return FALSE;
486 }
487
488 // click in a tab selects that tab page
489 if (is_click
490# ifdef FEAT_CMDWIN
491 && cmdwin_type == 0
492# endif
493 && mouse_col < Columns)
494 {
495 in_tab_line = TRUE;
496 c1 = TabPageIdxs[mouse_col];
497 if (c1 >= 0)
498 {
499 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
500 {
501 // double click opens new page
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +0200502 end_visual_mode_keep_button();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200503 tabpage_new();
504 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
505 }
506 else
507 {
508 // Go to specified tab page, or next one if not clicking
509 // on a label.
510 goto_tabpage(c1);
511
512 // It's like clicking on the status line of a window.
513 if (curwin != old_curwin)
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +0200514 end_visual_mode_keep_button();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200515 }
516 }
517 else
518 {
519 tabpage_T *tp;
520
521 // Close the current or specified tab page.
522 if (c1 == -999)
523 tp = curtab;
524 else
525 tp = find_tabpage(-c1);
526 if (tp == curtab)
527 {
528 if (first_tabpage->tp_next != NULL)
529 tabpage_close(FALSE);
530 }
531 else if (tp != NULL)
532 tabpage_close_other(tp, FALSE);
533 }
534 }
535 return TRUE;
536 }
537 else if (is_drag && in_tab_line)
538 {
539 c1 = TabPageIdxs[mouse_col];
540 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
541 return FALSE;
542 }
543
544 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
545 // right button up -> pop-up menu
546 // shift-left button -> right button
547 // alt-left button -> alt-right button
548 if (mouse_model_popup())
549 {
550 if (which_button == MOUSE_RIGHT
551 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
552 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200553#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200554# ifdef FEAT_GUI
555 if (gui.in_use)
556 {
557# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200558 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200559 if (!is_click)
560 // Ignore right button release events, only shows the popup
561 // menu on the button down event.
562 return FALSE;
563# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100564# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200565 if (is_click || is_drag)
566 // Ignore right button down and drag mouse events. Windows
567 // only shows the popup menu on the button up event.
568 return FALSE;
569# endif
570 }
571# endif
572# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
573 else
574# endif
575# if defined(FEAT_TERM_POPUP_MENU)
576 if (!is_click)
577 // Ignore right button release events, only shows the popup
578 // menu on the button down event.
579 return FALSE;
580#endif
581
582 jump_flags = 0;
583 if (STRCMP(p_mousem, "popup_setpos") == 0)
584 {
585 // First set the cursor position before showing the popup
586 // menu.
587 if (VIsual_active)
588 {
589 pos_T m_pos;
590
591 // set MOUSE_MAY_STOP_VIS if we are outside the
592 // selection or the current window (might have false
593 // negative here)
594 if (mouse_row < curwin->w_winrow
595 || mouse_row
596 > (curwin->w_winrow + curwin->w_height))
597 jump_flags = MOUSE_MAY_STOP_VIS;
598 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
599 jump_flags = MOUSE_MAY_STOP_VIS;
600 else
601 {
602 if ((LT_POS(curwin->w_cursor, VIsual)
603 && (LT_POS(m_pos, curwin->w_cursor)
604 || LT_POS(VIsual, m_pos)))
605 || (LT_POS(VIsual, curwin->w_cursor)
606 && (LT_POS(m_pos, VIsual)
607 || LT_POS(curwin->w_cursor, m_pos))))
608 {
609 jump_flags = MOUSE_MAY_STOP_VIS;
610 }
611 else if (VIsual_mode == Ctrl_V)
612 {
613 getvcols(curwin, &curwin->w_cursor, &VIsual,
614 &leftcol, &rightcol);
615 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
616 if (m_pos.col < leftcol || m_pos.col > rightcol)
617 jump_flags = MOUSE_MAY_STOP_VIS;
618 }
619 }
620 }
621 else
622 jump_flags = MOUSE_MAY_STOP_VIS;
623 }
624 if (jump_flags)
625 {
626 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
627 update_curbuf(VIsual_active ? INVERTED : VALID);
628 setcursor();
629 out_flush(); // Update before showing popup menu
630 }
631# ifdef FEAT_MENU
632 show_popupmenu();
633 got_click = FALSE; // ignore release events
634# endif
635 return (jump_flags & CURSOR_MOVED) != 0;
636#else
637 return FALSE;
638#endif
639 }
640 if (which_button == MOUSE_LEFT
641 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
642 {
643 which_button = MOUSE_RIGHT;
644 mod_mask &= ~MOD_MASK_SHIFT;
645 }
646 }
647
Bram Moolenaar24959102022-05-07 20:01:16 +0100648 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200649 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
650 {
651 if (which_button == MOUSE_LEFT)
652 {
653 if (is_click)
654 {
655 // stop Visual mode for a left click in a window, but not when
656 // on a status line
657 if (VIsual_active)
658 jump_flags |= MOUSE_MAY_STOP_VIS;
659 }
660 else if (mouse_has(MOUSE_VISUAL))
661 jump_flags |= MOUSE_MAY_VIS;
662 }
663 else if (which_button == MOUSE_RIGHT)
664 {
665 if (is_click && VIsual_active)
666 {
667 // Remember the start and end of visual before moving the
668 // cursor.
669 if (LT_POS(curwin->w_cursor, VIsual))
670 {
671 start_visual = curwin->w_cursor;
672 end_visual = VIsual;
673 }
674 else
675 {
676 start_visual = VIsual;
677 end_visual = curwin->w_cursor;
678 }
679 }
680 jump_flags |= MOUSE_FOCUS;
681 if (mouse_has(MOUSE_VISUAL))
682 jump_flags |= MOUSE_MAY_VIS;
683 }
684 }
685
686 // If an operator is pending, ignore all drags and releases until the
687 // next mouse click.
688 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
689 {
690 got_click = FALSE;
691 oap->motion_type = MCHAR;
692 }
693
694 // When releasing the button let jump_to_mouse() know.
695 if (!is_click && !is_drag)
696 jump_flags |= MOUSE_RELEASED;
697
698 // JUMP!
699 jump_flags = jump_to_mouse(jump_flags,
700 oap == NULL ? NULL : &(oap->inclusive), which_button);
701
702#ifdef FEAT_MENU
703 // A click in the window toolbar has no side effects.
704 if (jump_flags & MOUSE_WINBAR)
705 return FALSE;
706#endif
707 moved = (jump_flags & CURSOR_MOVED);
708 in_status_line = (jump_flags & IN_STATUS_LINE);
709 in_sep_line = (jump_flags & IN_SEP_LINE);
710
711#ifdef FEAT_NETBEANS_INTG
712 if (isNetbeansBuffer(curbuf)
713 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
714 {
715 int key = KEY2TERMCAP1(c);
716
717 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
718 || key == (int)KE_RIGHTRELEASE)
719 netbeans_button_release(which_button);
720 }
721#endif
722
723 // When jumping to another window, clear a pending operator. That's a bit
724 // friendlier than beeping and not jumping to that window.
725 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
726 clearop(oap);
727
728#ifdef FEAT_FOLDING
729 if (mod_mask == 0
730 && !is_drag
731 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
732 && which_button == MOUSE_LEFT)
733 {
734 // open or close a fold at this line
735 if (jump_flags & MOUSE_FOLD_OPEN)
736 openFold(curwin->w_cursor.lnum, 1L);
737 else
738 closeFold(curwin->w_cursor.lnum, 1L);
739 // don't move the cursor if still in the same window
740 if (curwin == old_curwin)
741 curwin->w_cursor = save_cursor;
742 }
743#endif
744
745#if defined(FEAT_CLIPBOARD) && defined(FEAT_CMDWIN)
746 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
747 {
748 clip_modeless(which_button, is_click, is_drag);
749 return FALSE;
750 }
751#endif
752
753 // Set global flag that we are extending the Visual area with mouse
754 // dragging; temporarily minimize 'scrolloff'.
755 if (VIsual_active && is_drag && get_scrolloff_value())
756 {
757 // In the very first line, allow scrolling one line
758 if (mouse_row == 0)
759 mouse_dragging = 2;
760 else
761 mouse_dragging = 1;
762 }
763
764 // When dragging the mouse above the window, scroll down.
765 if (is_drag && mouse_row < 0 && !in_status_line)
766 {
767 scroll_redraw(FALSE, 1L);
768 mouse_row = 0;
769 }
770
771 if (start_visual.lnum) // right click in visual mode
772 {
773 // When ALT is pressed make Visual mode blockwise.
774 if (mod_mask & MOD_MASK_ALT)
775 VIsual_mode = Ctrl_V;
776
777 // In Visual-block mode, divide the area in four, pick up the corner
778 // that is in the quarter that the cursor is in.
779 if (VIsual_mode == Ctrl_V)
780 {
781 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
782 if (curwin->w_curswant > (leftcol + rightcol) / 2)
783 end_visual.col = leftcol;
784 else
785 end_visual.col = rightcol;
786 if (curwin->w_cursor.lnum >=
787 (start_visual.lnum + end_visual.lnum) / 2)
788 end_visual.lnum = start_visual.lnum;
789
790 // move VIsual to the right column
791 start_visual = curwin->w_cursor; // save the cursor pos
792 curwin->w_cursor = end_visual;
793 coladvance(end_visual.col);
794 VIsual = curwin->w_cursor;
795 curwin->w_cursor = start_visual; // restore the cursor
796 }
797 else
798 {
799 // If the click is before the start of visual, change the start.
800 // If the click is after the end of visual, change the end. If
801 // the click is inside the visual, change the closest side.
802 if (LT_POS(curwin->w_cursor, start_visual))
803 VIsual = end_visual;
804 else if (LT_POS(end_visual, curwin->w_cursor))
805 VIsual = start_visual;
806 else
807 {
808 // In the same line, compare column number
809 if (end_visual.lnum == start_visual.lnum)
810 {
811 if (curwin->w_cursor.col - start_visual.col >
812 end_visual.col - curwin->w_cursor.col)
813 VIsual = start_visual;
814 else
815 VIsual = end_visual;
816 }
817
818 // In different lines, compare line number
819 else
820 {
821 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
822 (end_visual.lnum - curwin->w_cursor.lnum);
823
824 if (diff > 0) // closest to end
825 VIsual = start_visual;
826 else if (diff < 0) // closest to start
827 VIsual = end_visual;
828 else // in the middle line
829 {
830 if (curwin->w_cursor.col <
831 (start_visual.col + end_visual.col) / 2)
832 VIsual = end_visual;
833 else
834 VIsual = start_visual;
835 }
836 }
837 }
838 }
839 }
840 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100841 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200842 stuffcharReadbuff(Ctrl_O);
843
844 // Middle mouse click: Put text before cursor.
845 if (which_button == MOUSE_MIDDLE)
846 {
847#ifdef FEAT_CLIPBOARD
848 if (clip_star.available && regname == 0)
849 regname = '*';
850#endif
851 if (yank_register_mline(regname))
852 {
853 if (mouse_past_bottom)
854 dir = FORWARD;
855 }
856 else if (mouse_past_eol)
857 dir = FORWARD;
858
859 if (fixindent)
860 {
861 c1 = (dir == BACKWARD) ? '[' : ']';
862 c2 = 'p';
863 }
864 else
865 {
866 c1 = (dir == FORWARD) ? 'p' : 'P';
867 c2 = NUL;
868 }
869 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
870
871 // Remember where the paste started, so in edit() Insstart can be set
872 // to this position
873 if (restart_edit != 0)
874 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200875 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200876 }
877
878#if defined(FEAT_QUICKFIX)
879 // Ctrl-Mouse click or double click in a quickfix window jumps to the
880 // error under the mouse pointer.
881 else if (((mod_mask & MOD_MASK_CTRL)
882 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
883 && bt_quickfix(curbuf))
884 {
885 if (curwin->w_llist_ref == NULL) // quickfix window
886 do_cmdline_cmd((char_u *)".cc");
887 else // location list window
888 do_cmdline_cmd((char_u *)".ll");
889 got_click = FALSE; // ignore drag&release now
890 }
891#endif
892
893 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
894 // under the mouse pointer.
895 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
896 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
897 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100898 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200899 stuffcharReadbuff(Ctrl_O);
900 stuffcharReadbuff(Ctrl_RSB);
901 got_click = FALSE; // ignore drag&release now
902 }
903
904 // Shift-Mouse click searches for the next occurrence of the word under
905 // the mouse pointer
906 else if ((mod_mask & MOD_MASK_SHIFT))
907 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100908 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200909 stuffcharReadbuff(Ctrl_O);
910 if (which_button == MOUSE_LEFT)
911 stuffcharReadbuff('*');
912 else // MOUSE_RIGHT
913 stuffcharReadbuff('#');
914 }
915
916 // Handle double clicks, unless on status line
917 else if (in_status_line)
918 {
919#ifdef FEAT_MOUSESHAPE
920 if ((is_drag || is_click) && !drag_status_line)
921 {
922 drag_status_line = TRUE;
923 update_mouseshape(-1);
924 }
925#endif
926 }
927 else if (in_sep_line)
928 {
929#ifdef FEAT_MOUSESHAPE
930 if ((is_drag || is_click) && !drag_sep_line)
931 {
932 drag_sep_line = TRUE;
933 update_mouseshape(-1);
934 }
935#endif
936 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100937 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
938 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200939 && mouse_has(MOUSE_VISUAL))
940 {
941 if (is_click || !VIsual_active)
942 {
943 if (VIsual_active)
944 orig_cursor = VIsual;
945 else
946 {
947 check_visual_highlight();
948 VIsual = curwin->w_cursor;
949 orig_cursor = VIsual;
950 VIsual_active = TRUE;
951 VIsual_reselect = TRUE;
952 // start Select mode if 'selectmode' contains "mouse"
953 may_start_select('o');
954 setmouse();
955 }
956 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
957 {
958 // Double click with ALT pressed makes it blockwise.
959 if (mod_mask & MOD_MASK_ALT)
960 VIsual_mode = Ctrl_V;
961 else
962 VIsual_mode = 'v';
963 }
964 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
965 VIsual_mode = 'V';
966 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
967 VIsual_mode = Ctrl_V;
968#ifdef FEAT_CLIPBOARD
969 // Make sure the clipboard gets updated. Needed because start and
970 // end may still be the same, and the selection needs to be owned
971 clip_star.vmode = NUL;
972#endif
973 }
974 // A double click selects a word or a block.
975 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
976 {
977 pos_T *pos = NULL;
978 int gc;
979
980 if (is_click)
981 {
982 // If the character under the cursor (skipping white space) is
983 // not a word character, try finding a match and select a (),
984 // {}, [], #if/#endif, etc. block.
985 end_visual = curwin->w_cursor;
986 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
987 inc(&end_visual);
988 if (oap != NULL)
989 oap->motion_type = MCHAR;
990 if (oap != NULL
991 && VIsual_mode == 'v'
992 && !vim_iswordc(gchar_pos(&end_visual))
993 && EQUAL_POS(curwin->w_cursor, VIsual)
994 && (pos = findmatch(oap, NUL)) != NULL)
995 {
996 curwin->w_cursor = *pos;
997 if (oap->motion_type == MLINE)
998 VIsual_mode = 'V';
999 else if (*p_sel == 'e')
1000 {
1001 if (LT_POS(curwin->w_cursor, VIsual))
1002 ++VIsual.col;
1003 else
1004 ++curwin->w_cursor.col;
1005 }
1006 }
1007 }
1008
1009 if (pos == NULL && (is_click || is_drag))
1010 {
1011 // When not found a match or when dragging: extend to include
1012 // a word.
1013 if (LT_POS(curwin->w_cursor, orig_cursor))
1014 {
1015 find_start_of_word(&curwin->w_cursor);
1016 find_end_of_word(&VIsual);
1017 }
1018 else
1019 {
1020 find_start_of_word(&VIsual);
1021 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1022 curwin->w_cursor.col +=
1023 (*mb_ptr2len)(ml_get_cursor());
1024 find_end_of_word(&curwin->w_cursor);
1025 }
1026 }
1027 curwin->w_set_curswant = TRUE;
1028 }
1029 if (is_click)
1030 redraw_curbuf_later(INVERTED); // update the inversion
1031 }
1032 else if (VIsual_active && !old_active)
1033 {
1034 if (mod_mask & MOD_MASK_ALT)
1035 VIsual_mode = Ctrl_V;
1036 else
1037 VIsual_mode = 'v';
1038 }
1039
1040 // If Visual mode changed show it later.
1041 if ((!VIsual_active && old_active && mode_displayed)
1042 || (VIsual_active && p_smd && msg_silent == 0
1043 && (!old_active || VIsual_mode != old_mode)))
1044 redraw_cmdline = TRUE;
1045
1046 return moved;
1047}
1048
1049 void
1050ins_mouse(int c)
1051{
1052 pos_T tpos;
1053 win_T *old_curwin = curwin;
1054
1055# ifdef FEAT_GUI
1056 // When GUI is active, also move/paste when 'mouse' is empty
1057 if (!gui.in_use)
1058# endif
1059 if (!mouse_has(MOUSE_INSERT))
1060 return;
1061
1062 undisplay_dollar();
1063 tpos = curwin->w_cursor;
1064 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1065 {
1066 win_T *new_curwin = curwin;
1067
1068 if (curwin != old_curwin && win_valid(old_curwin))
1069 {
1070 // Mouse took us to another window. We need to go back to the
1071 // previous one to stop insert there properly.
1072 curwin = old_curwin;
1073 curbuf = curwin->w_buffer;
1074#ifdef FEAT_JOB_CHANNEL
1075 if (bt_prompt(curbuf))
1076 // Restart Insert mode when re-entering the prompt buffer.
1077 curbuf->b_prompt_insert = 'A';
1078#endif
1079 }
1080 start_arrow(curwin == old_curwin ? &tpos : NULL);
1081 if (curwin != new_curwin && win_valid(new_curwin))
1082 {
1083 curwin = new_curwin;
1084 curbuf = curwin->w_buffer;
1085 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001086 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001087 }
1088
1089 // redraw status lines (in case another window became active)
1090 redraw_statuslines();
1091}
1092
1093 void
1094ins_mousescroll(int dir)
1095{
1096 pos_T tpos;
1097 win_T *old_curwin = curwin, *wp;
1098 int did_scroll = FALSE;
1099
1100 tpos = curwin->w_cursor;
1101
1102 if (mouse_row >= 0 && mouse_col >= 0)
1103 {
1104 int row, col;
1105
1106 row = mouse_row;
1107 col = mouse_col;
1108
1109 // find the window at the pointer coordinates
1110 wp = mouse_find_win(&row, &col, FIND_POPUP);
1111 if (wp == NULL)
1112 return;
1113 curwin = wp;
1114 curbuf = curwin->w_buffer;
1115 }
1116 if (curwin == old_curwin)
1117 undisplay_dollar();
1118
1119 // Don't scroll the window in which completion is being done.
1120 if (!pum_visible() || curwin != old_curwin)
1121 {
LemonBoyc27747e2022-05-07 12:25:40 +01001122 long step;
1123
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001124 if (dir == MSCR_DOWN || dir == MSCR_UP)
1125 {
LemonBoyc27747e2022-05-07 12:25:40 +01001126 if (mouse_vert_step < 0
1127 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1128 step = (long)(curwin->w_botline - curwin->w_topline);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001129 else
LemonBoyc27747e2022-05-07 12:25:40 +01001130 step = mouse_vert_step;
1131 scroll_redraw(dir, step);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001132# ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001133 if (WIN_IS_POPUP(curwin))
1134 popup_set_firstline(curwin);
1135# endif
1136 }
1137#ifdef FEAT_GUI
1138 else
1139 {
LemonBoyc27747e2022-05-07 12:25:40 +01001140 int val;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001141
LemonBoyc27747e2022-05-07 12:25:40 +01001142 if (mouse_hor_step < 0
1143 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001144 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01001145 else
1146 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001147 val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
1148 if (val < 0)
1149 val = 0;
1150 gui_do_horiz_scroll(val, TRUE);
1151 }
1152#endif
1153 did_scroll = TRUE;
LemonBoy66e13ae2022-04-21 22:52:11 +01001154 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001155 }
1156
1157 curwin->w_redr_status = TRUE;
1158
1159 curwin = old_curwin;
1160 curbuf = curwin->w_buffer;
1161
1162 // The popup menu may overlay the window, need to redraw it.
1163 // TODO: Would be more efficient to only redraw the windows that are
1164 // overlapped by the popup menu.
1165 if (pum_visible() && did_scroll)
1166 {
1167 redraw_all_later(NOT_VALID);
1168 ins_compl_show_pum();
1169 }
1170
1171 if (!EQUAL_POS(curwin->w_cursor, tpos))
1172 {
1173 start_arrow(&tpos);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001174 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001175 }
1176}
1177
1178/*
1179 * Return TRUE if "c" is a mouse key.
1180 */
1181 int
1182is_mouse_key(int c)
1183{
1184 return c == K_LEFTMOUSE
1185 || c == K_LEFTMOUSE_NM
1186 || c == K_LEFTDRAG
1187 || c == K_LEFTRELEASE
1188 || c == K_LEFTRELEASE_NM
1189 || c == K_MOUSEMOVE
1190 || c == K_MIDDLEMOUSE
1191 || c == K_MIDDLEDRAG
1192 || c == K_MIDDLERELEASE
1193 || c == K_RIGHTMOUSE
1194 || c == K_RIGHTDRAG
1195 || c == K_RIGHTRELEASE
1196 || c == K_MOUSEDOWN
1197 || c == K_MOUSEUP
1198 || c == K_MOUSELEFT
1199 || c == K_MOUSERIGHT
1200 || c == K_X1MOUSE
1201 || c == K_X1DRAG
1202 || c == K_X1RELEASE
1203 || c == K_X2MOUSE
1204 || c == K_X2DRAG
1205 || c == K_X2RELEASE;
1206}
1207
1208static struct mousetable
1209{
1210 int pseudo_code; // Code for pseudo mouse event
1211 int button; // Which mouse button is it?
1212 int is_click; // Is it a mouse button click event?
1213 int is_drag; // Is it a mouse drag event?
1214} mouse_table[] =
1215{
1216 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1217#ifdef FEAT_GUI
1218 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1219#endif
1220 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1221 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1222#ifdef FEAT_GUI
1223 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1224#endif
1225 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1226 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1227 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1228 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1229 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1230 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1231 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1232 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1233 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1234 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1235 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1236 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1237 // DRAG without CLICK
1238 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1239 // RELEASE without CLICK
1240 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1241 {0, 0, 0, 0},
1242};
1243
1244/*
1245 * Look up the given mouse code to return the relevant information in the other
1246 * arguments. Return which button is down or was released.
1247 */
1248 int
1249get_mouse_button(int code, int *is_click, int *is_drag)
1250{
1251 int i;
1252
1253 for (i = 0; mouse_table[i].pseudo_code; i++)
1254 if (code == mouse_table[i].pseudo_code)
1255 {
1256 *is_click = mouse_table[i].is_click;
1257 *is_drag = mouse_table[i].is_drag;
1258 return mouse_table[i].button;
1259 }
1260 return 0; // Shouldn't get here
1261}
1262
1263/*
1264 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1265 * the given information about which mouse button is down, and whether the
1266 * mouse was clicked, dragged or released.
1267 */
1268 int
1269get_pseudo_mouse_code(
1270 int button, // eg MOUSE_LEFT
1271 int is_click,
1272 int is_drag)
1273{
1274 int i;
1275
1276 for (i = 0; mouse_table[i].pseudo_code; i++)
1277 if (button == mouse_table[i].button
1278 && is_click == mouse_table[i].is_click
1279 && is_drag == mouse_table[i].is_drag)
1280 {
1281#ifdef FEAT_GUI
1282 // Trick: a non mappable left click and release has mouse_col -1
1283 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1284 // gui_mouse_moved()
1285 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1286 {
1287 if (mouse_col < 0)
1288 mouse_col = 0;
1289 else
1290 mouse_col -= MOUSE_COLOFF;
1291 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1292 return (int)KE_LEFTMOUSE_NM;
1293 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1294 return (int)KE_LEFTRELEASE_NM;
1295 }
1296#endif
1297 return mouse_table[i].pseudo_code;
1298 }
1299 return (int)KE_IGNORE; // not recognized, ignore it
1300}
1301
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001302# define HMT_NORMAL 1
1303# define HMT_NETTERM 2
1304# define HMT_DEC 4
1305# define HMT_JSBTERM 8
1306# define HMT_PTERM 16
1307# define HMT_URXVT 32
1308# define HMT_GPM 64
1309# define HMT_SGR 128
1310# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001311static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001312
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001313 void
1314set_mouse_termcode(
1315 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1316 char_u *s)
1317{
1318 char_u name[2];
1319
1320 name[0] = n;
1321 name[1] = KE_FILLER;
1322 add_termcode(name, s, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001323# ifdef FEAT_MOUSE_JSB
1324 if (n == KS_JSBTERM_MOUSE)
1325 has_mouse_termcode |= HMT_JSBTERM;
1326 else
1327# endif
1328# ifdef FEAT_MOUSE_NET
1329 if (n == KS_NETTERM_MOUSE)
1330 has_mouse_termcode |= HMT_NETTERM;
1331 else
1332# endif
1333# ifdef FEAT_MOUSE_DEC
1334 if (n == KS_DEC_MOUSE)
1335 has_mouse_termcode |= HMT_DEC;
1336 else
1337# endif
1338# ifdef FEAT_MOUSE_PTERM
1339 if (n == KS_PTERM_MOUSE)
1340 has_mouse_termcode |= HMT_PTERM;
1341 else
1342# endif
1343# ifdef FEAT_MOUSE_URXVT
1344 if (n == KS_URXVT_MOUSE)
1345 has_mouse_termcode |= HMT_URXVT;
1346 else
1347# endif
1348# ifdef FEAT_MOUSE_GPM
1349 if (n == KS_GPM_MOUSE)
1350 has_mouse_termcode |= HMT_GPM;
1351 else
1352# endif
1353 if (n == KS_SGR_MOUSE)
1354 has_mouse_termcode |= HMT_SGR;
1355 else if (n == KS_SGR_MOUSE_RELEASE)
1356 has_mouse_termcode |= HMT_SGR_REL;
1357 else
1358 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001359}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001360
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001361# if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001362 void
1363del_mouse_termcode(
1364 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1365{
1366 char_u name[2];
1367
1368 name[0] = n;
1369 name[1] = KE_FILLER;
1370 del_termcode(name);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001371# ifdef FEAT_MOUSE_JSB
1372 if (n == KS_JSBTERM_MOUSE)
1373 has_mouse_termcode &= ~HMT_JSBTERM;
1374 else
1375# endif
1376# ifdef FEAT_MOUSE_NET
1377 if (n == KS_NETTERM_MOUSE)
1378 has_mouse_termcode &= ~HMT_NETTERM;
1379 else
1380# endif
1381# ifdef FEAT_MOUSE_DEC
1382 if (n == KS_DEC_MOUSE)
1383 has_mouse_termcode &= ~HMT_DEC;
1384 else
1385# endif
1386# ifdef FEAT_MOUSE_PTERM
1387 if (n == KS_PTERM_MOUSE)
1388 has_mouse_termcode &= ~HMT_PTERM;
1389 else
1390# endif
1391# ifdef FEAT_MOUSE_URXVT
1392 if (n == KS_URXVT_MOUSE)
1393 has_mouse_termcode &= ~HMT_URXVT;
1394 else
1395# endif
1396# ifdef FEAT_MOUSE_GPM
1397 if (n == KS_GPM_MOUSE)
1398 has_mouse_termcode &= ~HMT_GPM;
1399 else
1400# endif
1401 if (n == KS_SGR_MOUSE)
1402 has_mouse_termcode &= ~HMT_SGR;
1403 else if (n == KS_SGR_MOUSE_RELEASE)
1404 has_mouse_termcode &= ~HMT_SGR_REL;
1405 else
1406 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001407}
1408# endif
1409
1410/*
1411 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1412 */
1413 void
1414setmouse(void)
1415{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001416 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001417
1418# ifdef FEAT_MOUSESHAPE
1419 update_mouseshape(-1);
1420# endif
1421
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001422 // Should be outside proc, but may break MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001423# ifdef FEAT_GUI
1424 // In the GUI the mouse is always enabled.
1425 if (gui.in_use)
1426 return;
1427# endif
1428 // be quick when mouse is off
1429 if (*p_mouse == NUL || has_mouse_termcode == 0)
1430 return;
1431
1432 // don't switch mouse on when not in raw mode (Ex mode)
1433 if (cur_tmode != TMODE_RAW)
1434 {
1435 mch_setmouse(FALSE);
1436 return;
1437 }
1438
1439 if (VIsual_active)
1440 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001441 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1442 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001443 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001444 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001445 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001446 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001447 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001448 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001449 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1450 else
1451 checkfor = MOUSE_NORMAL; // assume normal mode
1452
1453 if (mouse_has(checkfor))
1454 mch_setmouse(TRUE);
1455 else
1456 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001457}
1458
1459/*
1460 * Return TRUE if
1461 * - "c" is in 'mouse', or
1462 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1463 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1464 * normal editing mode (not at hit-return message).
1465 */
1466 int
1467mouse_has(int c)
1468{
1469 char_u *p;
1470
1471 for (p = p_mouse; *p; ++p)
1472 switch (*p)
1473 {
1474 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1475 return TRUE;
1476 break;
1477 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1478 return TRUE;
1479 break;
1480 default: if (c == *p) return TRUE; break;
1481 }
1482 return FALSE;
1483}
1484
1485/*
1486 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1487 */
1488 int
1489mouse_model_popup(void)
1490{
1491 return (p_mousem[0] == 'p');
1492}
1493
1494/*
1495 * Move the cursor to the specified row and column on the screen.
1496 * Change current window if necessary. Returns an integer with the
1497 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1498 *
1499 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1500 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1501 *
1502 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1503 * if the mouse is outside the window then the text will scroll, or if the
1504 * mouse was previously on a status line, then the status line may be dragged.
1505 *
1506 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1507 * cursor is moved unless the cursor was on a status line.
1508 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1509 * IN_SEP_LINE depending on where the cursor was clicked.
1510 *
1511 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1512 * the mouse is on the status line of the same window.
1513 *
1514 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1515 * the last call.
1516 *
1517 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1518 * remembered.
1519 */
1520 int
1521jump_to_mouse(
1522 int flags,
1523 int *inclusive, // used for inclusive operator, can be NULL
1524 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1525{
1526 static int on_status_line = 0; // #lines below bottom of window
1527 static int on_sep_line = 0; // on separator right of window
1528#ifdef FEAT_MENU
1529 static int in_winbar = FALSE;
1530#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001531#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001532 static int in_popup_win = FALSE;
1533 static win_T *click_in_popup_win = NULL;
1534#endif
1535 static int prev_row = -1;
1536 static int prev_col = -1;
1537 static win_T *dragwin = NULL; // window being dragged
1538 static int did_drag = FALSE; // drag was noticed
1539
1540 win_T *wp, *old_curwin;
1541 pos_T old_cursor;
1542 int count;
1543 int first;
1544 int row = mouse_row;
1545 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001546 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001547#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001548 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001549#endif
1550
1551 mouse_past_bottom = FALSE;
1552 mouse_past_eol = FALSE;
1553
1554 if (flags & MOUSE_RELEASED)
1555 {
1556 // On button release we may change window focus if positioned on a
1557 // status line and no dragging happened.
1558 if (dragwin != NULL && !did_drag)
1559 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1560 dragwin = NULL;
1561 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001562#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001563 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1564 popup_close_for_mouse_click(click_in_popup_win);
1565
1566 popup_dragwin = NULL;
1567 click_in_popup_win = NULL;
1568#endif
1569 }
1570
1571 if ((flags & MOUSE_DID_MOVE)
1572 && prev_row == mouse_row
1573 && prev_col == mouse_col)
1574 {
1575retnomove:
1576 // before moving the cursor for a left click which is NOT in a status
1577 // line, stop Visual mode
1578 if (on_status_line)
1579 return IN_STATUS_LINE;
1580 if (on_sep_line)
1581 return IN_SEP_LINE;
1582#ifdef FEAT_MENU
1583 if (in_winbar)
1584 {
1585 // A quick second click may arrive as a double-click, but we use it
1586 // as a second click in the WinBar.
1587 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1588 {
1589 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1590 if (wp == NULL)
1591 return IN_UNKNOWN;
1592 winbar_click(wp, col);
1593 }
1594 return IN_OTHER_WIN | MOUSE_WINBAR;
1595 }
1596#endif
1597 if (flags & MOUSE_MAY_STOP_VIS)
1598 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001599 end_visual_mode_keep_button();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001600 redraw_curbuf_later(INVERTED); // delete the inversion
1601 }
1602#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1603 // Continue a modeless selection in another window.
1604 if (cmdwin_type != 0 && row < curwin->w_winrow)
1605 return IN_OTHER_WIN;
1606#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001607#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001608 // Continue a modeless selection in a popup window or dragging it.
1609 if (in_popup_win)
1610 {
1611 click_in_popup_win = NULL; // don't close it on release
1612 if (popup_dragwin != NULL)
1613 {
1614 // dragging a popup window
1615 popup_drag(popup_dragwin);
1616 return IN_UNKNOWN;
1617 }
1618 return IN_OTHER_WIN;
1619 }
1620#endif
1621 return IN_BUFFER;
1622 }
1623
1624 prev_row = mouse_row;
1625 prev_col = mouse_col;
1626
1627 if (flags & MOUSE_SETPOS)
1628 goto retnomove; // ugly goto...
1629
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001630 old_curwin = curwin;
1631 old_cursor = curwin->w_cursor;
1632
1633 if (!(flags & MOUSE_FOCUS))
1634 {
1635 if (row < 0 || col < 0) // check if it makes sense
1636 return IN_UNKNOWN;
1637
1638 // find the window where the row is in and adjust "row" and "col" to be
1639 // relative to top-left of the window
1640 wp = mouse_find_win(&row, &col, FIND_POPUP);
1641 if (wp == NULL)
1642 return IN_UNKNOWN;
1643 dragwin = NULL;
1644
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001645#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001646 // Click in a popup window may start dragging or modeless selection,
1647 // but not much else.
1648 if (WIN_IS_POPUP(wp))
1649 {
1650 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001651 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001652 in_popup_win = TRUE;
1653 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1654 {
1655 return IN_UNKNOWN;
1656 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001657 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001658 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001659 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001660 {
1661 popup_dragwin = wp;
1662 popup_start_drag(wp, row, col);
1663 return IN_UNKNOWN;
1664 }
1665 // Only close on release, otherwise it's not possible to drag or do
1666 // modeless selection.
1667 else if (wp->w_popup_close == POPCLOSE_CLICK
1668 && which_button == MOUSE_LEFT)
1669 {
1670 click_in_popup_win = wp;
1671 }
1672 else if (which_button == MOUSE_LEFT)
1673 // If the click is in the scrollbar, may scroll up/down.
1674 popup_handle_scrollbar_click(wp, row, col);
1675# ifdef FEAT_CLIPBOARD
1676 return IN_OTHER_WIN;
1677# else
1678 return IN_UNKNOWN;
1679# endif
1680 }
1681 in_popup_win = FALSE;
1682 popup_dragwin = NULL;
1683#endif
1684#ifdef FEAT_MENU
1685 if (row == -1)
1686 {
1687 // A click in the window toolbar does not enter another window or
1688 // change Visual highlighting.
1689 winbar_click(wp, col);
1690 in_winbar = TRUE;
1691 return IN_OTHER_WIN | MOUSE_WINBAR;
1692 }
1693 in_winbar = FALSE;
1694#endif
1695
1696 // winpos and height may change in win_enter()!
1697 if (row >= wp->w_height) // In (or below) status line
1698 {
1699 on_status_line = row - wp->w_height + 1;
1700 dragwin = wp;
1701 }
1702 else
1703 on_status_line = 0;
1704 if (col >= wp->w_width) // In separator line
1705 {
1706 on_sep_line = col - wp->w_width + 1;
1707 dragwin = wp;
1708 }
1709 else
1710 on_sep_line = 0;
1711
1712 // The rightmost character of the status line might be a vertical
1713 // separator character if there is no connecting window to the right.
1714 if (on_status_line && on_sep_line)
1715 {
1716 if (stl_connected(wp))
1717 on_sep_line = 0;
1718 else
1719 on_status_line = 0;
1720 }
1721
1722 // Before jumping to another buffer, or moving the cursor for a left
1723 // click, stop Visual mode.
1724 if (VIsual_active
1725 && (wp->w_buffer != curwin->w_buffer
1726 || (!on_status_line && !on_sep_line
1727#ifdef FEAT_FOLDING
1728 && (
1729# ifdef FEAT_RIGHTLEFT
1730 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1731# endif
1732 col >= wp->w_p_fdc
1733# ifdef FEAT_CMDWIN
1734 + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
1735# endif
1736 )
1737#endif
1738 && (flags & MOUSE_MAY_STOP_VIS))))
1739 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001740 end_visual_mode_keep_button();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001741 redraw_curbuf_later(INVERTED); // delete the inversion
1742 }
1743#ifdef FEAT_CMDWIN
1744 if (cmdwin_type != 0 && wp != curwin)
1745 {
1746 // A click outside the command-line window: Use modeless
1747 // selection if possible. Allow dragging the status lines.
1748 on_sep_line = 0;
1749# ifdef FEAT_CLIPBOARD
1750 if (on_status_line)
1751 return IN_STATUS_LINE;
1752 return IN_OTHER_WIN;
1753# else
1754 row = 0;
1755 col += wp->w_wincol;
1756 wp = curwin;
1757# endif
1758 }
1759#endif
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001760#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1761 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1762 // terminal in popup window: don't jump to another window
1763 return IN_OTHER_WIN;
1764#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001765 // Only change window focus when not clicking on or dragging the
1766 // status line. Do change focus when releasing the mouse button
1767 // (MOUSE_FOCUS was set above if we dragged first).
1768 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1769 win_enter(wp, TRUE); // can make wp invalid!
1770
1771 if (curwin != old_curwin)
1772 {
1773#ifdef CHECK_DOUBLE_CLICK
1774 // set topline, to be able to check for double click ourselves
1775 set_mouse_topline(curwin);
1776#endif
1777#ifdef FEAT_TERMINAL
1778 // when entering a terminal window may change state
1779 term_win_entered();
1780#endif
1781 }
1782 if (on_status_line) // In (or below) status line
1783 {
1784 // Don't use start_arrow() if we're in the same window
1785 if (curwin == old_curwin)
1786 return IN_STATUS_LINE;
1787 else
1788 return IN_STATUS_LINE | CURSOR_MOVED;
1789 }
1790 if (on_sep_line) // In (or below) status line
1791 {
1792 // Don't use start_arrow() if we're in the same window
1793 if (curwin == old_curwin)
1794 return IN_SEP_LINE;
1795 else
1796 return IN_SEP_LINE | CURSOR_MOVED;
1797 }
1798
1799 curwin->w_cursor.lnum = curwin->w_topline;
1800#ifdef FEAT_GUI
1801 // remember topline, needed for double click
1802 gui_prev_topline = curwin->w_topline;
1803# ifdef FEAT_DIFF
1804 gui_prev_topfill = curwin->w_topfill;
1805# endif
1806#endif
1807 }
1808 else if (on_status_line && which_button == MOUSE_LEFT)
1809 {
1810 if (dragwin != NULL)
1811 {
1812 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001813 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001814 - on_status_line;
1815 win_drag_status_line(dragwin, count);
1816 did_drag |= count;
1817 }
1818 return IN_STATUS_LINE; // Cursor didn't move
1819 }
1820 else if (on_sep_line && which_button == MOUSE_LEFT)
1821 {
1822 if (dragwin != NULL)
1823 {
1824 // Drag the separator column
1825 count = col - dragwin->w_wincol - dragwin->w_width + 1
1826 - on_sep_line;
1827 win_drag_vsep_line(dragwin, count);
1828 did_drag |= count;
1829 }
1830 return IN_SEP_LINE; // Cursor didn't move
1831 }
1832#ifdef FEAT_MENU
1833 else if (in_winbar)
1834 {
1835 // After a click on the window toolbar don't start Visual mode.
1836 return IN_OTHER_WIN | MOUSE_WINBAR;
1837 }
1838#endif
1839 else // keep_window_focus must be TRUE
1840 {
1841 // before moving the cursor for a left click, stop Visual mode
1842 if (flags & MOUSE_MAY_STOP_VIS)
1843 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001844 end_visual_mode_keep_button();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001845 redraw_curbuf_later(INVERTED); // delete the inversion
1846 }
1847
1848#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1849 // Continue a modeless selection in another window.
1850 if (cmdwin_type != 0 && row < curwin->w_winrow)
1851 return IN_OTHER_WIN;
1852#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001853#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001854 if (in_popup_win)
1855 {
1856 if (popup_dragwin != NULL)
1857 {
1858 // dragging a popup window
1859 popup_drag(popup_dragwin);
1860 return IN_UNKNOWN;
1861 }
1862 // continue a modeless selection in a popup window
1863 click_in_popup_win = NULL;
1864 return IN_OTHER_WIN;
1865 }
1866#endif
1867
1868 row -= W_WINROW(curwin);
1869 col -= curwin->w_wincol;
1870
1871 // When clicking beyond the end of the window, scroll the screen.
1872 // Scroll by however many rows outside the window we are.
1873 if (row < 0)
1874 {
1875 count = 0;
1876 for (first = TRUE; curwin->w_topline > 1; )
1877 {
1878#ifdef FEAT_DIFF
1879 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1880 ++count;
1881 else
1882#endif
1883 count += plines(curwin->w_topline - 1);
1884 if (!first && count > -row)
1885 break;
1886 first = FALSE;
1887#ifdef FEAT_FOLDING
1888 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1889#endif
1890#ifdef FEAT_DIFF
1891 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1892 ++curwin->w_topfill;
1893 else
1894#endif
1895 {
1896 --curwin->w_topline;
1897#ifdef FEAT_DIFF
1898 curwin->w_topfill = 0;
1899#endif
1900 }
1901 }
1902#ifdef FEAT_DIFF
1903 check_topfill(curwin, FALSE);
1904#endif
1905 curwin->w_valid &=
1906 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1907 redraw_later(VALID);
1908 row = 0;
1909 }
1910 else if (row >= curwin->w_height)
1911 {
1912 count = 0;
1913 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
1914 {
1915#ifdef FEAT_DIFF
1916 if (curwin->w_topfill > 0)
1917 ++count;
1918 else
1919#endif
1920 count += plines(curwin->w_topline);
1921 if (!first && count > row - curwin->w_height + 1)
1922 break;
1923 first = FALSE;
1924#ifdef FEAT_FOLDING
1925 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
1926 && curwin->w_topline == curbuf->b_ml.ml_line_count)
1927 break;
1928#endif
1929#ifdef FEAT_DIFF
1930 if (curwin->w_topfill > 0)
1931 --curwin->w_topfill;
1932 else
1933#endif
1934 {
1935 ++curwin->w_topline;
1936#ifdef FEAT_DIFF
1937 curwin->w_topfill =
1938 diff_check_fill(curwin, curwin->w_topline);
1939#endif
1940 }
1941 }
1942#ifdef FEAT_DIFF
1943 check_topfill(curwin, FALSE);
1944#endif
1945 redraw_later(VALID);
1946 curwin->w_valid &=
1947 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1948 row = curwin->w_height - 1;
1949 }
1950 else if (row == 0)
1951 {
1952 // When dragging the mouse, while the text has been scrolled up as
1953 // far as it goes, moving the mouse in the top line should scroll
1954 // the text down (done later when recomputing w_topline).
1955 if (mouse_dragging > 0
1956 && curwin->w_cursor.lnum
1957 == curwin->w_buffer->b_ml.ml_line_count
1958 && curwin->w_cursor.lnum == curwin->w_topline)
1959 curwin->w_valid &= ~(VALID_TOPLINE);
1960 }
1961 }
1962
Bram Moolenaarb9081882022-07-09 04:56:24 +01001963 if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns
1964 && ScreenLines != NULL)
1965 {
1966 int off = LineOffset[prev_row] + prev_col;
1967
1968 // Only use ScreenCols[] after the window was redrawn. Mainly matters
1969 // for tests, a user would not click before redrawing.
1970 if (curwin->w_redr_type <= VALID_NO_UPDATE)
1971 col_from_screen = ScreenCols[off];
1972#ifdef FEAT_FOLDING
1973 // Remember the character under the mouse, it might be a '-' or '+' in
1974 // the fold column.
1975 mouse_char = ScreenLines[off];
1976#endif
1977 }
1978
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001979#ifdef FEAT_FOLDING
1980 // Check for position outside of the fold column.
1981 if (
1982# ifdef FEAT_RIGHTLEFT
1983 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
1984# endif
1985 col >= curwin->w_p_fdc
1986# ifdef FEAT_CMDWIN
1987 + (cmdwin_type == 0 ? 0 : 1)
1988# endif
1989 )
1990 mouse_char = ' ';
1991#endif
1992
1993 // compute the position in the buffer line from the posn on the screen
1994 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
1995 mouse_past_bottom = TRUE;
1996
1997 // Start Visual mode before coladvance(), for when 'sel' != "old"
1998 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
1999 {
2000 check_visual_highlight();
2001 VIsual = old_cursor;
2002 VIsual_active = TRUE;
2003 VIsual_reselect = TRUE;
2004 // if 'selectmode' contains "mouse", start Select mode
2005 may_start_select('o');
2006 setmouse();
2007 if (p_smd && msg_silent == 0)
2008 redraw_cmdline = TRUE; // show visual mode later
2009 }
2010
Bram Moolenaarb9081882022-07-09 04:56:24 +01002011 if (col_from_screen >= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002012 {
Bram Moolenaarb9081882022-07-09 04:56:24 +01002013 // Use the column from ScreenCols[], it is accurate also after
2014 // concealed characters.
2015 curwin->w_cursor.col = col_from_screen;
2016 if (col_from_screen == MAXCOL)
2017 {
2018 curwin->w_curswant = col_from_screen;
2019 curwin->w_set_curswant = FALSE; // May still have been TRUE
2020 mouse_past_eol = TRUE;
2021 if (inclusive != NULL)
2022 *inclusive = TRUE;
2023 }
2024 else
2025 {
2026 curwin->w_set_curswant = TRUE;
2027 if (inclusive != NULL)
2028 *inclusive = FALSE;
2029 }
2030 check_cursor_col();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002031 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002032 else
2033 {
2034 curwin->w_curswant = col;
2035 curwin->w_set_curswant = FALSE; // May still have been TRUE
2036 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2037 {
2038 if (inclusive != NULL)
2039 *inclusive = TRUE;
2040 mouse_past_eol = TRUE;
2041 }
2042 else if (inclusive != NULL)
2043 *inclusive = FALSE;
2044 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002045
2046 count = IN_BUFFER;
2047 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2048 || curwin->w_cursor.col != old_cursor.col)
2049 count |= CURSOR_MOVED; // Cursor has moved
2050
2051# ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002052 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002053 count |= MOUSE_FOLD_OPEN;
2054 else if (mouse_char != ' ')
2055 count |= MOUSE_FOLD_CLOSE;
2056# endif
2057
2058 return count;
2059}
2060
2061/*
LemonBoyc27747e2022-05-07 12:25:40 +01002062 * Mouse scroll wheel: Default action is to scroll mouse_vert_step lines (or
2063 * mouse_hor_step, depending on the scroll direction), or one page when Shift or
2064 * Ctrl is used.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002065 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
2066 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
2067 */
2068 void
2069nv_mousescroll(cmdarg_T *cap)
2070{
2071 win_T *old_curwin = curwin, *wp;
2072
2073 if (mouse_row >= 0 && mouse_col >= 0)
2074 {
2075 int row, col;
2076
2077 row = mouse_row;
2078 col = mouse_col;
2079
2080 // find the window at the pointer coordinates
2081 wp = mouse_find_win(&row, &col, FIND_POPUP);
2082 if (wp == NULL)
2083 return;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002084#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002085 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
2086 return;
2087#endif
2088 curwin = wp;
2089 curbuf = curwin->w_buffer;
2090 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002091 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
2092 {
2093# ifdef FEAT_TERMINAL
2094 if (term_use_loop())
2095 // This window is a terminal window, send the mouse event there.
2096 // Set "typed" to FALSE to avoid an endless loop.
Bram Moolenaar1e814bc2019-11-03 21:19:41 +01002097 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002098 else
2099# endif
LemonBoyc27747e2022-05-07 12:25:40 +01002100 if (mouse_vert_step < 0 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002101 {
2102 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
2103 }
2104 else
2105 {
2106 // Don't scroll more than half the window height.
LemonBoyc27747e2022-05-07 12:25:40 +01002107 if (curwin->w_height < mouse_vert_step * 2)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002108 {
2109 cap->count1 = curwin->w_height / 2;
2110 if (cap->count1 == 0)
2111 cap->count1 = 1;
2112 }
2113 else
LemonBoyc27747e2022-05-07 12:25:40 +01002114 cap->count1 = mouse_vert_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002115 cap->count0 = cap->count1;
2116 nv_scroll_line(cap);
2117 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002118#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002119 if (WIN_IS_POPUP(curwin))
2120 popup_set_firstline(curwin);
2121#endif
2122 }
2123# ifdef FEAT_GUI
2124 else
2125 {
2126 // Horizontal scroll - only allowed when 'wrap' is disabled
2127 if (!curwin->w_p_wrap)
2128 {
LemonBoyc27747e2022-05-07 12:25:40 +01002129 int val, step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002130
LemonBoyc27747e2022-05-07 12:25:40 +01002131 if (mouse_hor_step < 0
2132 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002133 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01002134 else
2135 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002136 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
2137 if (val < 0)
2138 val = 0;
2139
2140 gui_do_horiz_scroll(val, TRUE);
2141 }
2142 }
2143# endif
2144# ifdef FEAT_SYN_HL
2145 if (curwin != old_curwin && curwin->w_p_cul)
2146 redraw_for_cursorline(curwin);
2147# endif
LemonBoy66e13ae2022-04-21 22:52:11 +01002148 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002149
2150 curwin->w_redr_status = TRUE;
2151
2152 curwin = old_curwin;
2153 curbuf = curwin->w_buffer;
2154}
2155
2156/*
2157 * Mouse clicks and drags.
2158 */
2159 void
2160nv_mouse(cmdarg_T *cap)
2161{
2162 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2163}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002164
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002165static int held_button = MOUSE_RELEASE;
2166
2167 void
2168reset_held_button()
2169{
2170 held_button = MOUSE_RELEASE;
2171}
2172
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002173/*
2174 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2175 * modifiers found in typebuf in 'modifiers'.
2176 */
2177 int
2178check_termcode_mouse(
2179 char_u *tp,
2180 int *slen,
2181 char_u *key_name,
2182 char_u *modifiers_start,
2183 int idx,
2184 int *modifiers)
2185{
2186 int j;
2187 char_u *p;
2188# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2189 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2190 char_u bytes[6];
2191 int num_bytes;
2192# endif
2193 int mouse_code = 0; // init for GCC
2194 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002195 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002196 int wheel_code = 0;
2197 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002198 static int orig_num_clicks = 1;
2199 static int orig_mouse_code = 0x0;
2200# ifdef CHECK_DOUBLE_CLICK
2201 static int orig_mouse_col = 0;
2202 static int orig_mouse_row = 0;
2203 static struct timeval orig_mouse_time = {0, 0};
2204 // time of previous mouse click
2205 struct timeval mouse_time; // time of current mouse click
2206 long timediff; // elapsed time in msec
2207# endif
2208
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002209 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002210
2211# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2212 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2213 if (key_name[0] == KS_MOUSE
2214# ifdef FEAT_MOUSE_GPM
2215 || key_name[0] == KS_GPM_MOUSE
2216# endif
2217 )
2218 {
2219 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002220 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002221 * 0x20 = left button down
2222 * 0x21 = middle button down
2223 * 0x22 = right button down
2224 * 0x23 = any button release
2225 * 0x60 = button 4 down (scroll wheel down)
2226 * 0x61 = button 5 down (scroll wheel up)
2227 * add 0x04 for SHIFT
2228 * add 0x08 for ALT
2229 * add 0x10 for CTRL
2230 * add 0x20 for mouse drag (0x40 is drag with left button)
2231 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2232 * 0x43 (drag + release) is also move
2233 * c == column + ' ' + 1 == column + 33
2234 * r == row + ' ' + 1 == row + 33
2235 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002236 * The coordinates are passed on through global variables. Ugly, but
2237 * this avoids trouble with mouse clicks at an unexpected moment and
2238 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002239 */
2240 for (;;)
2241 {
2242# ifdef FEAT_GUI
2243 if (gui.in_use)
2244 {
2245 // GUI uses more bits for columns > 223
2246 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2247 if (num_bytes == -1) // not enough coordinates
2248 return -1;
2249 mouse_code = bytes[0];
2250 mouse_col = 128 * (bytes[1] - ' ' - 1)
2251 + bytes[2] - ' ' - 1;
2252 mouse_row = 128 * (bytes[3] - ' ' - 1)
2253 + bytes[4] - ' ' - 1;
2254 }
2255 else
2256# endif
2257 {
2258 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2259 if (num_bytes == -1) // not enough coordinates
2260 return -1;
2261 mouse_code = bytes[0];
2262 mouse_col = bytes[1] - ' ' - 1;
2263 mouse_row = bytes[2] - ' ' - 1;
2264 }
2265 *slen += num_bytes;
2266
Bram Moolenaar13c04632020-07-12 13:47:42 +02002267 // If the following bytes is also a mouse code and it has the same
2268 // code, dump this one and get the next. This makes dragging a
2269 // whole lot faster.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002270# ifdef FEAT_GUI
2271 if (gui.in_use)
2272 j = 3;
2273 else
2274# endif
2275 j = get_termcode_len(idx);
2276 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2277 && tp[*slen + j] == mouse_code
2278 && tp[*slen + j + 1] != NUL
2279 && tp[*slen + j + 2] != NUL
2280# ifdef FEAT_GUI
2281 && (!gui.in_use
2282 || (tp[*slen + j + 3] != NUL
2283 && tp[*slen + j + 4] != NUL))
2284# endif
2285 )
2286 *slen += j;
2287 else
2288 break;
2289 }
2290 }
2291
2292 if (key_name[0] == KS_URXVT_MOUSE
2293 || key_name[0] == KS_SGR_MOUSE
2294 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2295 {
2296 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002297 // Almost identical to xterm mouse mode, except the values are decimal
2298 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002299 //
2300 // \033[%d;%d;%dM
2301 // ^-- row
2302 // ^----- column
2303 // ^-------- code
2304 //
2305 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002306 // Almost identical to xterm mouse mode, except the values are decimal
2307 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002308 //
2309 // \033[<%d;%d;%dM
2310 // ^-- row
2311 // ^----- column
2312 // ^-------- code
2313 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002314 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002315 // ^-- row
2316 // ^----- column
2317 // ^-------- code
2318 p = modifiers_start;
2319 if (p == NULL)
2320 return -1;
2321
2322 mouse_code = getdigits(&p);
2323 if (*p++ != ';')
2324 return -1;
2325
2326 // when mouse reporting is SGR, add 32 to mouse code
2327 if (key_name[0] == KS_SGR_MOUSE
2328 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2329 mouse_code += 32;
2330
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002331 mouse_col = getdigits(&p) - 1;
2332 if (*p++ != ';')
2333 return -1;
2334
2335 mouse_row = getdigits(&p) - 1;
2336
Bram Moolenaar13c04632020-07-12 13:47:42 +02002337 // The modifiers were the mouse coordinates, not the modifier keys
2338 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002339 *modifiers = 0;
2340 }
2341
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002342 if (key_name[0] == KS_SGR_MOUSE
2343 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2344 {
2345 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002346 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002347 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002348 // This is used below to set held_button.
2349 mouse_code |= MOUSE_RELEASE;
2350 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002351 }
2352 else
2353 {
2354 release_is_ambiguous = TRUE;
2355 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2356 is_release = TRUE;
2357 }
2358
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002359 if (key_name[0] == KS_MOUSE
2360# ifdef FEAT_MOUSE_GPM
2361 || key_name[0] == KS_GPM_MOUSE
2362# endif
2363# ifdef FEAT_MOUSE_URXVT
2364 || key_name[0] == KS_URXVT_MOUSE
2365# endif
2366 || key_name[0] == KS_SGR_MOUSE
2367 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2368 {
2369# if !defined(MSWIN)
2370 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002371 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002372 * Recognize the xterm mouse wheel, but not in the GUI, the
2373 * Linux console with GPM and the MS-DOS or Win32 console
2374 * (multi-clicks use >= 0x60).
2375 */
2376 if (mouse_code >= MOUSEWHEEL_LOW
2377# ifdef FEAT_GUI
2378 && !gui.in_use
2379# endif
2380# ifdef FEAT_MOUSE_GPM
2381 && key_name[0] != KS_GPM_MOUSE
2382# endif
2383 )
2384 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002385# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002386 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2387 // mouse-move event, using MOUSE_DRAG works
2388 mouse_code = MOUSE_DRAG;
2389 else
2390# endif
2391 // Keep the mouse_code before it's changed, so that we
2392 // remember that it was a mouse wheel click.
2393 wheel_code = mouse_code;
2394 }
2395# ifdef FEAT_MOUSE_XTERM
2396 else if (held_button == MOUSE_RELEASE
2397# ifdef FEAT_GUI
2398 && !gui.in_use
2399# endif
2400 && (mouse_code == 0x23 || mouse_code == 0x24
2401 || mouse_code == 0x40 || mouse_code == 0x41))
2402 {
2403 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2404 // And 0x40 and 0x41 are used by some xterm emulator.
2405 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
2406 + MOUSEWHEEL_LOW;
2407 }
2408# endif
2409
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002410# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002411 else if (use_xterm_mouse() > 1)
2412 {
2413 if (mouse_code & MOUSE_DRAG_XTERM)
2414 mouse_code |= MOUSE_DRAG;
2415 }
2416# endif
2417# ifdef FEAT_XCLIPBOARD
2418 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2419 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002420 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002421 stop_xterm_trace();
2422 else
2423 start_xterm_trace(mouse_code);
2424 }
2425# endif
2426# endif
2427 }
2428# endif // !UNIX || FEAT_MOUSE_XTERM
2429# ifdef FEAT_MOUSE_NET
2430 if (key_name[0] == KS_NETTERM_MOUSE)
2431 {
2432 int mc, mr;
2433
2434 // expect a rather limited sequence like: balancing {
2435 // \033}6,45\r
2436 // '6' is the row, 45 is the column
2437 p = tp + *slen;
2438 mr = getdigits(&p);
2439 if (*p++ != ',')
2440 return -1;
2441 mc = getdigits(&p);
2442 if (*p++ != '\r')
2443 return -1;
2444
2445 mouse_col = mc - 1;
2446 mouse_row = mr - 1;
2447 mouse_code = MOUSE_LEFT;
2448 *slen += (int)(p - (tp + *slen));
2449 }
2450# endif // FEAT_MOUSE_NET
2451# ifdef FEAT_MOUSE_JSB
2452 if (key_name[0] == KS_JSBTERM_MOUSE)
2453 {
2454 int mult, val, iter, button, status;
2455
2456 /*
2457 * JSBTERM Input Model
2458 * \033[0~zw uniq escape sequence
2459 * (L-x) Left button pressed - not pressed x not reporting
2460 * (M-x) Middle button pressed - not pressed x not reporting
2461 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002462 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2463 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002464 * ### X cursor position padded to 3 digits
2465 * ### Y cursor position padded to 3 digits
2466 * (s-x) SHIFT key pressed - not pressed x not reporting
2467 * (c-x) CTRL key pressed - not pressed x not reporting
2468 * \033\\ terminating sequence
2469 */
2470 p = tp + *slen;
2471 button = mouse_code = 0;
2472 switch (*p++)
2473 {
2474 case 'L': button = 1; break;
2475 case '-': break;
2476 case 'x': break; // ignore sequence
2477 default: return -1; // Unknown Result
2478 }
2479 switch (*p++)
2480 {
2481 case 'M': button |= 2; break;
2482 case '-': break;
2483 case 'x': break; // ignore sequence
2484 default: return -1; // Unknown Result
2485 }
2486 switch (*p++)
2487 {
2488 case 'R': button |= 4; break;
2489 case '-': break;
2490 case 'x': break; // ignore sequence
2491 default: return -1; // Unknown Result
2492 }
2493 status = *p++;
2494 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2495 mult /= 10, p++)
2496 if (*p >= '0' && *p <= '9')
2497 val += (*p - '0') * mult;
2498 else
2499 return -1;
2500 mouse_col = val;
2501 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2502 mult /= 10, p++)
2503 if (*p >= '0' && *p <= '9')
2504 val += (*p - '0') * mult;
2505 else
2506 return -1;
2507 mouse_row = val;
2508 switch (*p++)
2509 {
2510 case 's': button |= 8; break; // SHIFT key Pressed
2511 case '-': break; // Not Pressed
2512 case 'x': break; // Not Reporting
2513 default: return -1; // Unknown Result
2514 }
2515 switch (*p++)
2516 {
2517 case 'c': button |= 16; break; // CTRL key Pressed
2518 case '-': break; // Not Pressed
2519 case 'x': break; // Not Reporting
2520 default: return -1; // Unknown Result
2521 }
2522 if (*p++ != '\033')
2523 return -1;
2524 if (*p++ != '\\')
2525 return -1;
2526 switch (status)
2527 {
2528 case 'D': // Double Click
2529 case 'S': // Single Click
2530 if (button & 1) mouse_code |= MOUSE_LEFT;
2531 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2532 if (button & 4) mouse_code |= MOUSE_RIGHT;
2533 if (button & 8) mouse_code |= MOUSE_SHIFT;
2534 if (button & 16) mouse_code |= MOUSE_CTRL;
2535 break;
2536 case 'm': // Mouse move
2537 if (button & 1) mouse_code |= MOUSE_LEFT;
2538 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2539 if (button & 4) mouse_code |= MOUSE_RIGHT;
2540 if (button & 8) mouse_code |= MOUSE_SHIFT;
2541 if (button & 16) mouse_code |= MOUSE_CTRL;
2542 if ((button & 7) != 0)
2543 {
2544 held_button = mouse_code;
2545 mouse_code |= MOUSE_DRAG;
2546 }
2547 is_drag = TRUE;
2548 showmode();
2549 break;
2550 case 'd': // Button Down
2551 if (button & 1) mouse_code |= MOUSE_LEFT;
2552 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2553 if (button & 4) mouse_code |= MOUSE_RIGHT;
2554 if (button & 8) mouse_code |= MOUSE_SHIFT;
2555 if (button & 16) mouse_code |= MOUSE_CTRL;
2556 break;
2557 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002558 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002559 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002560 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002561 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002562 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002563 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002564 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002565 if (button & 8)
2566 mouse_code |= MOUSE_SHIFT;
2567 if (button & 16)
2568 mouse_code |= MOUSE_CTRL;
2569 break;
2570 default: return -1; // Unknown Result
2571 }
2572
2573 *slen += (p - (tp + *slen));
2574 }
2575# endif // FEAT_MOUSE_JSB
2576# ifdef FEAT_MOUSE_DEC
2577 if (key_name[0] == KS_DEC_MOUSE)
2578 {
2579 /*
2580 * The DEC Locator Input Model
2581 * Netterm delivers the code sequence:
2582 * \033[2;4;24;80&w (left button down)
2583 * \033[3;0;24;80&w (left button up)
2584 * \033[6;1;24;80&w (right button down)
2585 * \033[7;0;24;80&w (right button up)
2586 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2587 * Pe is the event code
2588 * Pb is the button code
2589 * Pr is the row coordinate
2590 * Pc is the column coordinate
2591 * Pp is the third coordinate (page number)
2592 * Pe, the event code indicates what event caused this report
2593 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002594 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002595 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002596 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002597 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002598 * 2 - left button down
2599 * 3 - left button up
2600 * 4 - middle button down
2601 * 5 - middle button up
2602 * 6 - right button down
2603 * 7 - right button up
2604 * 8 - fourth button down
2605 * 9 - fourth button up
2606 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002607 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2608 * down if any. The state of the four buttons on the locator
2609 * correspond to the low four bits of the decimal value, "1" means
2610 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002611 * 0 - no buttons down,
2612 * 1 - right,
2613 * 2 - middle,
2614 * 4 - left,
2615 * 8 - fourth
2616 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002617 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2618 * position is undefined (outside the terminal window for example).
2619 * Pc is the column coordinate of the locator position in the page,
2620 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2621 * position is undefined (outside the terminal window for example).
2622 * Pp is the page coordinate of the locator position encoded as an
2623 * ASCII decimal value. The page coordinate may be omitted if the
2624 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002625 */
2626 int Pe, Pb, Pr, Pc;
2627
2628 p = tp + *slen;
2629
2630 // get event status
2631 Pe = getdigits(&p);
2632 if (*p++ != ';')
2633 return -1;
2634
2635 // get button status
2636 Pb = getdigits(&p);
2637 if (*p++ != ';')
2638 return -1;
2639
2640 // get row status
2641 Pr = getdigits(&p);
2642 if (*p++ != ';')
2643 return -1;
2644
2645 // get column status
2646 Pc = getdigits(&p);
2647
2648 // the page parameter is optional
2649 if (*p == ';')
2650 {
2651 p++;
2652 (void)getdigits(&p);
2653 }
2654 if (*p++ != '&')
2655 return -1;
2656 if (*p++ != 'w')
2657 return -1;
2658
2659 mouse_code = 0;
2660 switch (Pe)
2661 {
2662 case 0: return -1; // position request while unavailable
2663 case 1: // a response to a locator position request includes
2664 // the status of all buttons
2665 Pb &= 7; // mask off and ignore fourth button
2666 if (Pb & 4)
2667 mouse_code = MOUSE_LEFT;
2668 if (Pb & 2)
2669 mouse_code = MOUSE_MIDDLE;
2670 if (Pb & 1)
2671 mouse_code = MOUSE_RIGHT;
2672 if (Pb)
2673 {
2674 held_button = mouse_code;
2675 mouse_code |= MOUSE_DRAG;
2676 WantQueryMouse = TRUE;
2677 }
2678 is_drag = TRUE;
2679 showmode();
2680 break;
2681 case 2: mouse_code = MOUSE_LEFT;
2682 WantQueryMouse = TRUE;
2683 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002684 case 3: mouse_code = MOUSE_LEFT;
2685 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002686 break;
2687 case 4: mouse_code = MOUSE_MIDDLE;
2688 WantQueryMouse = TRUE;
2689 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002690 case 5: mouse_code = MOUSE_MIDDLE;
2691 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002692 break;
2693 case 6: mouse_code = MOUSE_RIGHT;
2694 WantQueryMouse = TRUE;
2695 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002696 case 7: mouse_code = MOUSE_RIGHT;
2697 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002698 break;
2699 case 8: return -1; // fourth button down
2700 case 9: return -1; // fourth button up
2701 case 10: return -1; // mouse outside of filter rectangle
2702 default: return -1; // should never occur
2703 }
2704
2705 mouse_col = Pc - 1;
2706 mouse_row = Pr - 1;
2707
2708 *slen += (int)(p - (tp + *slen));
2709 }
2710# endif // FEAT_MOUSE_DEC
2711# ifdef FEAT_MOUSE_PTERM
2712 if (key_name[0] == KS_PTERM_MOUSE)
2713 {
2714 int button, num_clicks, action;
2715
2716 p = tp + *slen;
2717
2718 action = getdigits(&p);
2719 if (*p++ != ';')
2720 return -1;
2721
2722 mouse_row = getdigits(&p);
2723 if (*p++ != ';')
2724 return -1;
2725 mouse_col = getdigits(&p);
2726 if (*p++ != ';')
2727 return -1;
2728
2729 button = getdigits(&p);
2730 mouse_code = 0;
2731
2732 switch (button)
2733 {
2734 case 4: mouse_code = MOUSE_LEFT; break;
2735 case 1: mouse_code = MOUSE_RIGHT; break;
2736 case 2: mouse_code = MOUSE_MIDDLE; break;
2737 default: return -1;
2738 }
2739
2740 switch (action)
2741 {
2742 case 31: // Initial press
2743 if (*p++ != ';')
2744 return -1;
2745
2746 num_clicks = getdigits(&p); // Not used
2747 break;
2748
2749 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002750 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002751 break;
2752
2753 case 33: // Drag
2754 held_button = mouse_code;
2755 mouse_code |= MOUSE_DRAG;
2756 break;
2757
2758 default:
2759 return -1;
2760 }
2761
2762 if (*p++ != 't')
2763 return -1;
2764
2765 *slen += (p - (tp + *slen));
2766 }
2767# endif // FEAT_MOUSE_PTERM
2768
2769 // Interpret the mouse code
2770 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002771 if (is_release)
2772 current_button |= MOUSE_RELEASE;
2773
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002774 if (current_button == MOUSE_RELEASE
2775# ifdef FEAT_MOUSE_XTERM
2776 && wheel_code == 0
2777# endif
2778 )
2779 {
2780 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002781 * If we get a mouse drag or release event when there is no mouse
2782 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2783 * below.
2784 * (can happen when you hold down two buttons and then let them go, or
2785 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002786 */
2787 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2788 is_drag = TRUE;
2789 current_button = held_button;
2790 }
2791 else if (wheel_code == 0)
2792 {
2793# ifdef CHECK_DOUBLE_CLICK
2794# ifdef FEAT_MOUSE_GPM
2795 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002796 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2797 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002798 */
2799# ifdef FEAT_GUI
2800 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
2801# else
2802 if (key_name[0] != KS_GPM_MOUSE)
2803# endif
2804# else
2805# ifdef FEAT_GUI
2806 if (!gui.in_use)
2807# endif
2808# endif
2809 {
2810 /*
2811 * Compute the time elapsed since the previous mouse click.
2812 */
2813 gettimeofday(&mouse_time, NULL);
2814 if (orig_mouse_time.tv_sec == 0)
2815 {
2816 /*
2817 * Avoid computing the difference between mouse_time
2818 * and orig_mouse_time for the first click, as the
2819 * difference would be huge and would cause
2820 * multiplication overflow.
2821 */
2822 timediff = p_mouset;
2823 }
2824 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002825 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002826 orig_mouse_time = mouse_time;
2827 if (mouse_code == orig_mouse_code
2828 && timediff < p_mouset
2829 && orig_num_clicks != 4
2830 && orig_mouse_col == mouse_col
2831 && orig_mouse_row == mouse_row
2832 && (is_mouse_topline(curwin)
2833 // Double click in tab pages line also works
2834 // when window contents changes.
2835 || (mouse_row == 0 && firstwin->w_winrow > 0))
2836 )
2837 ++orig_num_clicks;
2838 else
2839 orig_num_clicks = 1;
2840 orig_mouse_col = mouse_col;
2841 orig_mouse_row = mouse_row;
2842 set_mouse_topline(curwin);
2843 }
2844# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
2845 else
2846 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2847# endif
2848# else
2849 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2850# endif
2851 is_click = TRUE;
2852 orig_mouse_code = mouse_code;
2853 }
2854 if (!is_drag)
2855 held_button = mouse_code & MOUSE_CLICK_MASK;
2856
2857 /*
2858 * Translate the actual mouse event into a pseudo mouse event.
2859 * First work out what modifiers are to be used.
2860 */
2861 if (orig_mouse_code & MOUSE_SHIFT)
2862 *modifiers |= MOD_MASK_SHIFT;
2863 if (orig_mouse_code & MOUSE_CTRL)
2864 *modifiers |= MOD_MASK_CTRL;
2865 if (orig_mouse_code & MOUSE_ALT)
2866 *modifiers |= MOD_MASK_ALT;
2867 if (orig_num_clicks == 2)
2868 *modifiers |= MOD_MASK_2CLICK;
2869 else if (orig_num_clicks == 3)
2870 *modifiers |= MOD_MASK_3CLICK;
2871 else if (orig_num_clicks == 4)
2872 *modifiers |= MOD_MASK_4CLICK;
2873
Bram Moolenaar13c04632020-07-12 13:47:42 +02002874 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2875 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002876 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002877 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002878 {
2879 if (wheel_code & MOUSE_CTRL)
2880 *modifiers |= MOD_MASK_CTRL;
2881 if (wheel_code & MOUSE_ALT)
2882 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002883
2884 if (wheel_code & 1 && wheel_code & 2)
2885 key_name[1] = (int)KE_MOUSELEFT;
2886 else if (wheel_code & 2)
2887 key_name[1] = (int)KE_MOUSERIGHT;
2888 else if (wheel_code & 1)
2889 key_name[1] = (int)KE_MOUSEUP;
2890 else
2891 key_name[1] = (int)KE_MOUSEDOWN;
2892
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002893 held_button = MOUSE_RELEASE;
2894 }
2895 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002896 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002897
Bram Moolenaar13c04632020-07-12 13:47:42 +02002898
2899 // Make sure the mouse position is valid. Some terminals may return weird
2900 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002901 if (mouse_col >= Columns)
2902 mouse_col = Columns - 1;
2903 if (mouse_row >= Rows)
2904 mouse_row = Rows - 1;
2905
2906 return 0;
2907}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002908
2909// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002910
2911/*
2912 * Compute the buffer line position from the screen position "rowp" / "colp" in
2913 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02002914 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
2915 * caches the plines_win() result from a previous call. Entry is zero if not
2916 * computed yet. There must be no text or setting changes since the entry is
2917 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002918 * Returns TRUE if the position is below the last line.
2919 */
2920 int
2921mouse_comp_pos(
2922 win_T *win,
2923 int *rowp,
2924 int *colp,
2925 linenr_T *lnump,
2926 int *plines_cache)
2927{
2928 int col = *colp;
2929 int row = *rowp;
2930 linenr_T lnum;
2931 int retval = FALSE;
2932 int off;
2933 int count;
2934
2935#ifdef FEAT_RIGHTLEFT
2936 if (win->w_p_rl)
2937 col = win->w_width - 1 - col;
2938#endif
2939
2940 lnum = win->w_topline;
2941
2942 while (row > 0)
2943 {
2944 int cache_idx = lnum - win->w_topline;
2945
Bram Moolenaar452143c2020-07-15 17:38:21 +02002946 // Only "Rows" lines are cached, with folding we'll run out of entries
2947 // and use the slow way.
2948 if (plines_cache != NULL && cache_idx < Rows
2949 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002950 count = plines_cache[cache_idx];
2951 else
2952 {
2953#ifdef FEAT_DIFF
2954 // Don't include filler lines in "count"
2955 if (win->w_p_diff
2956# ifdef FEAT_FOLDING
2957 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
2958# endif
2959 )
2960 {
2961 if (lnum == win->w_topline)
2962 row -= win->w_topfill;
2963 else
2964 row -= diff_check_fill(win, lnum);
2965 count = plines_win_nofill(win, lnum, TRUE);
2966 }
2967 else
2968#endif
2969 count = plines_win(win, lnum, TRUE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02002970 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002971 plines_cache[cache_idx] = count;
2972 }
2973 if (count > row)
2974 break; // Position is in this buffer line.
2975#ifdef FEAT_FOLDING
2976 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
2977#endif
2978 if (lnum == win->w_buffer->b_ml.ml_line_count)
2979 {
2980 retval = TRUE;
2981 break; // past end of file
2982 }
2983 row -= count;
2984 ++lnum;
2985 }
2986
2987 if (!retval)
2988 {
2989 // Compute the column without wrapping.
2990 off = win_col_off(win) - win_col_off2(win);
2991 if (col < off)
2992 col = off;
2993 col += row * (win->w_width - off);
2994 // add skip column (for long wrapping line)
2995 col += win->w_skipcol;
2996 }
2997
2998 if (!win->w_p_wrap)
2999 col += win->w_leftcol;
3000
3001 // skip line number and fold column in front of the line
3002 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003003 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003004 {
3005#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003006 // if mouse is clicked on the gutter, then inform the netbeans server
3007 if (*colp < win_col_off(win))
3008 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003009#endif
3010 col = 0;
3011 }
3012
3013 *colp = col;
3014 *rowp = row;
3015 *lnump = lnum;
3016 return retval;
3017}
3018
3019/*
3020 * Find the window at screen position "*rowp" and "*colp". The positions are
3021 * updated to become relative to the top-left of the window.
3022 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3023 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3024 * windows.
3025 * Returns NULL when something is wrong.
3026 */
3027 win_T *
3028mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3029{
3030 frame_T *fp;
3031 win_T *wp;
3032
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003033#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003034 win_T *pwp = NULL;
3035
3036 if (popup != IGNORE_POPUP)
3037 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003038 popup_reset_handled(POPUP_HANDLED_1);
3039 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003040 {
3041 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3042 && *colp >= wp->w_wincol
3043 && *colp < wp->w_wincol + popup_width(wp))
3044 pwp = wp;
3045 }
3046 if (pwp != NULL)
3047 {
3048 if (popup == FAIL_POPUP)
3049 return NULL;
3050 *rowp -= pwp->w_winrow;
3051 *colp -= pwp->w_wincol;
3052 return pwp;
3053 }
3054 }
3055#endif
3056
3057 fp = topframe;
3058 *rowp -= firstwin->w_winrow;
3059 for (;;)
3060 {
3061 if (fp->fr_layout == FR_LEAF)
3062 break;
3063 if (fp->fr_layout == FR_ROW)
3064 {
3065 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3066 {
3067 if (*colp < fp->fr_width)
3068 break;
3069 *colp -= fp->fr_width;
3070 }
3071 }
3072 else // fr_layout == FR_COL
3073 {
3074 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3075 {
3076 if (*rowp < fp->fr_height)
3077 break;
3078 *rowp -= fp->fr_height;
3079 }
3080 }
3081 }
3082 // When using a timer that closes a window the window might not actually
3083 // exist.
3084 FOR_ALL_WINDOWS(wp)
3085 if (wp == fp->fr_win)
3086 {
3087#ifdef FEAT_MENU
3088 *rowp -= wp->w_winbar_height;
3089#endif
3090 return wp;
3091 }
3092 return NULL;
3093}
3094
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003095#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003096 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003097/*
3098 * Convert a virtual (screen) column to a character column.
3099 * The first column is one.
3100 */
3101 int
3102vcol2col(win_T *wp, linenr_T lnum, int vcol)
3103{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003104 char_u *line;
3105 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003106
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003107 // try to advance to the specified column
3108 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3109 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3110 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003111 {
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003112 cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
3113 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003114 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003115 clear_chartabsize_arg(&cts);
3116
3117 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003118}
3119#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003120
3121#if defined(FEAT_EVAL) || defined(PROTO)
3122 void
3123f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3124{
3125 dict_T *d;
3126 win_T *wp;
3127 int row = mouse_row;
3128 int col = mouse_col;
3129 varnumber_T winid = 0;
3130 varnumber_T winrow = 0;
3131 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003132 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003133 varnumber_T column = 0;
3134
Bram Moolenaar93a10962022-06-16 11:42:09 +01003135 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003136 return;
3137 d = rettv->vval.v_dict;
3138
3139 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3140 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3141
3142 wp = mouse_find_win(&row, &col, FIND_POPUP);
3143 if (wp != NULL)
3144 {
3145 int top_off = 0;
3146 int left_off = 0;
3147 int height = wp->w_height + wp->w_status_height;
3148
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003149#ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003150 if (WIN_IS_POPUP(wp))
3151 {
3152 top_off = popup_top_extra(wp);
3153 left_off = popup_left_extra(wp);
3154 height = popup_height(wp);
3155 }
3156#endif
3157 if (row < height)
3158 {
3159 winid = wp->w_id;
3160 winrow = row + 1;
3161 wincol = col + 1;
3162 row -= top_off;
3163 col -= left_off;
3164 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3165 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003166 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
3167 col = vcol2col(wp, lnum, col);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003168 column = col + 1;
3169 }
3170 }
3171 }
3172 dict_add_number(d, "winid", winid);
3173 dict_add_number(d, "winrow", winrow);
3174 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003175 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003176 dict_add_number(d, "column", column);
3177}
3178#endif