blob: 12895f8094b980577095b81528ec3a1aa8810455 [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
Bram Moolenaar80525752022-08-24 19:27:45 +0100474 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200475 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100476 // Check for clicking in the tab page line.
477 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200478 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100479 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200480 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100481 if (in_tab_line)
482 {
483 c1 = TabPageIdxs[mouse_col];
484 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
485 ? c1 - 1 : c1);
486 }
487 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200488 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100489
490 // click in a tab selects that tab page
491 if (is_click
492# ifdef FEAT_CMDWIN
493 && cmdwin_type == 0
494# endif
495 && mouse_col < Columns)
496 {
497 in_tab_line = TRUE;
498 c1 = TabPageIdxs[mouse_col];
499 if (c1 >= 0)
500 {
501 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
502 {
503 // double click opens new page
504 end_visual_mode_keep_button();
505 tabpage_new();
506 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
507 }
508 else
509 {
510 // Go to specified tab page, or next one if not clicking
511 // on a label.
512 goto_tabpage(c1);
513
514 // It's like clicking on the status line of a window.
515 if (curwin != old_curwin)
516 end_visual_mode_keep_button();
517 }
518 }
519 else
520 {
521 tabpage_T *tp;
522
523 // Close the current or specified tab page.
524 if (c1 == -999)
525 tp = curtab;
526 else
527 tp = find_tabpage(-c1);
528 if (tp == curtab)
529 {
530 if (first_tabpage->tp_next != NULL)
531 tabpage_close(FALSE);
532 }
533 else if (tp != NULL)
534 tabpage_close_other(tp, FALSE);
535 }
536 }
537 return TRUE;
538 }
539 else if (is_drag && in_tab_line)
540 {
541 c1 = TabPageIdxs[mouse_col];
542 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200543 return FALSE;
544 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200545 }
546
547 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
548 // right button up -> pop-up menu
549 // shift-left button -> right button
550 // alt-left button -> alt-right button
551 if (mouse_model_popup())
552 {
553 if (which_button == MOUSE_RIGHT
554 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
555 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200556#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200557# ifdef FEAT_GUI
558 if (gui.in_use)
559 {
560# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200561 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200562 if (!is_click)
563 // Ignore right button release events, only shows the popup
564 // menu on the button down event.
565 return FALSE;
566# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100567# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200568 if (is_click || is_drag)
569 // Ignore right button down and drag mouse events. Windows
570 // only shows the popup menu on the button up event.
571 return FALSE;
572# endif
573 }
574# endif
575# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
576 else
577# endif
578# if defined(FEAT_TERM_POPUP_MENU)
579 if (!is_click)
580 // Ignore right button release events, only shows the popup
581 // menu on the button down event.
582 return FALSE;
583#endif
584
585 jump_flags = 0;
586 if (STRCMP(p_mousem, "popup_setpos") == 0)
587 {
588 // First set the cursor position before showing the popup
589 // menu.
590 if (VIsual_active)
591 {
592 pos_T m_pos;
593
594 // set MOUSE_MAY_STOP_VIS if we are outside the
595 // selection or the current window (might have false
596 // negative here)
597 if (mouse_row < curwin->w_winrow
598 || mouse_row
599 > (curwin->w_winrow + curwin->w_height))
600 jump_flags = MOUSE_MAY_STOP_VIS;
601 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
602 jump_flags = MOUSE_MAY_STOP_VIS;
603 else
604 {
605 if ((LT_POS(curwin->w_cursor, VIsual)
606 && (LT_POS(m_pos, curwin->w_cursor)
607 || LT_POS(VIsual, m_pos)))
608 || (LT_POS(VIsual, curwin->w_cursor)
609 && (LT_POS(m_pos, VIsual)
610 || LT_POS(curwin->w_cursor, m_pos))))
611 {
612 jump_flags = MOUSE_MAY_STOP_VIS;
613 }
614 else if (VIsual_mode == Ctrl_V)
615 {
616 getvcols(curwin, &curwin->w_cursor, &VIsual,
617 &leftcol, &rightcol);
618 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
619 if (m_pos.col < leftcol || m_pos.col > rightcol)
620 jump_flags = MOUSE_MAY_STOP_VIS;
621 }
622 }
623 }
624 else
625 jump_flags = MOUSE_MAY_STOP_VIS;
626 }
627 if (jump_flags)
628 {
629 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100630 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200631 setcursor();
632 out_flush(); // Update before showing popup menu
633 }
634# ifdef FEAT_MENU
635 show_popupmenu();
636 got_click = FALSE; // ignore release events
637# endif
638 return (jump_flags & CURSOR_MOVED) != 0;
639#else
640 return FALSE;
641#endif
642 }
643 if (which_button == MOUSE_LEFT
644 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
645 {
646 which_button = MOUSE_RIGHT;
647 mod_mask &= ~MOD_MASK_SHIFT;
648 }
649 }
650
Bram Moolenaar24959102022-05-07 20:01:16 +0100651 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200652 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
653 {
654 if (which_button == MOUSE_LEFT)
655 {
656 if (is_click)
657 {
658 // stop Visual mode for a left click in a window, but not when
659 // on a status line
660 if (VIsual_active)
661 jump_flags |= MOUSE_MAY_STOP_VIS;
662 }
663 else if (mouse_has(MOUSE_VISUAL))
664 jump_flags |= MOUSE_MAY_VIS;
665 }
666 else if (which_button == MOUSE_RIGHT)
667 {
668 if (is_click && VIsual_active)
669 {
670 // Remember the start and end of visual before moving the
671 // cursor.
672 if (LT_POS(curwin->w_cursor, VIsual))
673 {
674 start_visual = curwin->w_cursor;
675 end_visual = VIsual;
676 }
677 else
678 {
679 start_visual = VIsual;
680 end_visual = curwin->w_cursor;
681 }
682 }
683 jump_flags |= MOUSE_FOCUS;
684 if (mouse_has(MOUSE_VISUAL))
685 jump_flags |= MOUSE_MAY_VIS;
686 }
687 }
688
689 // If an operator is pending, ignore all drags and releases until the
690 // next mouse click.
691 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
692 {
693 got_click = FALSE;
694 oap->motion_type = MCHAR;
695 }
696
697 // When releasing the button let jump_to_mouse() know.
698 if (!is_click && !is_drag)
699 jump_flags |= MOUSE_RELEASED;
700
701 // JUMP!
702 jump_flags = jump_to_mouse(jump_flags,
703 oap == NULL ? NULL : &(oap->inclusive), which_button);
704
705#ifdef FEAT_MENU
706 // A click in the window toolbar has no side effects.
707 if (jump_flags & MOUSE_WINBAR)
708 return FALSE;
709#endif
710 moved = (jump_flags & CURSOR_MOVED);
711 in_status_line = (jump_flags & IN_STATUS_LINE);
712 in_sep_line = (jump_flags & IN_SEP_LINE);
713
714#ifdef FEAT_NETBEANS_INTG
715 if (isNetbeansBuffer(curbuf)
716 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
717 {
718 int key = KEY2TERMCAP1(c);
719
720 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
721 || key == (int)KE_RIGHTRELEASE)
722 netbeans_button_release(which_button);
723 }
724#endif
725
726 // When jumping to another window, clear a pending operator. That's a bit
727 // friendlier than beeping and not jumping to that window.
728 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
729 clearop(oap);
730
731#ifdef FEAT_FOLDING
732 if (mod_mask == 0
733 && !is_drag
734 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
735 && which_button == MOUSE_LEFT)
736 {
737 // open or close a fold at this line
738 if (jump_flags & MOUSE_FOLD_OPEN)
739 openFold(curwin->w_cursor.lnum, 1L);
740 else
741 closeFold(curwin->w_cursor.lnum, 1L);
742 // don't move the cursor if still in the same window
743 if (curwin == old_curwin)
744 curwin->w_cursor = save_cursor;
745 }
746#endif
747
748#if defined(FEAT_CLIPBOARD) && defined(FEAT_CMDWIN)
749 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
750 {
751 clip_modeless(which_button, is_click, is_drag);
752 return FALSE;
753 }
754#endif
755
756 // Set global flag that we are extending the Visual area with mouse
757 // dragging; temporarily minimize 'scrolloff'.
758 if (VIsual_active && is_drag && get_scrolloff_value())
759 {
760 // In the very first line, allow scrolling one line
761 if (mouse_row == 0)
762 mouse_dragging = 2;
763 else
764 mouse_dragging = 1;
765 }
766
767 // When dragging the mouse above the window, scroll down.
768 if (is_drag && mouse_row < 0 && !in_status_line)
769 {
770 scroll_redraw(FALSE, 1L);
771 mouse_row = 0;
772 }
773
774 if (start_visual.lnum) // right click in visual mode
775 {
776 // When ALT is pressed make Visual mode blockwise.
777 if (mod_mask & MOD_MASK_ALT)
778 VIsual_mode = Ctrl_V;
779
780 // In Visual-block mode, divide the area in four, pick up the corner
781 // that is in the quarter that the cursor is in.
782 if (VIsual_mode == Ctrl_V)
783 {
784 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
785 if (curwin->w_curswant > (leftcol + rightcol) / 2)
786 end_visual.col = leftcol;
787 else
788 end_visual.col = rightcol;
789 if (curwin->w_cursor.lnum >=
790 (start_visual.lnum + end_visual.lnum) / 2)
791 end_visual.lnum = start_visual.lnum;
792
793 // move VIsual to the right column
794 start_visual = curwin->w_cursor; // save the cursor pos
795 curwin->w_cursor = end_visual;
796 coladvance(end_visual.col);
797 VIsual = curwin->w_cursor;
798 curwin->w_cursor = start_visual; // restore the cursor
799 }
800 else
801 {
802 // If the click is before the start of visual, change the start.
803 // If the click is after the end of visual, change the end. If
804 // the click is inside the visual, change the closest side.
805 if (LT_POS(curwin->w_cursor, start_visual))
806 VIsual = end_visual;
807 else if (LT_POS(end_visual, curwin->w_cursor))
808 VIsual = start_visual;
809 else
810 {
811 // In the same line, compare column number
812 if (end_visual.lnum == start_visual.lnum)
813 {
814 if (curwin->w_cursor.col - start_visual.col >
815 end_visual.col - curwin->w_cursor.col)
816 VIsual = start_visual;
817 else
818 VIsual = end_visual;
819 }
820
821 // In different lines, compare line number
822 else
823 {
824 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
825 (end_visual.lnum - curwin->w_cursor.lnum);
826
827 if (diff > 0) // closest to end
828 VIsual = start_visual;
829 else if (diff < 0) // closest to start
830 VIsual = end_visual;
831 else // in the middle line
832 {
833 if (curwin->w_cursor.col <
834 (start_visual.col + end_visual.col) / 2)
835 VIsual = end_visual;
836 else
837 VIsual = start_visual;
838 }
839 }
840 }
841 }
842 }
843 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100844 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200845 stuffcharReadbuff(Ctrl_O);
846
847 // Middle mouse click: Put text before cursor.
848 if (which_button == MOUSE_MIDDLE)
849 {
850#ifdef FEAT_CLIPBOARD
851 if (clip_star.available && regname == 0)
852 regname = '*';
853#endif
854 if (yank_register_mline(regname))
855 {
856 if (mouse_past_bottom)
857 dir = FORWARD;
858 }
859 else if (mouse_past_eol)
860 dir = FORWARD;
861
862 if (fixindent)
863 {
864 c1 = (dir == BACKWARD) ? '[' : ']';
865 c2 = 'p';
866 }
867 else
868 {
869 c1 = (dir == FORWARD) ? 'p' : 'P';
870 c2 = NUL;
871 }
872 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
873
874 // Remember where the paste started, so in edit() Insstart can be set
875 // to this position
876 if (restart_edit != 0)
877 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200878 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200879 }
880
881#if defined(FEAT_QUICKFIX)
882 // Ctrl-Mouse click or double click in a quickfix window jumps to the
883 // error under the mouse pointer.
884 else if (((mod_mask & MOD_MASK_CTRL)
885 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
886 && bt_quickfix(curbuf))
887 {
888 if (curwin->w_llist_ref == NULL) // quickfix window
889 do_cmdline_cmd((char_u *)".cc");
890 else // location list window
891 do_cmdline_cmd((char_u *)".ll");
892 got_click = FALSE; // ignore drag&release now
893 }
894#endif
895
896 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
897 // under the mouse pointer.
898 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
899 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
900 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100901 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200902 stuffcharReadbuff(Ctrl_O);
903 stuffcharReadbuff(Ctrl_RSB);
904 got_click = FALSE; // ignore drag&release now
905 }
906
907 // Shift-Mouse click searches for the next occurrence of the word under
908 // the mouse pointer
909 else if ((mod_mask & MOD_MASK_SHIFT))
910 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100911 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200912 stuffcharReadbuff(Ctrl_O);
913 if (which_button == MOUSE_LEFT)
914 stuffcharReadbuff('*');
915 else // MOUSE_RIGHT
916 stuffcharReadbuff('#');
917 }
918
919 // Handle double clicks, unless on status line
920 else if (in_status_line)
921 {
922#ifdef FEAT_MOUSESHAPE
923 if ((is_drag || is_click) && !drag_status_line)
924 {
925 drag_status_line = TRUE;
926 update_mouseshape(-1);
927 }
928#endif
929 }
930 else if (in_sep_line)
931 {
932#ifdef FEAT_MOUSESHAPE
933 if ((is_drag || is_click) && !drag_sep_line)
934 {
935 drag_sep_line = TRUE;
936 update_mouseshape(-1);
937 }
938#endif
939 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100940 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
941 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200942 && mouse_has(MOUSE_VISUAL))
943 {
944 if (is_click || !VIsual_active)
945 {
946 if (VIsual_active)
947 orig_cursor = VIsual;
948 else
949 {
950 check_visual_highlight();
951 VIsual = curwin->w_cursor;
952 orig_cursor = VIsual;
953 VIsual_active = TRUE;
954 VIsual_reselect = TRUE;
955 // start Select mode if 'selectmode' contains "mouse"
956 may_start_select('o');
957 setmouse();
958 }
959 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
960 {
961 // Double click with ALT pressed makes it blockwise.
962 if (mod_mask & MOD_MASK_ALT)
963 VIsual_mode = Ctrl_V;
964 else
965 VIsual_mode = 'v';
966 }
967 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
968 VIsual_mode = 'V';
969 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
970 VIsual_mode = Ctrl_V;
971#ifdef FEAT_CLIPBOARD
972 // Make sure the clipboard gets updated. Needed because start and
973 // end may still be the same, and the selection needs to be owned
974 clip_star.vmode = NUL;
975#endif
976 }
977 // A double click selects a word or a block.
978 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
979 {
980 pos_T *pos = NULL;
981 int gc;
982
983 if (is_click)
984 {
985 // If the character under the cursor (skipping white space) is
986 // not a word character, try finding a match and select a (),
987 // {}, [], #if/#endif, etc. block.
988 end_visual = curwin->w_cursor;
989 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
990 inc(&end_visual);
991 if (oap != NULL)
992 oap->motion_type = MCHAR;
993 if (oap != NULL
994 && VIsual_mode == 'v'
995 && !vim_iswordc(gchar_pos(&end_visual))
996 && EQUAL_POS(curwin->w_cursor, VIsual)
997 && (pos = findmatch(oap, NUL)) != NULL)
998 {
999 curwin->w_cursor = *pos;
1000 if (oap->motion_type == MLINE)
1001 VIsual_mode = 'V';
1002 else if (*p_sel == 'e')
1003 {
1004 if (LT_POS(curwin->w_cursor, VIsual))
1005 ++VIsual.col;
1006 else
1007 ++curwin->w_cursor.col;
1008 }
1009 }
1010 }
1011
1012 if (pos == NULL && (is_click || is_drag))
1013 {
1014 // When not found a match or when dragging: extend to include
1015 // a word.
1016 if (LT_POS(curwin->w_cursor, orig_cursor))
1017 {
1018 find_start_of_word(&curwin->w_cursor);
1019 find_end_of_word(&VIsual);
1020 }
1021 else
1022 {
1023 find_start_of_word(&VIsual);
1024 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1025 curwin->w_cursor.col +=
1026 (*mb_ptr2len)(ml_get_cursor());
1027 find_end_of_word(&curwin->w_cursor);
1028 }
1029 }
1030 curwin->w_set_curswant = TRUE;
1031 }
1032 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001033 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001034 }
1035 else if (VIsual_active && !old_active)
1036 {
1037 if (mod_mask & MOD_MASK_ALT)
1038 VIsual_mode = Ctrl_V;
1039 else
1040 VIsual_mode = 'v';
1041 }
1042
1043 // If Visual mode changed show it later.
1044 if ((!VIsual_active && old_active && mode_displayed)
1045 || (VIsual_active && p_smd && msg_silent == 0
1046 && (!old_active || VIsual_mode != old_mode)))
1047 redraw_cmdline = TRUE;
1048
1049 return moved;
1050}
1051
1052 void
1053ins_mouse(int c)
1054{
1055 pos_T tpos;
1056 win_T *old_curwin = curwin;
1057
1058# ifdef FEAT_GUI
1059 // When GUI is active, also move/paste when 'mouse' is empty
1060 if (!gui.in_use)
1061# endif
1062 if (!mouse_has(MOUSE_INSERT))
1063 return;
1064
1065 undisplay_dollar();
1066 tpos = curwin->w_cursor;
1067 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1068 {
1069 win_T *new_curwin = curwin;
1070
1071 if (curwin != old_curwin && win_valid(old_curwin))
1072 {
1073 // Mouse took us to another window. We need to go back to the
1074 // previous one to stop insert there properly.
1075 curwin = old_curwin;
1076 curbuf = curwin->w_buffer;
1077#ifdef FEAT_JOB_CHANNEL
1078 if (bt_prompt(curbuf))
1079 // Restart Insert mode when re-entering the prompt buffer.
1080 curbuf->b_prompt_insert = 'A';
1081#endif
1082 }
1083 start_arrow(curwin == old_curwin ? &tpos : NULL);
1084 if (curwin != new_curwin && win_valid(new_curwin))
1085 {
1086 curwin = new_curwin;
1087 curbuf = curwin->w_buffer;
1088 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001089 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001090 }
1091
1092 // redraw status lines (in case another window became active)
1093 redraw_statuslines();
1094}
1095
1096 void
1097ins_mousescroll(int dir)
1098{
1099 pos_T tpos;
1100 win_T *old_curwin = curwin, *wp;
1101 int did_scroll = FALSE;
1102
1103 tpos = curwin->w_cursor;
1104
1105 if (mouse_row >= 0 && mouse_col >= 0)
1106 {
1107 int row, col;
1108
1109 row = mouse_row;
1110 col = mouse_col;
1111
1112 // find the window at the pointer coordinates
1113 wp = mouse_find_win(&row, &col, FIND_POPUP);
1114 if (wp == NULL)
1115 return;
1116 curwin = wp;
1117 curbuf = curwin->w_buffer;
1118 }
1119 if (curwin == old_curwin)
1120 undisplay_dollar();
1121
1122 // Don't scroll the window in which completion is being done.
1123 if (!pum_visible() || curwin != old_curwin)
1124 {
LemonBoyc27747e2022-05-07 12:25:40 +01001125 long step;
1126
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001127 if (dir == MSCR_DOWN || dir == MSCR_UP)
1128 {
LemonBoyc27747e2022-05-07 12:25:40 +01001129 if (mouse_vert_step < 0
1130 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1131 step = (long)(curwin->w_botline - curwin->w_topline);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001132 else
LemonBoyc27747e2022-05-07 12:25:40 +01001133 step = mouse_vert_step;
1134 scroll_redraw(dir, step);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001135# ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001136 if (WIN_IS_POPUP(curwin))
1137 popup_set_firstline(curwin);
1138# endif
1139 }
1140#ifdef FEAT_GUI
1141 else
1142 {
LemonBoyc27747e2022-05-07 12:25:40 +01001143 int val;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001144
LemonBoyc27747e2022-05-07 12:25:40 +01001145 if (mouse_hor_step < 0
1146 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001147 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01001148 else
1149 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001150 val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
1151 if (val < 0)
1152 val = 0;
1153 gui_do_horiz_scroll(val, TRUE);
1154 }
1155#endif
1156 did_scroll = TRUE;
LemonBoy66e13ae2022-04-21 22:52:11 +01001157 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001158 }
1159
1160 curwin->w_redr_status = TRUE;
1161
1162 curwin = old_curwin;
1163 curbuf = curwin->w_buffer;
1164
1165 // The popup menu may overlay the window, need to redraw it.
1166 // TODO: Would be more efficient to only redraw the windows that are
1167 // overlapped by the popup menu.
1168 if (pum_visible() && did_scroll)
1169 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001170 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001171 ins_compl_show_pum();
1172 }
1173
1174 if (!EQUAL_POS(curwin->w_cursor, tpos))
1175 {
1176 start_arrow(&tpos);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001177 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001178 }
1179}
1180
1181/*
1182 * Return TRUE if "c" is a mouse key.
1183 */
1184 int
1185is_mouse_key(int c)
1186{
1187 return c == K_LEFTMOUSE
1188 || c == K_LEFTMOUSE_NM
1189 || c == K_LEFTDRAG
1190 || c == K_LEFTRELEASE
1191 || c == K_LEFTRELEASE_NM
1192 || c == K_MOUSEMOVE
1193 || c == K_MIDDLEMOUSE
1194 || c == K_MIDDLEDRAG
1195 || c == K_MIDDLERELEASE
1196 || c == K_RIGHTMOUSE
1197 || c == K_RIGHTDRAG
1198 || c == K_RIGHTRELEASE
1199 || c == K_MOUSEDOWN
1200 || c == K_MOUSEUP
1201 || c == K_MOUSELEFT
1202 || c == K_MOUSERIGHT
1203 || c == K_X1MOUSE
1204 || c == K_X1DRAG
1205 || c == K_X1RELEASE
1206 || c == K_X2MOUSE
1207 || c == K_X2DRAG
1208 || c == K_X2RELEASE;
1209}
1210
1211static struct mousetable
1212{
1213 int pseudo_code; // Code for pseudo mouse event
1214 int button; // Which mouse button is it?
1215 int is_click; // Is it a mouse button click event?
1216 int is_drag; // Is it a mouse drag event?
1217} mouse_table[] =
1218{
1219 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1220#ifdef FEAT_GUI
1221 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1222#endif
1223 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1224 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1225#ifdef FEAT_GUI
1226 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1227#endif
1228 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1229 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1230 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1231 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1232 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1233 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1234 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1235 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1236 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1237 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1238 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1239 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1240 // DRAG without CLICK
1241 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1242 // RELEASE without CLICK
1243 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1244 {0, 0, 0, 0},
1245};
1246
1247/*
1248 * Look up the given mouse code to return the relevant information in the other
1249 * arguments. Return which button is down or was released.
1250 */
1251 int
1252get_mouse_button(int code, int *is_click, int *is_drag)
1253{
1254 int i;
1255
1256 for (i = 0; mouse_table[i].pseudo_code; i++)
1257 if (code == mouse_table[i].pseudo_code)
1258 {
1259 *is_click = mouse_table[i].is_click;
1260 *is_drag = mouse_table[i].is_drag;
1261 return mouse_table[i].button;
1262 }
1263 return 0; // Shouldn't get here
1264}
1265
1266/*
1267 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1268 * the given information about which mouse button is down, and whether the
1269 * mouse was clicked, dragged or released.
1270 */
1271 int
1272get_pseudo_mouse_code(
1273 int button, // eg MOUSE_LEFT
1274 int is_click,
1275 int is_drag)
1276{
1277 int i;
1278
1279 for (i = 0; mouse_table[i].pseudo_code; i++)
1280 if (button == mouse_table[i].button
1281 && is_click == mouse_table[i].is_click
1282 && is_drag == mouse_table[i].is_drag)
1283 {
1284#ifdef FEAT_GUI
1285 // Trick: a non mappable left click and release has mouse_col -1
1286 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1287 // gui_mouse_moved()
1288 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1289 {
1290 if (mouse_col < 0)
1291 mouse_col = 0;
1292 else
1293 mouse_col -= MOUSE_COLOFF;
1294 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1295 return (int)KE_LEFTMOUSE_NM;
1296 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1297 return (int)KE_LEFTRELEASE_NM;
1298 }
1299#endif
1300 return mouse_table[i].pseudo_code;
1301 }
1302 return (int)KE_IGNORE; // not recognized, ignore it
1303}
1304
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001305# define HMT_NORMAL 1
1306# define HMT_NETTERM 2
1307# define HMT_DEC 4
1308# define HMT_JSBTERM 8
1309# define HMT_PTERM 16
1310# define HMT_URXVT 32
1311# define HMT_GPM 64
1312# define HMT_SGR 128
1313# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001314static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001315
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001316 void
1317set_mouse_termcode(
1318 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1319 char_u *s)
1320{
1321 char_u name[2];
1322
1323 name[0] = n;
1324 name[1] = KE_FILLER;
1325 add_termcode(name, s, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001326# ifdef FEAT_MOUSE_JSB
1327 if (n == KS_JSBTERM_MOUSE)
1328 has_mouse_termcode |= HMT_JSBTERM;
1329 else
1330# endif
1331# ifdef FEAT_MOUSE_NET
1332 if (n == KS_NETTERM_MOUSE)
1333 has_mouse_termcode |= HMT_NETTERM;
1334 else
1335# endif
1336# ifdef FEAT_MOUSE_DEC
1337 if (n == KS_DEC_MOUSE)
1338 has_mouse_termcode |= HMT_DEC;
1339 else
1340# endif
1341# ifdef FEAT_MOUSE_PTERM
1342 if (n == KS_PTERM_MOUSE)
1343 has_mouse_termcode |= HMT_PTERM;
1344 else
1345# endif
1346# ifdef FEAT_MOUSE_URXVT
1347 if (n == KS_URXVT_MOUSE)
1348 has_mouse_termcode |= HMT_URXVT;
1349 else
1350# endif
1351# ifdef FEAT_MOUSE_GPM
1352 if (n == KS_GPM_MOUSE)
1353 has_mouse_termcode |= HMT_GPM;
1354 else
1355# endif
1356 if (n == KS_SGR_MOUSE)
1357 has_mouse_termcode |= HMT_SGR;
1358 else if (n == KS_SGR_MOUSE_RELEASE)
1359 has_mouse_termcode |= HMT_SGR_REL;
1360 else
1361 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001362}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001363
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001364# if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001365 void
1366del_mouse_termcode(
1367 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1368{
1369 char_u name[2];
1370
1371 name[0] = n;
1372 name[1] = KE_FILLER;
1373 del_termcode(name);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001374# ifdef FEAT_MOUSE_JSB
1375 if (n == KS_JSBTERM_MOUSE)
1376 has_mouse_termcode &= ~HMT_JSBTERM;
1377 else
1378# endif
1379# ifdef FEAT_MOUSE_NET
1380 if (n == KS_NETTERM_MOUSE)
1381 has_mouse_termcode &= ~HMT_NETTERM;
1382 else
1383# endif
1384# ifdef FEAT_MOUSE_DEC
1385 if (n == KS_DEC_MOUSE)
1386 has_mouse_termcode &= ~HMT_DEC;
1387 else
1388# endif
1389# ifdef FEAT_MOUSE_PTERM
1390 if (n == KS_PTERM_MOUSE)
1391 has_mouse_termcode &= ~HMT_PTERM;
1392 else
1393# endif
1394# ifdef FEAT_MOUSE_URXVT
1395 if (n == KS_URXVT_MOUSE)
1396 has_mouse_termcode &= ~HMT_URXVT;
1397 else
1398# endif
1399# ifdef FEAT_MOUSE_GPM
1400 if (n == KS_GPM_MOUSE)
1401 has_mouse_termcode &= ~HMT_GPM;
1402 else
1403# endif
1404 if (n == KS_SGR_MOUSE)
1405 has_mouse_termcode &= ~HMT_SGR;
1406 else if (n == KS_SGR_MOUSE_RELEASE)
1407 has_mouse_termcode &= ~HMT_SGR_REL;
1408 else
1409 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001410}
1411# endif
1412
1413/*
1414 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1415 */
1416 void
1417setmouse(void)
1418{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001419 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001420
1421# ifdef FEAT_MOUSESHAPE
1422 update_mouseshape(-1);
1423# endif
1424
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001425 // Should be outside proc, but may break MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001426# ifdef FEAT_GUI
1427 // In the GUI the mouse is always enabled.
1428 if (gui.in_use)
1429 return;
1430# endif
1431 // be quick when mouse is off
1432 if (*p_mouse == NUL || has_mouse_termcode == 0)
1433 return;
1434
1435 // don't switch mouse on when not in raw mode (Ex mode)
1436 if (cur_tmode != TMODE_RAW)
1437 {
1438 mch_setmouse(FALSE);
1439 return;
1440 }
1441
1442 if (VIsual_active)
1443 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001444 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1445 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001446 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001447 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001448 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001449 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001450 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001451 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001452 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1453 else
1454 checkfor = MOUSE_NORMAL; // assume normal mode
1455
1456 if (mouse_has(checkfor))
1457 mch_setmouse(TRUE);
1458 else
1459 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001460}
1461
1462/*
1463 * Return TRUE if
1464 * - "c" is in 'mouse', or
1465 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1466 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1467 * normal editing mode (not at hit-return message).
1468 */
1469 int
1470mouse_has(int c)
1471{
1472 char_u *p;
1473
1474 for (p = p_mouse; *p; ++p)
1475 switch (*p)
1476 {
1477 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1478 return TRUE;
1479 break;
1480 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1481 return TRUE;
1482 break;
1483 default: if (c == *p) return TRUE; break;
1484 }
1485 return FALSE;
1486}
1487
1488/*
1489 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1490 */
1491 int
1492mouse_model_popup(void)
1493{
1494 return (p_mousem[0] == 'p');
1495}
1496
1497/*
1498 * Move the cursor to the specified row and column on the screen.
1499 * Change current window if necessary. Returns an integer with the
1500 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1501 *
1502 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1503 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1504 *
1505 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1506 * if the mouse is outside the window then the text will scroll, or if the
1507 * mouse was previously on a status line, then the status line may be dragged.
1508 *
1509 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1510 * cursor is moved unless the cursor was on a status line.
1511 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1512 * IN_SEP_LINE depending on where the cursor was clicked.
1513 *
1514 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1515 * the mouse is on the status line of the same window.
1516 *
1517 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1518 * the last call.
1519 *
1520 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1521 * remembered.
1522 */
1523 int
1524jump_to_mouse(
1525 int flags,
1526 int *inclusive, // used for inclusive operator, can be NULL
1527 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1528{
1529 static int on_status_line = 0; // #lines below bottom of window
1530 static int on_sep_line = 0; // on separator right of window
1531#ifdef FEAT_MENU
1532 static int in_winbar = FALSE;
1533#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001534#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001535 static int in_popup_win = FALSE;
1536 static win_T *click_in_popup_win = NULL;
1537#endif
1538 static int prev_row = -1;
1539 static int prev_col = -1;
1540 static win_T *dragwin = NULL; // window being dragged
1541 static int did_drag = FALSE; // drag was noticed
1542
1543 win_T *wp, *old_curwin;
1544 pos_T old_cursor;
1545 int count;
1546 int first;
1547 int row = mouse_row;
1548 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001549 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001550#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001551 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001552#endif
1553
1554 mouse_past_bottom = FALSE;
1555 mouse_past_eol = FALSE;
1556
1557 if (flags & MOUSE_RELEASED)
1558 {
1559 // On button release we may change window focus if positioned on a
1560 // status line and no dragging happened.
1561 if (dragwin != NULL && !did_drag)
1562 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1563 dragwin = NULL;
1564 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001565#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001566 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1567 popup_close_for_mouse_click(click_in_popup_win);
1568
1569 popup_dragwin = NULL;
1570 click_in_popup_win = NULL;
1571#endif
1572 }
1573
1574 if ((flags & MOUSE_DID_MOVE)
1575 && prev_row == mouse_row
1576 && prev_col == mouse_col)
1577 {
1578retnomove:
1579 // before moving the cursor for a left click which is NOT in a status
1580 // line, stop Visual mode
1581 if (on_status_line)
1582 return IN_STATUS_LINE;
1583 if (on_sep_line)
1584 return IN_SEP_LINE;
1585#ifdef FEAT_MENU
1586 if (in_winbar)
1587 {
1588 // A quick second click may arrive as a double-click, but we use it
1589 // as a second click in the WinBar.
1590 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1591 {
1592 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1593 if (wp == NULL)
1594 return IN_UNKNOWN;
1595 winbar_click(wp, col);
1596 }
1597 return IN_OTHER_WIN | MOUSE_WINBAR;
1598 }
1599#endif
1600 if (flags & MOUSE_MAY_STOP_VIS)
1601 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001602 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001603 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001604 }
1605#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1606 // Continue a modeless selection in another window.
1607 if (cmdwin_type != 0 && row < curwin->w_winrow)
1608 return IN_OTHER_WIN;
1609#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001610#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001611 // Continue a modeless selection in a popup window or dragging it.
1612 if (in_popup_win)
1613 {
1614 click_in_popup_win = NULL; // don't close it on release
1615 if (popup_dragwin != NULL)
1616 {
1617 // dragging a popup window
1618 popup_drag(popup_dragwin);
1619 return IN_UNKNOWN;
1620 }
1621 return IN_OTHER_WIN;
1622 }
1623#endif
1624 return IN_BUFFER;
1625 }
1626
1627 prev_row = mouse_row;
1628 prev_col = mouse_col;
1629
1630 if (flags & MOUSE_SETPOS)
1631 goto retnomove; // ugly goto...
1632
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001633 old_curwin = curwin;
1634 old_cursor = curwin->w_cursor;
1635
1636 if (!(flags & MOUSE_FOCUS))
1637 {
1638 if (row < 0 || col < 0) // check if it makes sense
1639 return IN_UNKNOWN;
1640
1641 // find the window where the row is in and adjust "row" and "col" to be
1642 // relative to top-left of the window
1643 wp = mouse_find_win(&row, &col, FIND_POPUP);
1644 if (wp == NULL)
1645 return IN_UNKNOWN;
1646 dragwin = NULL;
1647
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001648#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001649 // Click in a popup window may start dragging or modeless selection,
1650 // but not much else.
1651 if (WIN_IS_POPUP(wp))
1652 {
1653 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001654 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001655 in_popup_win = TRUE;
1656 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1657 {
1658 return IN_UNKNOWN;
1659 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001660 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001661 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001662 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001663 {
1664 popup_dragwin = wp;
1665 popup_start_drag(wp, row, col);
1666 return IN_UNKNOWN;
1667 }
1668 // Only close on release, otherwise it's not possible to drag or do
1669 // modeless selection.
1670 else if (wp->w_popup_close == POPCLOSE_CLICK
1671 && which_button == MOUSE_LEFT)
1672 {
1673 click_in_popup_win = wp;
1674 }
1675 else if (which_button == MOUSE_LEFT)
1676 // If the click is in the scrollbar, may scroll up/down.
1677 popup_handle_scrollbar_click(wp, row, col);
1678# ifdef FEAT_CLIPBOARD
1679 return IN_OTHER_WIN;
1680# else
1681 return IN_UNKNOWN;
1682# endif
1683 }
1684 in_popup_win = FALSE;
1685 popup_dragwin = NULL;
1686#endif
1687#ifdef FEAT_MENU
1688 if (row == -1)
1689 {
1690 // A click in the window toolbar does not enter another window or
1691 // change Visual highlighting.
1692 winbar_click(wp, col);
1693 in_winbar = TRUE;
1694 return IN_OTHER_WIN | MOUSE_WINBAR;
1695 }
1696 in_winbar = FALSE;
1697#endif
1698
1699 // winpos and height may change in win_enter()!
1700 if (row >= wp->w_height) // In (or below) status line
1701 {
1702 on_status_line = row - wp->w_height + 1;
1703 dragwin = wp;
1704 }
1705 else
1706 on_status_line = 0;
1707 if (col >= wp->w_width) // In separator line
1708 {
1709 on_sep_line = col - wp->w_width + 1;
1710 dragwin = wp;
1711 }
1712 else
1713 on_sep_line = 0;
1714
1715 // The rightmost character of the status line might be a vertical
1716 // separator character if there is no connecting window to the right.
1717 if (on_status_line && on_sep_line)
1718 {
1719 if (stl_connected(wp))
1720 on_sep_line = 0;
1721 else
1722 on_status_line = 0;
1723 }
1724
1725 // Before jumping to another buffer, or moving the cursor for a left
1726 // click, stop Visual mode.
1727 if (VIsual_active
1728 && (wp->w_buffer != curwin->w_buffer
1729 || (!on_status_line && !on_sep_line
1730#ifdef FEAT_FOLDING
1731 && (
1732# ifdef FEAT_RIGHTLEFT
1733 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1734# endif
1735 col >= wp->w_p_fdc
1736# ifdef FEAT_CMDWIN
1737 + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
1738# endif
1739 )
1740#endif
1741 && (flags & MOUSE_MAY_STOP_VIS))))
1742 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001743 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001744 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001745 }
1746#ifdef FEAT_CMDWIN
1747 if (cmdwin_type != 0 && wp != curwin)
1748 {
1749 // A click outside the command-line window: Use modeless
1750 // selection if possible. Allow dragging the status lines.
1751 on_sep_line = 0;
1752# ifdef FEAT_CLIPBOARD
1753 if (on_status_line)
1754 return IN_STATUS_LINE;
1755 return IN_OTHER_WIN;
1756# else
1757 row = 0;
1758 col += wp->w_wincol;
1759 wp = curwin;
1760# endif
1761 }
1762#endif
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001763#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1764 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1765 // terminal in popup window: don't jump to another window
1766 return IN_OTHER_WIN;
1767#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001768 // Only change window focus when not clicking on or dragging the
1769 // status line. Do change focus when releasing the mouse button
1770 // (MOUSE_FOCUS was set above if we dragged first).
1771 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1772 win_enter(wp, TRUE); // can make wp invalid!
1773
1774 if (curwin != old_curwin)
1775 {
1776#ifdef CHECK_DOUBLE_CLICK
1777 // set topline, to be able to check for double click ourselves
1778 set_mouse_topline(curwin);
1779#endif
1780#ifdef FEAT_TERMINAL
1781 // when entering a terminal window may change state
1782 term_win_entered();
1783#endif
1784 }
1785 if (on_status_line) // In (or below) status line
1786 {
1787 // Don't use start_arrow() if we're in the same window
1788 if (curwin == old_curwin)
1789 return IN_STATUS_LINE;
1790 else
1791 return IN_STATUS_LINE | CURSOR_MOVED;
1792 }
1793 if (on_sep_line) // In (or below) status line
1794 {
1795 // Don't use start_arrow() if we're in the same window
1796 if (curwin == old_curwin)
1797 return IN_SEP_LINE;
1798 else
1799 return IN_SEP_LINE | CURSOR_MOVED;
1800 }
1801
1802 curwin->w_cursor.lnum = curwin->w_topline;
1803#ifdef FEAT_GUI
1804 // remember topline, needed for double click
1805 gui_prev_topline = curwin->w_topline;
1806# ifdef FEAT_DIFF
1807 gui_prev_topfill = curwin->w_topfill;
1808# endif
1809#endif
1810 }
1811 else if (on_status_line && which_button == MOUSE_LEFT)
1812 {
1813 if (dragwin != NULL)
1814 {
1815 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001816 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001817 - on_status_line;
1818 win_drag_status_line(dragwin, count);
1819 did_drag |= count;
1820 }
1821 return IN_STATUS_LINE; // Cursor didn't move
1822 }
1823 else if (on_sep_line && which_button == MOUSE_LEFT)
1824 {
1825 if (dragwin != NULL)
1826 {
1827 // Drag the separator column
1828 count = col - dragwin->w_wincol - dragwin->w_width + 1
1829 - on_sep_line;
1830 win_drag_vsep_line(dragwin, count);
1831 did_drag |= count;
1832 }
1833 return IN_SEP_LINE; // Cursor didn't move
1834 }
1835#ifdef FEAT_MENU
1836 else if (in_winbar)
1837 {
1838 // After a click on the window toolbar don't start Visual mode.
1839 return IN_OTHER_WIN | MOUSE_WINBAR;
1840 }
1841#endif
1842 else // keep_window_focus must be TRUE
1843 {
1844 // before moving the cursor for a left click, stop Visual mode
1845 if (flags & MOUSE_MAY_STOP_VIS)
1846 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001847 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001848 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001849 }
1850
1851#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1852 // Continue a modeless selection in another window.
1853 if (cmdwin_type != 0 && row < curwin->w_winrow)
1854 return IN_OTHER_WIN;
1855#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001856#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001857 if (in_popup_win)
1858 {
1859 if (popup_dragwin != NULL)
1860 {
1861 // dragging a popup window
1862 popup_drag(popup_dragwin);
1863 return IN_UNKNOWN;
1864 }
1865 // continue a modeless selection in a popup window
1866 click_in_popup_win = NULL;
1867 return IN_OTHER_WIN;
1868 }
1869#endif
1870
1871 row -= W_WINROW(curwin);
1872 col -= curwin->w_wincol;
1873
1874 // When clicking beyond the end of the window, scroll the screen.
1875 // Scroll by however many rows outside the window we are.
1876 if (row < 0)
1877 {
1878 count = 0;
1879 for (first = TRUE; curwin->w_topline > 1; )
1880 {
1881#ifdef FEAT_DIFF
1882 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1883 ++count;
1884 else
1885#endif
1886 count += plines(curwin->w_topline - 1);
1887 if (!first && count > -row)
1888 break;
1889 first = FALSE;
1890#ifdef FEAT_FOLDING
1891 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1892#endif
1893#ifdef FEAT_DIFF
1894 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1895 ++curwin->w_topfill;
1896 else
1897#endif
1898 {
1899 --curwin->w_topline;
1900#ifdef FEAT_DIFF
1901 curwin->w_topfill = 0;
1902#endif
1903 }
1904 }
1905#ifdef FEAT_DIFF
1906 check_topfill(curwin, FALSE);
1907#endif
1908 curwin->w_valid &=
1909 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001910 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001911 row = 0;
1912 }
1913 else if (row >= curwin->w_height)
1914 {
1915 count = 0;
1916 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
1917 {
1918#ifdef FEAT_DIFF
1919 if (curwin->w_topfill > 0)
1920 ++count;
1921 else
1922#endif
1923 count += plines(curwin->w_topline);
1924 if (!first && count > row - curwin->w_height + 1)
1925 break;
1926 first = FALSE;
1927#ifdef FEAT_FOLDING
1928 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
1929 && curwin->w_topline == curbuf->b_ml.ml_line_count)
1930 break;
1931#endif
1932#ifdef FEAT_DIFF
1933 if (curwin->w_topfill > 0)
1934 --curwin->w_topfill;
1935 else
1936#endif
1937 {
1938 ++curwin->w_topline;
1939#ifdef FEAT_DIFF
1940 curwin->w_topfill =
1941 diff_check_fill(curwin, curwin->w_topline);
1942#endif
1943 }
1944 }
1945#ifdef FEAT_DIFF
1946 check_topfill(curwin, FALSE);
1947#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001948 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001949 curwin->w_valid &=
1950 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1951 row = curwin->w_height - 1;
1952 }
1953 else if (row == 0)
1954 {
1955 // When dragging the mouse, while the text has been scrolled up as
1956 // far as it goes, moving the mouse in the top line should scroll
1957 // the text down (done later when recomputing w_topline).
1958 if (mouse_dragging > 0
1959 && curwin->w_cursor.lnum
1960 == curwin->w_buffer->b_ml.ml_line_count
1961 && curwin->w_cursor.lnum == curwin->w_topline)
1962 curwin->w_valid &= ~(VALID_TOPLINE);
1963 }
1964 }
1965
Bram Moolenaarb9081882022-07-09 04:56:24 +01001966 if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns
1967 && ScreenLines != NULL)
1968 {
1969 int off = LineOffset[prev_row] + prev_col;
1970
1971 // Only use ScreenCols[] after the window was redrawn. Mainly matters
1972 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01001973 // Do not use when 'virtualedit' is active.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001974 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE && !virtual_active())
Bram Moolenaarb9081882022-07-09 04:56:24 +01001975 col_from_screen = ScreenCols[off];
1976#ifdef FEAT_FOLDING
1977 // Remember the character under the mouse, it might be a '-' or '+' in
1978 // the fold column.
1979 mouse_char = ScreenLines[off];
1980#endif
1981 }
1982
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001983#ifdef FEAT_FOLDING
1984 // Check for position outside of the fold column.
1985 if (
1986# ifdef FEAT_RIGHTLEFT
1987 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
1988# endif
1989 col >= curwin->w_p_fdc
1990# ifdef FEAT_CMDWIN
1991 + (cmdwin_type == 0 ? 0 : 1)
1992# endif
1993 )
1994 mouse_char = ' ';
1995#endif
1996
1997 // compute the position in the buffer line from the posn on the screen
1998 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
1999 mouse_past_bottom = TRUE;
2000
2001 // Start Visual mode before coladvance(), for when 'sel' != "old"
2002 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2003 {
2004 check_visual_highlight();
2005 VIsual = old_cursor;
2006 VIsual_active = TRUE;
2007 VIsual_reselect = TRUE;
2008 // if 'selectmode' contains "mouse", start Select mode
2009 may_start_select('o');
2010 setmouse();
2011 if (p_smd && msg_silent == 0)
2012 redraw_cmdline = TRUE; // show visual mode later
2013 }
2014
Bram Moolenaarb9081882022-07-09 04:56:24 +01002015 if (col_from_screen >= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002016 {
Bram Moolenaarb9081882022-07-09 04:56:24 +01002017 // Use the column from ScreenCols[], it is accurate also after
2018 // concealed characters.
2019 curwin->w_cursor.col = col_from_screen;
2020 if (col_from_screen == MAXCOL)
2021 {
2022 curwin->w_curswant = col_from_screen;
2023 curwin->w_set_curswant = FALSE; // May still have been TRUE
2024 mouse_past_eol = TRUE;
2025 if (inclusive != NULL)
2026 *inclusive = TRUE;
2027 }
2028 else
2029 {
2030 curwin->w_set_curswant = TRUE;
2031 if (inclusive != NULL)
2032 *inclusive = FALSE;
2033 }
2034 check_cursor_col();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002035 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002036 else
2037 {
2038 curwin->w_curswant = col;
2039 curwin->w_set_curswant = FALSE; // May still have been TRUE
2040 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2041 {
2042 if (inclusive != NULL)
2043 *inclusive = TRUE;
2044 mouse_past_eol = TRUE;
2045 }
2046 else if (inclusive != NULL)
2047 *inclusive = FALSE;
2048 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002049
2050 count = IN_BUFFER;
2051 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2052 || curwin->w_cursor.col != old_cursor.col)
2053 count |= CURSOR_MOVED; // Cursor has moved
2054
2055# ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002056 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002057 count |= MOUSE_FOLD_OPEN;
2058 else if (mouse_char != ' ')
2059 count |= MOUSE_FOLD_CLOSE;
2060# endif
2061
2062 return count;
2063}
2064
2065/*
LemonBoyc27747e2022-05-07 12:25:40 +01002066 * Mouse scroll wheel: Default action is to scroll mouse_vert_step lines (or
2067 * mouse_hor_step, depending on the scroll direction), or one page when Shift or
2068 * Ctrl is used.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002069 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
2070 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
2071 */
2072 void
2073nv_mousescroll(cmdarg_T *cap)
2074{
2075 win_T *old_curwin = curwin, *wp;
2076
2077 if (mouse_row >= 0 && mouse_col >= 0)
2078 {
2079 int row, col;
2080
2081 row = mouse_row;
2082 col = mouse_col;
2083
2084 // find the window at the pointer coordinates
2085 wp = mouse_find_win(&row, &col, FIND_POPUP);
2086 if (wp == NULL)
2087 return;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002088#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002089 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
2090 return;
2091#endif
2092 curwin = wp;
2093 curbuf = curwin->w_buffer;
2094 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002095 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
2096 {
2097# ifdef FEAT_TERMINAL
2098 if (term_use_loop())
2099 // This window is a terminal window, send the mouse event there.
2100 // Set "typed" to FALSE to avoid an endless loop.
Bram Moolenaar1e814bc2019-11-03 21:19:41 +01002101 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002102 else
2103# endif
LemonBoyc27747e2022-05-07 12:25:40 +01002104 if (mouse_vert_step < 0 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002105 {
2106 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
2107 }
2108 else
2109 {
2110 // Don't scroll more than half the window height.
LemonBoyc27747e2022-05-07 12:25:40 +01002111 if (curwin->w_height < mouse_vert_step * 2)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002112 {
2113 cap->count1 = curwin->w_height / 2;
2114 if (cap->count1 == 0)
2115 cap->count1 = 1;
2116 }
2117 else
LemonBoyc27747e2022-05-07 12:25:40 +01002118 cap->count1 = mouse_vert_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002119 cap->count0 = cap->count1;
2120 nv_scroll_line(cap);
2121 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002122#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002123 if (WIN_IS_POPUP(curwin))
2124 popup_set_firstline(curwin);
2125#endif
2126 }
2127# ifdef FEAT_GUI
2128 else
2129 {
2130 // Horizontal scroll - only allowed when 'wrap' is disabled
2131 if (!curwin->w_p_wrap)
2132 {
LemonBoyc27747e2022-05-07 12:25:40 +01002133 int val, step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002134
LemonBoyc27747e2022-05-07 12:25:40 +01002135 if (mouse_hor_step < 0
2136 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002137 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01002138 else
2139 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002140 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
2141 if (val < 0)
2142 val = 0;
2143
2144 gui_do_horiz_scroll(val, TRUE);
2145 }
2146 }
2147# endif
2148# ifdef FEAT_SYN_HL
2149 if (curwin != old_curwin && curwin->w_p_cul)
2150 redraw_for_cursorline(curwin);
2151# endif
LemonBoy66e13ae2022-04-21 22:52:11 +01002152 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002153
2154 curwin->w_redr_status = TRUE;
2155
2156 curwin = old_curwin;
2157 curbuf = curwin->w_buffer;
2158}
2159
2160/*
2161 * Mouse clicks and drags.
2162 */
2163 void
2164nv_mouse(cmdarg_T *cap)
2165{
2166 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2167}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002168
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002169static int held_button = MOUSE_RELEASE;
2170
2171 void
2172reset_held_button()
2173{
2174 held_button = MOUSE_RELEASE;
2175}
2176
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002177/*
2178 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2179 * modifiers found in typebuf in 'modifiers'.
2180 */
2181 int
2182check_termcode_mouse(
2183 char_u *tp,
2184 int *slen,
2185 char_u *key_name,
2186 char_u *modifiers_start,
2187 int idx,
2188 int *modifiers)
2189{
2190 int j;
2191 char_u *p;
2192# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2193 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2194 char_u bytes[6];
2195 int num_bytes;
2196# endif
2197 int mouse_code = 0; // init for GCC
2198 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002199 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002200 int wheel_code = 0;
2201 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002202 static int orig_num_clicks = 1;
2203 static int orig_mouse_code = 0x0;
2204# ifdef CHECK_DOUBLE_CLICK
2205 static int orig_mouse_col = 0;
2206 static int orig_mouse_row = 0;
2207 static struct timeval orig_mouse_time = {0, 0};
2208 // time of previous mouse click
2209 struct timeval mouse_time; // time of current mouse click
2210 long timediff; // elapsed time in msec
2211# endif
2212
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002213 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002214
2215# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2216 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2217 if (key_name[0] == KS_MOUSE
2218# ifdef FEAT_MOUSE_GPM
2219 || key_name[0] == KS_GPM_MOUSE
2220# endif
2221 )
2222 {
2223 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002224 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002225 * 0x20 = left button down
2226 * 0x21 = middle button down
2227 * 0x22 = right button down
2228 * 0x23 = any button release
2229 * 0x60 = button 4 down (scroll wheel down)
2230 * 0x61 = button 5 down (scroll wheel up)
2231 * add 0x04 for SHIFT
2232 * add 0x08 for ALT
2233 * add 0x10 for CTRL
2234 * add 0x20 for mouse drag (0x40 is drag with left button)
2235 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2236 * 0x43 (drag + release) is also move
2237 * c == column + ' ' + 1 == column + 33
2238 * r == row + ' ' + 1 == row + 33
2239 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002240 * The coordinates are passed on through global variables. Ugly, but
2241 * this avoids trouble with mouse clicks at an unexpected moment and
2242 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002243 */
2244 for (;;)
2245 {
2246# ifdef FEAT_GUI
2247 if (gui.in_use)
2248 {
2249 // GUI uses more bits for columns > 223
2250 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2251 if (num_bytes == -1) // not enough coordinates
2252 return -1;
2253 mouse_code = bytes[0];
2254 mouse_col = 128 * (bytes[1] - ' ' - 1)
2255 + bytes[2] - ' ' - 1;
2256 mouse_row = 128 * (bytes[3] - ' ' - 1)
2257 + bytes[4] - ' ' - 1;
2258 }
2259 else
2260# endif
2261 {
2262 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2263 if (num_bytes == -1) // not enough coordinates
2264 return -1;
2265 mouse_code = bytes[0];
2266 mouse_col = bytes[1] - ' ' - 1;
2267 mouse_row = bytes[2] - ' ' - 1;
2268 }
2269 *slen += num_bytes;
2270
Bram Moolenaar13c04632020-07-12 13:47:42 +02002271 // If the following bytes is also a mouse code and it has the same
2272 // code, dump this one and get the next. This makes dragging a
2273 // whole lot faster.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002274# ifdef FEAT_GUI
2275 if (gui.in_use)
2276 j = 3;
2277 else
2278# endif
2279 j = get_termcode_len(idx);
2280 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2281 && tp[*slen + j] == mouse_code
2282 && tp[*slen + j + 1] != NUL
2283 && tp[*slen + j + 2] != NUL
2284# ifdef FEAT_GUI
2285 && (!gui.in_use
2286 || (tp[*slen + j + 3] != NUL
2287 && tp[*slen + j + 4] != NUL))
2288# endif
2289 )
2290 *slen += j;
2291 else
2292 break;
2293 }
2294 }
2295
2296 if (key_name[0] == KS_URXVT_MOUSE
2297 || key_name[0] == KS_SGR_MOUSE
2298 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2299 {
2300 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002301 // Almost identical to xterm mouse mode, except the values are decimal
2302 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002303 //
2304 // \033[%d;%d;%dM
2305 // ^-- row
2306 // ^----- column
2307 // ^-------- code
2308 //
2309 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002310 // Almost identical to xterm mouse mode, except the values are decimal
2311 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002312 //
2313 // \033[<%d;%d;%dM
2314 // ^-- row
2315 // ^----- column
2316 // ^-------- code
2317 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002318 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002319 // ^-- row
2320 // ^----- column
2321 // ^-------- code
2322 p = modifiers_start;
2323 if (p == NULL)
2324 return -1;
2325
2326 mouse_code = getdigits(&p);
2327 if (*p++ != ';')
2328 return -1;
2329
2330 // when mouse reporting is SGR, add 32 to mouse code
2331 if (key_name[0] == KS_SGR_MOUSE
2332 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2333 mouse_code += 32;
2334
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002335 mouse_col = getdigits(&p) - 1;
2336 if (*p++ != ';')
2337 return -1;
2338
2339 mouse_row = getdigits(&p) - 1;
2340
Bram Moolenaar13c04632020-07-12 13:47:42 +02002341 // The modifiers were the mouse coordinates, not the modifier keys
2342 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002343 *modifiers = 0;
2344 }
2345
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002346 if (key_name[0] == KS_SGR_MOUSE
2347 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2348 {
2349 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002350 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002351 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002352 // This is used below to set held_button.
2353 mouse_code |= MOUSE_RELEASE;
2354 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002355 }
2356 else
2357 {
2358 release_is_ambiguous = TRUE;
2359 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2360 is_release = TRUE;
2361 }
2362
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002363 if (key_name[0] == KS_MOUSE
2364# ifdef FEAT_MOUSE_GPM
2365 || key_name[0] == KS_GPM_MOUSE
2366# endif
2367# ifdef FEAT_MOUSE_URXVT
2368 || key_name[0] == KS_URXVT_MOUSE
2369# endif
2370 || key_name[0] == KS_SGR_MOUSE
2371 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2372 {
2373# if !defined(MSWIN)
2374 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002375 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002376 * Recognize the xterm mouse wheel, but not in the GUI, the
2377 * Linux console with GPM and the MS-DOS or Win32 console
2378 * (multi-clicks use >= 0x60).
2379 */
2380 if (mouse_code >= MOUSEWHEEL_LOW
2381# ifdef FEAT_GUI
2382 && !gui.in_use
2383# endif
2384# ifdef FEAT_MOUSE_GPM
2385 && key_name[0] != KS_GPM_MOUSE
2386# endif
2387 )
2388 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002389# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002390 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2391 // mouse-move event, using MOUSE_DRAG works
2392 mouse_code = MOUSE_DRAG;
2393 else
2394# endif
2395 // Keep the mouse_code before it's changed, so that we
2396 // remember that it was a mouse wheel click.
2397 wheel_code = mouse_code;
2398 }
2399# ifdef FEAT_MOUSE_XTERM
2400 else if (held_button == MOUSE_RELEASE
2401# ifdef FEAT_GUI
2402 && !gui.in_use
2403# endif
2404 && (mouse_code == 0x23 || mouse_code == 0x24
2405 || mouse_code == 0x40 || mouse_code == 0x41))
2406 {
2407 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2408 // And 0x40 and 0x41 are used by some xterm emulator.
2409 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002410 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002411 }
2412# endif
2413
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002414# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002415 else if (use_xterm_mouse() > 1)
2416 {
2417 if (mouse_code & MOUSE_DRAG_XTERM)
2418 mouse_code |= MOUSE_DRAG;
2419 }
2420# endif
2421# ifdef FEAT_XCLIPBOARD
2422 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2423 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002424 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002425 stop_xterm_trace();
2426 else
2427 start_xterm_trace(mouse_code);
2428 }
2429# endif
2430# endif
2431 }
2432# endif // !UNIX || FEAT_MOUSE_XTERM
2433# ifdef FEAT_MOUSE_NET
2434 if (key_name[0] == KS_NETTERM_MOUSE)
2435 {
2436 int mc, mr;
2437
2438 // expect a rather limited sequence like: balancing {
2439 // \033}6,45\r
2440 // '6' is the row, 45 is the column
2441 p = tp + *slen;
2442 mr = getdigits(&p);
2443 if (*p++ != ',')
2444 return -1;
2445 mc = getdigits(&p);
2446 if (*p++ != '\r')
2447 return -1;
2448
2449 mouse_col = mc - 1;
2450 mouse_row = mr - 1;
2451 mouse_code = MOUSE_LEFT;
2452 *slen += (int)(p - (tp + *slen));
2453 }
2454# endif // FEAT_MOUSE_NET
2455# ifdef FEAT_MOUSE_JSB
2456 if (key_name[0] == KS_JSBTERM_MOUSE)
2457 {
2458 int mult, val, iter, button, status;
2459
2460 /*
2461 * JSBTERM Input Model
2462 * \033[0~zw uniq escape sequence
2463 * (L-x) Left button pressed - not pressed x not reporting
2464 * (M-x) Middle button pressed - not pressed x not reporting
2465 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002466 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2467 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002468 * ### X cursor position padded to 3 digits
2469 * ### Y cursor position padded to 3 digits
2470 * (s-x) SHIFT key pressed - not pressed x not reporting
2471 * (c-x) CTRL key pressed - not pressed x not reporting
2472 * \033\\ terminating sequence
2473 */
2474 p = tp + *slen;
2475 button = mouse_code = 0;
2476 switch (*p++)
2477 {
2478 case 'L': button = 1; break;
2479 case '-': break;
2480 case 'x': break; // ignore sequence
2481 default: return -1; // Unknown Result
2482 }
2483 switch (*p++)
2484 {
2485 case 'M': button |= 2; break;
2486 case '-': break;
2487 case 'x': break; // ignore sequence
2488 default: return -1; // Unknown Result
2489 }
2490 switch (*p++)
2491 {
2492 case 'R': button |= 4; break;
2493 case '-': break;
2494 case 'x': break; // ignore sequence
2495 default: return -1; // Unknown Result
2496 }
2497 status = *p++;
2498 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2499 mult /= 10, p++)
2500 if (*p >= '0' && *p <= '9')
2501 val += (*p - '0') * mult;
2502 else
2503 return -1;
2504 mouse_col = val;
2505 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2506 mult /= 10, p++)
2507 if (*p >= '0' && *p <= '9')
2508 val += (*p - '0') * mult;
2509 else
2510 return -1;
2511 mouse_row = val;
2512 switch (*p++)
2513 {
2514 case 's': button |= 8; break; // SHIFT key Pressed
2515 case '-': break; // Not Pressed
2516 case 'x': break; // Not Reporting
2517 default: return -1; // Unknown Result
2518 }
2519 switch (*p++)
2520 {
2521 case 'c': button |= 16; break; // CTRL key Pressed
2522 case '-': break; // Not Pressed
2523 case 'x': break; // Not Reporting
2524 default: return -1; // Unknown Result
2525 }
2526 if (*p++ != '\033')
2527 return -1;
2528 if (*p++ != '\\')
2529 return -1;
2530 switch (status)
2531 {
2532 case 'D': // Double Click
2533 case 'S': // Single Click
2534 if (button & 1) mouse_code |= MOUSE_LEFT;
2535 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2536 if (button & 4) mouse_code |= MOUSE_RIGHT;
2537 if (button & 8) mouse_code |= MOUSE_SHIFT;
2538 if (button & 16) mouse_code |= MOUSE_CTRL;
2539 break;
2540 case 'm': // Mouse move
2541 if (button & 1) mouse_code |= MOUSE_LEFT;
2542 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2543 if (button & 4) mouse_code |= MOUSE_RIGHT;
2544 if (button & 8) mouse_code |= MOUSE_SHIFT;
2545 if (button & 16) mouse_code |= MOUSE_CTRL;
2546 if ((button & 7) != 0)
2547 {
2548 held_button = mouse_code;
2549 mouse_code |= MOUSE_DRAG;
2550 }
2551 is_drag = TRUE;
2552 showmode();
2553 break;
2554 case 'd': // Button Down
2555 if (button & 1) mouse_code |= MOUSE_LEFT;
2556 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2557 if (button & 4) mouse_code |= MOUSE_RIGHT;
2558 if (button & 8) mouse_code |= MOUSE_SHIFT;
2559 if (button & 16) mouse_code |= MOUSE_CTRL;
2560 break;
2561 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002562 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002563 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002564 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002565 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002566 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002567 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002568 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002569 if (button & 8)
2570 mouse_code |= MOUSE_SHIFT;
2571 if (button & 16)
2572 mouse_code |= MOUSE_CTRL;
2573 break;
2574 default: return -1; // Unknown Result
2575 }
2576
2577 *slen += (p - (tp + *slen));
2578 }
2579# endif // FEAT_MOUSE_JSB
2580# ifdef FEAT_MOUSE_DEC
2581 if (key_name[0] == KS_DEC_MOUSE)
2582 {
2583 /*
2584 * The DEC Locator Input Model
2585 * Netterm delivers the code sequence:
2586 * \033[2;4;24;80&w (left button down)
2587 * \033[3;0;24;80&w (left button up)
2588 * \033[6;1;24;80&w (right button down)
2589 * \033[7;0;24;80&w (right button up)
2590 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2591 * Pe is the event code
2592 * Pb is the button code
2593 * Pr is the row coordinate
2594 * Pc is the column coordinate
2595 * Pp is the third coordinate (page number)
2596 * Pe, the event code indicates what event caused this report
2597 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002598 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002599 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002600 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002601 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002602 * 2 - left button down
2603 * 3 - left button up
2604 * 4 - middle button down
2605 * 5 - middle button up
2606 * 6 - right button down
2607 * 7 - right button up
2608 * 8 - fourth button down
2609 * 9 - fourth button up
2610 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002611 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2612 * down if any. The state of the four buttons on the locator
2613 * correspond to the low four bits of the decimal value, "1" means
2614 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002615 * 0 - no buttons down,
2616 * 1 - right,
2617 * 2 - middle,
2618 * 4 - left,
2619 * 8 - fourth
2620 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002621 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2622 * position is undefined (outside the terminal window for example).
2623 * Pc is the column coordinate of the locator position in the page,
2624 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2625 * position is undefined (outside the terminal window for example).
2626 * Pp is the page coordinate of the locator position encoded as an
2627 * ASCII decimal value. The page coordinate may be omitted if the
2628 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002629 */
2630 int Pe, Pb, Pr, Pc;
2631
2632 p = tp + *slen;
2633
2634 // get event status
2635 Pe = getdigits(&p);
2636 if (*p++ != ';')
2637 return -1;
2638
2639 // get button status
2640 Pb = getdigits(&p);
2641 if (*p++ != ';')
2642 return -1;
2643
2644 // get row status
2645 Pr = getdigits(&p);
2646 if (*p++ != ';')
2647 return -1;
2648
2649 // get column status
2650 Pc = getdigits(&p);
2651
2652 // the page parameter is optional
2653 if (*p == ';')
2654 {
2655 p++;
2656 (void)getdigits(&p);
2657 }
2658 if (*p++ != '&')
2659 return -1;
2660 if (*p++ != 'w')
2661 return -1;
2662
2663 mouse_code = 0;
2664 switch (Pe)
2665 {
2666 case 0: return -1; // position request while unavailable
2667 case 1: // a response to a locator position request includes
2668 // the status of all buttons
2669 Pb &= 7; // mask off and ignore fourth button
2670 if (Pb & 4)
2671 mouse_code = MOUSE_LEFT;
2672 if (Pb & 2)
2673 mouse_code = MOUSE_MIDDLE;
2674 if (Pb & 1)
2675 mouse_code = MOUSE_RIGHT;
2676 if (Pb)
2677 {
2678 held_button = mouse_code;
2679 mouse_code |= MOUSE_DRAG;
2680 WantQueryMouse = TRUE;
2681 }
2682 is_drag = TRUE;
2683 showmode();
2684 break;
2685 case 2: mouse_code = MOUSE_LEFT;
2686 WantQueryMouse = TRUE;
2687 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002688 case 3: mouse_code = MOUSE_LEFT;
2689 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002690 break;
2691 case 4: mouse_code = MOUSE_MIDDLE;
2692 WantQueryMouse = TRUE;
2693 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002694 case 5: mouse_code = MOUSE_MIDDLE;
2695 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002696 break;
2697 case 6: mouse_code = MOUSE_RIGHT;
2698 WantQueryMouse = TRUE;
2699 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002700 case 7: mouse_code = MOUSE_RIGHT;
2701 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002702 break;
2703 case 8: return -1; // fourth button down
2704 case 9: return -1; // fourth button up
2705 case 10: return -1; // mouse outside of filter rectangle
2706 default: return -1; // should never occur
2707 }
2708
2709 mouse_col = Pc - 1;
2710 mouse_row = Pr - 1;
2711
2712 *slen += (int)(p - (tp + *slen));
2713 }
2714# endif // FEAT_MOUSE_DEC
2715# ifdef FEAT_MOUSE_PTERM
2716 if (key_name[0] == KS_PTERM_MOUSE)
2717 {
2718 int button, num_clicks, action;
2719
2720 p = tp + *slen;
2721
2722 action = getdigits(&p);
2723 if (*p++ != ';')
2724 return -1;
2725
2726 mouse_row = getdigits(&p);
2727 if (*p++ != ';')
2728 return -1;
2729 mouse_col = getdigits(&p);
2730 if (*p++ != ';')
2731 return -1;
2732
2733 button = getdigits(&p);
2734 mouse_code = 0;
2735
2736 switch (button)
2737 {
2738 case 4: mouse_code = MOUSE_LEFT; break;
2739 case 1: mouse_code = MOUSE_RIGHT; break;
2740 case 2: mouse_code = MOUSE_MIDDLE; break;
2741 default: return -1;
2742 }
2743
2744 switch (action)
2745 {
2746 case 31: // Initial press
2747 if (*p++ != ';')
2748 return -1;
2749
2750 num_clicks = getdigits(&p); // Not used
2751 break;
2752
2753 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002754 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002755 break;
2756
2757 case 33: // Drag
2758 held_button = mouse_code;
2759 mouse_code |= MOUSE_DRAG;
2760 break;
2761
2762 default:
2763 return -1;
2764 }
2765
2766 if (*p++ != 't')
2767 return -1;
2768
2769 *slen += (p - (tp + *slen));
2770 }
2771# endif // FEAT_MOUSE_PTERM
2772
2773 // Interpret the mouse code
2774 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002775 if (is_release)
2776 current_button |= MOUSE_RELEASE;
2777
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002778 if (current_button == MOUSE_RELEASE
2779# ifdef FEAT_MOUSE_XTERM
2780 && wheel_code == 0
2781# endif
2782 )
2783 {
2784 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002785 * If we get a mouse drag or release event when there is no mouse
2786 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2787 * below.
2788 * (can happen when you hold down two buttons and then let them go, or
2789 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002790 */
2791 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2792 is_drag = TRUE;
2793 current_button = held_button;
2794 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002795 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002796 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002797 if (wheel_code == 0)
2798 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002799# ifdef CHECK_DOUBLE_CLICK
2800# ifdef FEAT_MOUSE_GPM
2801 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002802 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2803 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002804 */
2805# ifdef FEAT_GUI
2806 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
2807# else
2808 if (key_name[0] != KS_GPM_MOUSE)
2809# endif
2810# else
2811# ifdef FEAT_GUI
2812 if (!gui.in_use)
2813# endif
2814# endif
2815 {
2816 /*
2817 * Compute the time elapsed since the previous mouse click.
2818 */
2819 gettimeofday(&mouse_time, NULL);
2820 if (orig_mouse_time.tv_sec == 0)
2821 {
2822 /*
2823 * Avoid computing the difference between mouse_time
2824 * and orig_mouse_time for the first click, as the
2825 * difference would be huge and would cause
2826 * multiplication overflow.
2827 */
2828 timediff = p_mouset;
2829 }
2830 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002831 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002832 orig_mouse_time = mouse_time;
2833 if (mouse_code == orig_mouse_code
2834 && timediff < p_mouset
2835 && orig_num_clicks != 4
2836 && orig_mouse_col == mouse_col
2837 && orig_mouse_row == mouse_row
2838 && (is_mouse_topline(curwin)
2839 // Double click in tab pages line also works
2840 // when window contents changes.
2841 || (mouse_row == 0 && firstwin->w_winrow > 0))
2842 )
2843 ++orig_num_clicks;
2844 else
2845 orig_num_clicks = 1;
2846 orig_mouse_col = mouse_col;
2847 orig_mouse_row = mouse_row;
2848 set_mouse_topline(curwin);
2849 }
2850# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
2851 else
2852 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2853# endif
2854# else
2855 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2856# endif
2857 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002858 }
2859 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002860 }
2861 if (!is_drag)
2862 held_button = mouse_code & MOUSE_CLICK_MASK;
2863
2864 /*
2865 * Translate the actual mouse event into a pseudo mouse event.
2866 * First work out what modifiers are to be used.
2867 */
2868 if (orig_mouse_code & MOUSE_SHIFT)
2869 *modifiers |= MOD_MASK_SHIFT;
2870 if (orig_mouse_code & MOUSE_CTRL)
2871 *modifiers |= MOD_MASK_CTRL;
2872 if (orig_mouse_code & MOUSE_ALT)
2873 *modifiers |= MOD_MASK_ALT;
2874 if (orig_num_clicks == 2)
2875 *modifiers |= MOD_MASK_2CLICK;
2876 else if (orig_num_clicks == 3)
2877 *modifiers |= MOD_MASK_3CLICK;
2878 else if (orig_num_clicks == 4)
2879 *modifiers |= MOD_MASK_4CLICK;
2880
Bram Moolenaar13c04632020-07-12 13:47:42 +02002881 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2882 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002883 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002884 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002885 {
2886 if (wheel_code & MOUSE_CTRL)
2887 *modifiers |= MOD_MASK_CTRL;
2888 if (wheel_code & MOUSE_ALT)
2889 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002890
2891 if (wheel_code & 1 && wheel_code & 2)
2892 key_name[1] = (int)KE_MOUSELEFT;
2893 else if (wheel_code & 2)
2894 key_name[1] = (int)KE_MOUSERIGHT;
2895 else if (wheel_code & 1)
2896 key_name[1] = (int)KE_MOUSEUP;
2897 else
2898 key_name[1] = (int)KE_MOUSEDOWN;
2899
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002900 held_button = MOUSE_RELEASE;
2901 }
2902 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002903 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002904
Bram Moolenaar13c04632020-07-12 13:47:42 +02002905
2906 // Make sure the mouse position is valid. Some terminals may return weird
2907 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002908 if (mouse_col >= Columns)
2909 mouse_col = Columns - 1;
2910 if (mouse_row >= Rows)
2911 mouse_row = Rows - 1;
2912
2913 return 0;
2914}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002915
2916// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002917
2918/*
2919 * Compute the buffer line position from the screen position "rowp" / "colp" in
2920 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02002921 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
2922 * caches the plines_win() result from a previous call. Entry is zero if not
2923 * computed yet. There must be no text or setting changes since the entry is
2924 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002925 * Returns TRUE if the position is below the last line.
2926 */
2927 int
2928mouse_comp_pos(
2929 win_T *win,
2930 int *rowp,
2931 int *colp,
2932 linenr_T *lnump,
2933 int *plines_cache)
2934{
2935 int col = *colp;
2936 int row = *rowp;
2937 linenr_T lnum;
2938 int retval = FALSE;
2939 int off;
2940 int count;
2941
2942#ifdef FEAT_RIGHTLEFT
2943 if (win->w_p_rl)
2944 col = win->w_width - 1 - col;
2945#endif
2946
2947 lnum = win->w_topline;
2948
2949 while (row > 0)
2950 {
2951 int cache_idx = lnum - win->w_topline;
2952
Bram Moolenaar452143c2020-07-15 17:38:21 +02002953 // Only "Rows" lines are cached, with folding we'll run out of entries
2954 // and use the slow way.
2955 if (plines_cache != NULL && cache_idx < Rows
2956 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002957 count = plines_cache[cache_idx];
2958 else
2959 {
2960#ifdef FEAT_DIFF
2961 // Don't include filler lines in "count"
2962 if (win->w_p_diff
2963# ifdef FEAT_FOLDING
2964 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
2965# endif
2966 )
2967 {
2968 if (lnum == win->w_topline)
2969 row -= win->w_topfill;
2970 else
2971 row -= diff_check_fill(win, lnum);
2972 count = plines_win_nofill(win, lnum, TRUE);
2973 }
2974 else
2975#endif
2976 count = plines_win(win, lnum, TRUE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02002977 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002978 plines_cache[cache_idx] = count;
2979 }
2980 if (count > row)
2981 break; // Position is in this buffer line.
2982#ifdef FEAT_FOLDING
2983 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
2984#endif
2985 if (lnum == win->w_buffer->b_ml.ml_line_count)
2986 {
2987 retval = TRUE;
2988 break; // past end of file
2989 }
2990 row -= count;
2991 ++lnum;
2992 }
2993
2994 if (!retval)
2995 {
2996 // Compute the column without wrapping.
2997 off = win_col_off(win) - win_col_off2(win);
2998 if (col < off)
2999 col = off;
3000 col += row * (win->w_width - off);
3001 // add skip column (for long wrapping line)
3002 col += win->w_skipcol;
3003 }
3004
3005 if (!win->w_p_wrap)
3006 col += win->w_leftcol;
3007
3008 // skip line number and fold column in front of the line
3009 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003010 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003011 {
3012#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003013 // if mouse is clicked on the gutter, then inform the netbeans server
3014 if (*colp < win_col_off(win))
3015 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003016#endif
3017 col = 0;
3018 }
3019
3020 *colp = col;
3021 *rowp = row;
3022 *lnump = lnum;
3023 return retval;
3024}
3025
3026/*
3027 * Find the window at screen position "*rowp" and "*colp". The positions are
3028 * updated to become relative to the top-left of the window.
3029 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3030 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3031 * windows.
3032 * Returns NULL when something is wrong.
3033 */
3034 win_T *
3035mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3036{
3037 frame_T *fp;
3038 win_T *wp;
3039
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003040#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003041 win_T *pwp = NULL;
3042
3043 if (popup != IGNORE_POPUP)
3044 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003045 popup_reset_handled(POPUP_HANDLED_1);
3046 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003047 {
3048 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3049 && *colp >= wp->w_wincol
3050 && *colp < wp->w_wincol + popup_width(wp))
3051 pwp = wp;
3052 }
3053 if (pwp != NULL)
3054 {
3055 if (popup == FAIL_POPUP)
3056 return NULL;
3057 *rowp -= pwp->w_winrow;
3058 *colp -= pwp->w_wincol;
3059 return pwp;
3060 }
3061 }
3062#endif
3063
3064 fp = topframe;
3065 *rowp -= firstwin->w_winrow;
3066 for (;;)
3067 {
3068 if (fp->fr_layout == FR_LEAF)
3069 break;
3070 if (fp->fr_layout == FR_ROW)
3071 {
3072 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3073 {
3074 if (*colp < fp->fr_width)
3075 break;
3076 *colp -= fp->fr_width;
3077 }
3078 }
3079 else // fr_layout == FR_COL
3080 {
3081 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3082 {
3083 if (*rowp < fp->fr_height)
3084 break;
3085 *rowp -= fp->fr_height;
3086 }
3087 }
3088 }
3089 // When using a timer that closes a window the window might not actually
3090 // exist.
3091 FOR_ALL_WINDOWS(wp)
3092 if (wp == fp->fr_win)
3093 {
3094#ifdef FEAT_MENU
3095 *rowp -= wp->w_winbar_height;
3096#endif
3097 return wp;
3098 }
3099 return NULL;
3100}
3101
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003102#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003103 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003104/*
3105 * Convert a virtual (screen) column to a character column.
3106 * The first column is one.
3107 */
3108 int
3109vcol2col(win_T *wp, linenr_T lnum, int vcol)
3110{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003111 char_u *line;
3112 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003113
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003114 // try to advance to the specified column
3115 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3116 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3117 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003118 {
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003119 cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
3120 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003121 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003122 clear_chartabsize_arg(&cts);
3123
3124 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003125}
3126#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003127
3128#if defined(FEAT_EVAL) || defined(PROTO)
3129 void
3130f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3131{
3132 dict_T *d;
3133 win_T *wp;
3134 int row = mouse_row;
3135 int col = mouse_col;
3136 varnumber_T winid = 0;
3137 varnumber_T winrow = 0;
3138 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003139 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003140 varnumber_T column = 0;
3141
Bram Moolenaar93a10962022-06-16 11:42:09 +01003142 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003143 return;
3144 d = rettv->vval.v_dict;
3145
3146 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3147 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3148
3149 wp = mouse_find_win(&row, &col, FIND_POPUP);
3150 if (wp != NULL)
3151 {
3152 int top_off = 0;
3153 int left_off = 0;
3154 int height = wp->w_height + wp->w_status_height;
3155
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003156#ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003157 if (WIN_IS_POPUP(wp))
3158 {
3159 top_off = popup_top_extra(wp);
3160 left_off = popup_left_extra(wp);
3161 height = popup_height(wp);
3162 }
3163#endif
3164 if (row < height)
3165 {
3166 winid = wp->w_id;
3167 winrow = row + 1;
3168 wincol = col + 1;
3169 row -= top_off;
3170 col -= left_off;
3171 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3172 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003173 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
3174 col = vcol2col(wp, lnum, col);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003175 column = col + 1;
3176 }
3177 }
3178 }
3179 dict_add_number(d, "winid", winid);
3180 dict_add_number(d, "winrow", winrow);
3181 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003182 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003183 dict_add_number(d, "column", column);
3184}
3185#endif