blob: 452a30131be559a3201ba6421cf92f02f5b51bef [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)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100485 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100486 }
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
Martin Tournoij7904fa42022-10-04 16:28:45 +0100491 if (is_click && cmdwin_type == 0 && mouse_col < Columns)
Bram Moolenaar80525752022-08-24 19:27:45 +0100492 {
493 in_tab_line = TRUE;
494 c1 = TabPageIdxs[mouse_col];
495 if (c1 >= 0)
496 {
497 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
498 {
499 // double click opens new page
500 end_visual_mode_keep_button();
501 tabpage_new();
502 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
503 }
504 else
505 {
506 // Go to specified tab page, or next one if not clicking
507 // on a label.
508 goto_tabpage(c1);
509
510 // It's like clicking on the status line of a window.
511 if (curwin != old_curwin)
512 end_visual_mode_keep_button();
513 }
514 }
515 else
516 {
517 tabpage_T *tp;
518
519 // Close the current or specified tab page.
520 if (c1 == -999)
521 tp = curtab;
522 else
523 tp = find_tabpage(-c1);
524 if (tp == curtab)
525 {
526 if (first_tabpage->tp_next != NULL)
527 tabpage_close(FALSE);
528 }
529 else if (tp != NULL)
530 tabpage_close_other(tp, FALSE);
531 }
532 }
533 return TRUE;
534 }
535 else if (is_drag && in_tab_line)
536 {
537 c1 = TabPageIdxs[mouse_col];
538 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200539 return FALSE;
540 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200541 }
542
543 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
544 // right button up -> pop-up menu
545 // shift-left button -> right button
546 // alt-left button -> alt-right button
547 if (mouse_model_popup())
548 {
549 if (which_button == MOUSE_RIGHT
550 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
551 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200552#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200553# ifdef FEAT_GUI
554 if (gui.in_use)
555 {
556# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200557 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200558 if (!is_click)
559 // Ignore right button release events, only shows the popup
560 // menu on the button down event.
561 return FALSE;
562# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100563# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200564 if (is_click || is_drag)
565 // Ignore right button down and drag mouse events. Windows
566 // only shows the popup menu on the button up event.
567 return FALSE;
568# endif
569 }
570# endif
571# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
572 else
573# endif
574# if defined(FEAT_TERM_POPUP_MENU)
575 if (!is_click)
576 // Ignore right button release events, only shows the popup
577 // menu on the button down event.
578 return FALSE;
579#endif
580
581 jump_flags = 0;
582 if (STRCMP(p_mousem, "popup_setpos") == 0)
583 {
584 // First set the cursor position before showing the popup
585 // menu.
586 if (VIsual_active)
587 {
588 pos_T m_pos;
589
590 // set MOUSE_MAY_STOP_VIS if we are outside the
591 // selection or the current window (might have false
592 // negative here)
593 if (mouse_row < curwin->w_winrow
594 || mouse_row
595 > (curwin->w_winrow + curwin->w_height))
596 jump_flags = MOUSE_MAY_STOP_VIS;
597 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
598 jump_flags = MOUSE_MAY_STOP_VIS;
599 else
600 {
601 if ((LT_POS(curwin->w_cursor, VIsual)
602 && (LT_POS(m_pos, curwin->w_cursor)
603 || LT_POS(VIsual, m_pos)))
604 || (LT_POS(VIsual, curwin->w_cursor)
605 && (LT_POS(m_pos, VIsual)
606 || LT_POS(curwin->w_cursor, m_pos))))
607 {
608 jump_flags = MOUSE_MAY_STOP_VIS;
609 }
610 else if (VIsual_mode == Ctrl_V)
611 {
612 getvcols(curwin, &curwin->w_cursor, &VIsual,
613 &leftcol, &rightcol);
614 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
615 if (m_pos.col < leftcol || m_pos.col > rightcol)
616 jump_flags = MOUSE_MAY_STOP_VIS;
617 }
618 }
619 }
620 else
621 jump_flags = MOUSE_MAY_STOP_VIS;
622 }
623 if (jump_flags)
624 {
625 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100626 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200627 setcursor();
628 out_flush(); // Update before showing popup menu
629 }
630# ifdef FEAT_MENU
631 show_popupmenu();
632 got_click = FALSE; // ignore release events
633# endif
634 return (jump_flags & CURSOR_MOVED) != 0;
635#else
636 return FALSE;
637#endif
638 }
639 if (which_button == MOUSE_LEFT
640 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
641 {
642 which_button = MOUSE_RIGHT;
643 mod_mask &= ~MOD_MASK_SHIFT;
644 }
645 }
646
Bram Moolenaar24959102022-05-07 20:01:16 +0100647 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200648 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
649 {
650 if (which_button == MOUSE_LEFT)
651 {
652 if (is_click)
653 {
654 // stop Visual mode for a left click in a window, but not when
655 // on a status line
656 if (VIsual_active)
657 jump_flags |= MOUSE_MAY_STOP_VIS;
658 }
659 else if (mouse_has(MOUSE_VISUAL))
660 jump_flags |= MOUSE_MAY_VIS;
661 }
662 else if (which_button == MOUSE_RIGHT)
663 {
664 if (is_click && VIsual_active)
665 {
666 // Remember the start and end of visual before moving the
667 // cursor.
668 if (LT_POS(curwin->w_cursor, VIsual))
669 {
670 start_visual = curwin->w_cursor;
671 end_visual = VIsual;
672 }
673 else
674 {
675 start_visual = VIsual;
676 end_visual = curwin->w_cursor;
677 }
678 }
679 jump_flags |= MOUSE_FOCUS;
680 if (mouse_has(MOUSE_VISUAL))
681 jump_flags |= MOUSE_MAY_VIS;
682 }
683 }
684
685 // If an operator is pending, ignore all drags and releases until the
686 // next mouse click.
687 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
688 {
689 got_click = FALSE;
690 oap->motion_type = MCHAR;
691 }
692
693 // When releasing the button let jump_to_mouse() know.
694 if (!is_click && !is_drag)
695 jump_flags |= MOUSE_RELEASED;
696
697 // JUMP!
698 jump_flags = jump_to_mouse(jump_flags,
699 oap == NULL ? NULL : &(oap->inclusive), which_button);
700
701#ifdef FEAT_MENU
702 // A click in the window toolbar has no side effects.
703 if (jump_flags & MOUSE_WINBAR)
704 return FALSE;
705#endif
706 moved = (jump_flags & CURSOR_MOVED);
707 in_status_line = (jump_flags & IN_STATUS_LINE);
708 in_sep_line = (jump_flags & IN_SEP_LINE);
709
710#ifdef FEAT_NETBEANS_INTG
711 if (isNetbeansBuffer(curbuf)
712 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
713 {
714 int key = KEY2TERMCAP1(c);
715
716 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
717 || key == (int)KE_RIGHTRELEASE)
718 netbeans_button_release(which_button);
719 }
720#endif
721
722 // When jumping to another window, clear a pending operator. That's a bit
723 // friendlier than beeping and not jumping to that window.
724 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
725 clearop(oap);
726
727#ifdef FEAT_FOLDING
728 if (mod_mask == 0
729 && !is_drag
730 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
731 && which_button == MOUSE_LEFT)
732 {
733 // open or close a fold at this line
734 if (jump_flags & MOUSE_FOLD_OPEN)
735 openFold(curwin->w_cursor.lnum, 1L);
736 else
737 closeFold(curwin->w_cursor.lnum, 1L);
738 // don't move the cursor if still in the same window
739 if (curwin == old_curwin)
740 curwin->w_cursor = save_cursor;
741 }
742#endif
743
Martin Tournoij7904fa42022-10-04 16:28:45 +0100744#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200745 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
746 {
747 clip_modeless(which_button, is_click, is_drag);
748 return FALSE;
749 }
750#endif
751
752 // Set global flag that we are extending the Visual area with mouse
753 // dragging; temporarily minimize 'scrolloff'.
754 if (VIsual_active && is_drag && get_scrolloff_value())
755 {
756 // In the very first line, allow scrolling one line
757 if (mouse_row == 0)
758 mouse_dragging = 2;
759 else
760 mouse_dragging = 1;
761 }
762
763 // When dragging the mouse above the window, scroll down.
764 if (is_drag && mouse_row < 0 && !in_status_line)
765 {
766 scroll_redraw(FALSE, 1L);
767 mouse_row = 0;
768 }
769
770 if (start_visual.lnum) // right click in visual mode
771 {
772 // When ALT is pressed make Visual mode blockwise.
773 if (mod_mask & MOD_MASK_ALT)
774 VIsual_mode = Ctrl_V;
775
776 // In Visual-block mode, divide the area in four, pick up the corner
777 // that is in the quarter that the cursor is in.
778 if (VIsual_mode == Ctrl_V)
779 {
780 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
781 if (curwin->w_curswant > (leftcol + rightcol) / 2)
782 end_visual.col = leftcol;
783 else
784 end_visual.col = rightcol;
785 if (curwin->w_cursor.lnum >=
786 (start_visual.lnum + end_visual.lnum) / 2)
787 end_visual.lnum = start_visual.lnum;
788
789 // move VIsual to the right column
790 start_visual = curwin->w_cursor; // save the cursor pos
791 curwin->w_cursor = end_visual;
792 coladvance(end_visual.col);
793 VIsual = curwin->w_cursor;
794 curwin->w_cursor = start_visual; // restore the cursor
795 }
796 else
797 {
798 // If the click is before the start of visual, change the start.
799 // If the click is after the end of visual, change the end. If
800 // the click is inside the visual, change the closest side.
801 if (LT_POS(curwin->w_cursor, start_visual))
802 VIsual = end_visual;
803 else if (LT_POS(end_visual, curwin->w_cursor))
804 VIsual = start_visual;
805 else
806 {
807 // In the same line, compare column number
808 if (end_visual.lnum == start_visual.lnum)
809 {
810 if (curwin->w_cursor.col - start_visual.col >
811 end_visual.col - curwin->w_cursor.col)
812 VIsual = start_visual;
813 else
814 VIsual = end_visual;
815 }
816
817 // In different lines, compare line number
818 else
819 {
820 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
821 (end_visual.lnum - curwin->w_cursor.lnum);
822
823 if (diff > 0) // closest to end
824 VIsual = start_visual;
825 else if (diff < 0) // closest to start
826 VIsual = end_visual;
827 else // in the middle line
828 {
829 if (curwin->w_cursor.col <
830 (start_visual.col + end_visual.col) / 2)
831 VIsual = end_visual;
832 else
833 VIsual = start_visual;
834 }
835 }
836 }
837 }
838 }
839 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100840 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200841 stuffcharReadbuff(Ctrl_O);
842
843 // Middle mouse click: Put text before cursor.
844 if (which_button == MOUSE_MIDDLE)
845 {
846#ifdef FEAT_CLIPBOARD
847 if (clip_star.available && regname == 0)
848 regname = '*';
849#endif
850 if (yank_register_mline(regname))
851 {
852 if (mouse_past_bottom)
853 dir = FORWARD;
854 }
855 else if (mouse_past_eol)
856 dir = FORWARD;
857
858 if (fixindent)
859 {
860 c1 = (dir == BACKWARD) ? '[' : ']';
861 c2 = 'p';
862 }
863 else
864 {
865 c1 = (dir == FORWARD) ? 'p' : 'P';
866 c2 = NUL;
867 }
868 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
869
870 // Remember where the paste started, so in edit() Insstart can be set
871 // to this position
872 if (restart_edit != 0)
873 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200874 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200875 }
876
877#if defined(FEAT_QUICKFIX)
878 // Ctrl-Mouse click or double click in a quickfix window jumps to the
879 // error under the mouse pointer.
880 else if (((mod_mask & MOD_MASK_CTRL)
881 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
882 && bt_quickfix(curbuf))
883 {
884 if (curwin->w_llist_ref == NULL) // quickfix window
885 do_cmdline_cmd((char_u *)".cc");
886 else // location list window
887 do_cmdline_cmd((char_u *)".ll");
888 got_click = FALSE; // ignore drag&release now
889 }
890#endif
891
892 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
893 // under the mouse pointer.
894 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
895 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
896 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100897 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200898 stuffcharReadbuff(Ctrl_O);
899 stuffcharReadbuff(Ctrl_RSB);
900 got_click = FALSE; // ignore drag&release now
901 }
902
903 // Shift-Mouse click searches for the next occurrence of the word under
904 // the mouse pointer
905 else if ((mod_mask & MOD_MASK_SHIFT))
906 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100907 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200908 stuffcharReadbuff(Ctrl_O);
909 if (which_button == MOUSE_LEFT)
910 stuffcharReadbuff('*');
911 else // MOUSE_RIGHT
912 stuffcharReadbuff('#');
913 }
914
915 // Handle double clicks, unless on status line
916 else if (in_status_line)
917 {
918#ifdef FEAT_MOUSESHAPE
919 if ((is_drag || is_click) && !drag_status_line)
920 {
921 drag_status_line = TRUE;
922 update_mouseshape(-1);
923 }
924#endif
925 }
926 else if (in_sep_line)
927 {
928#ifdef FEAT_MOUSESHAPE
929 if ((is_drag || is_click) && !drag_sep_line)
930 {
931 drag_sep_line = TRUE;
932 update_mouseshape(-1);
933 }
934#endif
935 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100936 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
937 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200938 && mouse_has(MOUSE_VISUAL))
939 {
940 if (is_click || !VIsual_active)
941 {
942 if (VIsual_active)
943 orig_cursor = VIsual;
944 else
945 {
946 check_visual_highlight();
947 VIsual = curwin->w_cursor;
948 orig_cursor = VIsual;
949 VIsual_active = TRUE;
950 VIsual_reselect = TRUE;
951 // start Select mode if 'selectmode' contains "mouse"
952 may_start_select('o');
953 setmouse();
954 }
955 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
956 {
957 // Double click with ALT pressed makes it blockwise.
958 if (mod_mask & MOD_MASK_ALT)
959 VIsual_mode = Ctrl_V;
960 else
961 VIsual_mode = 'v';
962 }
963 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
964 VIsual_mode = 'V';
965 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
966 VIsual_mode = Ctrl_V;
967#ifdef FEAT_CLIPBOARD
968 // Make sure the clipboard gets updated. Needed because start and
969 // end may still be the same, and the selection needs to be owned
970 clip_star.vmode = NUL;
971#endif
972 }
973 // A double click selects a word or a block.
974 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
975 {
976 pos_T *pos = NULL;
977 int gc;
978
979 if (is_click)
980 {
981 // If the character under the cursor (skipping white space) is
982 // not a word character, try finding a match and select a (),
983 // {}, [], #if/#endif, etc. block.
984 end_visual = curwin->w_cursor;
985 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
986 inc(&end_visual);
987 if (oap != NULL)
988 oap->motion_type = MCHAR;
989 if (oap != NULL
990 && VIsual_mode == 'v'
991 && !vim_iswordc(gchar_pos(&end_visual))
992 && EQUAL_POS(curwin->w_cursor, VIsual)
993 && (pos = findmatch(oap, NUL)) != NULL)
994 {
995 curwin->w_cursor = *pos;
996 if (oap->motion_type == MLINE)
997 VIsual_mode = 'V';
998 else if (*p_sel == 'e')
999 {
1000 if (LT_POS(curwin->w_cursor, VIsual))
1001 ++VIsual.col;
1002 else
1003 ++curwin->w_cursor.col;
1004 }
1005 }
1006 }
1007
1008 if (pos == NULL && (is_click || is_drag))
1009 {
1010 // When not found a match or when dragging: extend to include
1011 // a word.
1012 if (LT_POS(curwin->w_cursor, orig_cursor))
1013 {
1014 find_start_of_word(&curwin->w_cursor);
1015 find_end_of_word(&VIsual);
1016 }
1017 else
1018 {
1019 find_start_of_word(&VIsual);
1020 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1021 curwin->w_cursor.col +=
1022 (*mb_ptr2len)(ml_get_cursor());
1023 find_end_of_word(&curwin->w_cursor);
1024 }
1025 }
1026 curwin->w_set_curswant = TRUE;
1027 }
1028 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001029 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001030 }
1031 else if (VIsual_active && !old_active)
1032 {
1033 if (mod_mask & MOD_MASK_ALT)
1034 VIsual_mode = Ctrl_V;
1035 else
1036 VIsual_mode = 'v';
1037 }
1038
1039 // If Visual mode changed show it later.
1040 if ((!VIsual_active && old_active && mode_displayed)
1041 || (VIsual_active && p_smd && msg_silent == 0
1042 && (!old_active || VIsual_mode != old_mode)))
1043 redraw_cmdline = TRUE;
1044
1045 return moved;
1046}
1047
1048 void
1049ins_mouse(int c)
1050{
1051 pos_T tpos;
1052 win_T *old_curwin = curwin;
1053
1054# ifdef FEAT_GUI
1055 // When GUI is active, also move/paste when 'mouse' is empty
1056 if (!gui.in_use)
1057# endif
1058 if (!mouse_has(MOUSE_INSERT))
1059 return;
1060
1061 undisplay_dollar();
1062 tpos = curwin->w_cursor;
1063 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1064 {
1065 win_T *new_curwin = curwin;
1066
1067 if (curwin != old_curwin && win_valid(old_curwin))
1068 {
1069 // Mouse took us to another window. We need to go back to the
1070 // previous one to stop insert there properly.
1071 curwin = old_curwin;
1072 curbuf = curwin->w_buffer;
1073#ifdef FEAT_JOB_CHANNEL
1074 if (bt_prompt(curbuf))
1075 // Restart Insert mode when re-entering the prompt buffer.
1076 curbuf->b_prompt_insert = 'A';
1077#endif
1078 }
1079 start_arrow(curwin == old_curwin ? &tpos : NULL);
1080 if (curwin != new_curwin && win_valid(new_curwin))
1081 {
1082 curwin = new_curwin;
1083 curbuf = curwin->w_buffer;
1084 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001085 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001086 }
1087
1088 // redraw status lines (in case another window became active)
1089 redraw_statuslines();
1090}
1091
1092 void
1093ins_mousescroll(int dir)
1094{
1095 pos_T tpos;
1096 win_T *old_curwin = curwin, *wp;
1097 int did_scroll = FALSE;
1098
1099 tpos = curwin->w_cursor;
1100
1101 if (mouse_row >= 0 && mouse_col >= 0)
1102 {
1103 int row, col;
1104
1105 row = mouse_row;
1106 col = mouse_col;
1107
1108 // find the window at the pointer coordinates
1109 wp = mouse_find_win(&row, &col, FIND_POPUP);
1110 if (wp == NULL)
1111 return;
1112 curwin = wp;
1113 curbuf = curwin->w_buffer;
1114 }
1115 if (curwin == old_curwin)
1116 undisplay_dollar();
1117
1118 // Don't scroll the window in which completion is being done.
1119 if (!pum_visible() || curwin != old_curwin)
1120 {
LemonBoyc27747e2022-05-07 12:25:40 +01001121 long step;
1122
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001123 if (dir == MSCR_DOWN || dir == MSCR_UP)
1124 {
LemonBoyc27747e2022-05-07 12:25:40 +01001125 if (mouse_vert_step < 0
1126 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1127 step = (long)(curwin->w_botline - curwin->w_topline);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001128 else
LemonBoyc27747e2022-05-07 12:25:40 +01001129 step = mouse_vert_step;
1130 scroll_redraw(dir, step);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001131# ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001132 if (WIN_IS_POPUP(curwin))
1133 popup_set_firstline(curwin);
1134# endif
1135 }
1136#ifdef FEAT_GUI
1137 else
1138 {
LemonBoyc27747e2022-05-07 12:25:40 +01001139 int val;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001140
LemonBoyc27747e2022-05-07 12:25:40 +01001141 if (mouse_hor_step < 0
1142 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001143 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01001144 else
1145 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001146 val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
1147 if (val < 0)
1148 val = 0;
1149 gui_do_horiz_scroll(val, TRUE);
1150 }
1151#endif
1152 did_scroll = TRUE;
LemonBoy66e13ae2022-04-21 22:52:11 +01001153 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001154 }
1155
1156 curwin->w_redr_status = TRUE;
1157
1158 curwin = old_curwin;
1159 curbuf = curwin->w_buffer;
1160
1161 // The popup menu may overlay the window, need to redraw it.
1162 // TODO: Would be more efficient to only redraw the windows that are
1163 // overlapped by the popup menu.
1164 if (pum_visible() && did_scroll)
1165 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001166 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001167 ins_compl_show_pum();
1168 }
1169
1170 if (!EQUAL_POS(curwin->w_cursor, tpos))
1171 {
1172 start_arrow(&tpos);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001173 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001174 }
1175}
1176
1177/*
1178 * Return TRUE if "c" is a mouse key.
1179 */
1180 int
1181is_mouse_key(int c)
1182{
1183 return c == K_LEFTMOUSE
1184 || c == K_LEFTMOUSE_NM
1185 || c == K_LEFTDRAG
1186 || c == K_LEFTRELEASE
1187 || c == K_LEFTRELEASE_NM
1188 || c == K_MOUSEMOVE
1189 || c == K_MIDDLEMOUSE
1190 || c == K_MIDDLEDRAG
1191 || c == K_MIDDLERELEASE
1192 || c == K_RIGHTMOUSE
1193 || c == K_RIGHTDRAG
1194 || c == K_RIGHTRELEASE
1195 || c == K_MOUSEDOWN
1196 || c == K_MOUSEUP
1197 || c == K_MOUSELEFT
1198 || c == K_MOUSERIGHT
1199 || c == K_X1MOUSE
1200 || c == K_X1DRAG
1201 || c == K_X1RELEASE
1202 || c == K_X2MOUSE
1203 || c == K_X2DRAG
1204 || c == K_X2RELEASE;
1205}
1206
1207static struct mousetable
1208{
1209 int pseudo_code; // Code for pseudo mouse event
1210 int button; // Which mouse button is it?
1211 int is_click; // Is it a mouse button click event?
1212 int is_drag; // Is it a mouse drag event?
1213} mouse_table[] =
1214{
1215 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1216#ifdef FEAT_GUI
1217 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1218#endif
1219 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1220 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1221#ifdef FEAT_GUI
1222 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1223#endif
1224 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1225 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1226 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1227 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1228 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1229 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1230 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1231 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1232 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1233 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1234 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1235 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1236 // DRAG without CLICK
1237 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1238 // RELEASE without CLICK
1239 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1240 {0, 0, 0, 0},
1241};
1242
1243/*
1244 * Look up the given mouse code to return the relevant information in the other
1245 * arguments. Return which button is down or was released.
1246 */
1247 int
1248get_mouse_button(int code, int *is_click, int *is_drag)
1249{
1250 int i;
1251
1252 for (i = 0; mouse_table[i].pseudo_code; i++)
1253 if (code == mouse_table[i].pseudo_code)
1254 {
1255 *is_click = mouse_table[i].is_click;
1256 *is_drag = mouse_table[i].is_drag;
1257 return mouse_table[i].button;
1258 }
1259 return 0; // Shouldn't get here
1260}
1261
1262/*
1263 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1264 * the given information about which mouse button is down, and whether the
1265 * mouse was clicked, dragged or released.
1266 */
1267 int
1268get_pseudo_mouse_code(
1269 int button, // eg MOUSE_LEFT
1270 int is_click,
1271 int is_drag)
1272{
1273 int i;
1274
1275 for (i = 0; mouse_table[i].pseudo_code; i++)
1276 if (button == mouse_table[i].button
1277 && is_click == mouse_table[i].is_click
1278 && is_drag == mouse_table[i].is_drag)
1279 {
1280#ifdef FEAT_GUI
1281 // Trick: a non mappable left click and release has mouse_col -1
1282 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1283 // gui_mouse_moved()
1284 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1285 {
1286 if (mouse_col < 0)
1287 mouse_col = 0;
1288 else
1289 mouse_col -= MOUSE_COLOFF;
1290 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1291 return (int)KE_LEFTMOUSE_NM;
1292 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1293 return (int)KE_LEFTRELEASE_NM;
1294 }
1295#endif
1296 return mouse_table[i].pseudo_code;
1297 }
1298 return (int)KE_IGNORE; // not recognized, ignore it
1299}
1300
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001301# define HMT_NORMAL 1
1302# define HMT_NETTERM 2
1303# define HMT_DEC 4
1304# define HMT_JSBTERM 8
1305# define HMT_PTERM 16
1306# define HMT_URXVT 32
1307# define HMT_GPM 64
1308# define HMT_SGR 128
1309# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001310static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001311
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001312 void
1313set_mouse_termcode(
1314 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1315 char_u *s)
1316{
1317 char_u name[2];
1318
1319 name[0] = n;
1320 name[1] = KE_FILLER;
1321 add_termcode(name, s, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001322# ifdef FEAT_MOUSE_JSB
1323 if (n == KS_JSBTERM_MOUSE)
1324 has_mouse_termcode |= HMT_JSBTERM;
1325 else
1326# endif
1327# ifdef FEAT_MOUSE_NET
1328 if (n == KS_NETTERM_MOUSE)
1329 has_mouse_termcode |= HMT_NETTERM;
1330 else
1331# endif
1332# ifdef FEAT_MOUSE_DEC
1333 if (n == KS_DEC_MOUSE)
1334 has_mouse_termcode |= HMT_DEC;
1335 else
1336# endif
1337# ifdef FEAT_MOUSE_PTERM
1338 if (n == KS_PTERM_MOUSE)
1339 has_mouse_termcode |= HMT_PTERM;
1340 else
1341# endif
1342# ifdef FEAT_MOUSE_URXVT
1343 if (n == KS_URXVT_MOUSE)
1344 has_mouse_termcode |= HMT_URXVT;
1345 else
1346# endif
1347# ifdef FEAT_MOUSE_GPM
1348 if (n == KS_GPM_MOUSE)
1349 has_mouse_termcode |= HMT_GPM;
1350 else
1351# endif
1352 if (n == KS_SGR_MOUSE)
1353 has_mouse_termcode |= HMT_SGR;
1354 else if (n == KS_SGR_MOUSE_RELEASE)
1355 has_mouse_termcode |= HMT_SGR_REL;
1356 else
1357 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001358}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001359
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001360# if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001361 void
1362del_mouse_termcode(
1363 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1364{
1365 char_u name[2];
1366
1367 name[0] = n;
1368 name[1] = KE_FILLER;
1369 del_termcode(name);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001370# ifdef FEAT_MOUSE_JSB
1371 if (n == KS_JSBTERM_MOUSE)
1372 has_mouse_termcode &= ~HMT_JSBTERM;
1373 else
1374# endif
1375# ifdef FEAT_MOUSE_NET
1376 if (n == KS_NETTERM_MOUSE)
1377 has_mouse_termcode &= ~HMT_NETTERM;
1378 else
1379# endif
1380# ifdef FEAT_MOUSE_DEC
1381 if (n == KS_DEC_MOUSE)
1382 has_mouse_termcode &= ~HMT_DEC;
1383 else
1384# endif
1385# ifdef FEAT_MOUSE_PTERM
1386 if (n == KS_PTERM_MOUSE)
1387 has_mouse_termcode &= ~HMT_PTERM;
1388 else
1389# endif
1390# ifdef FEAT_MOUSE_URXVT
1391 if (n == KS_URXVT_MOUSE)
1392 has_mouse_termcode &= ~HMT_URXVT;
1393 else
1394# endif
1395# ifdef FEAT_MOUSE_GPM
1396 if (n == KS_GPM_MOUSE)
1397 has_mouse_termcode &= ~HMT_GPM;
1398 else
1399# endif
1400 if (n == KS_SGR_MOUSE)
1401 has_mouse_termcode &= ~HMT_SGR;
1402 else if (n == KS_SGR_MOUSE_RELEASE)
1403 has_mouse_termcode &= ~HMT_SGR_REL;
1404 else
1405 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001406}
1407# endif
1408
1409/*
1410 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1411 */
1412 void
1413setmouse(void)
1414{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001415 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001416
1417# ifdef FEAT_MOUSESHAPE
1418 update_mouseshape(-1);
1419# endif
1420
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001421 // Should be outside proc, but may break MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001422# ifdef FEAT_GUI
1423 // In the GUI the mouse is always enabled.
1424 if (gui.in_use)
1425 return;
1426# endif
1427 // be quick when mouse is off
1428 if (*p_mouse == NUL || has_mouse_termcode == 0)
1429 return;
1430
1431 // don't switch mouse on when not in raw mode (Ex mode)
1432 if (cur_tmode != TMODE_RAW)
1433 {
1434 mch_setmouse(FALSE);
1435 return;
1436 }
1437
1438 if (VIsual_active)
1439 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001440 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1441 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001442 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001443 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001444 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001445 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001446 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001447 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001448 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1449 else
1450 checkfor = MOUSE_NORMAL; // assume normal mode
1451
1452 if (mouse_has(checkfor))
1453 mch_setmouse(TRUE);
1454 else
1455 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001456}
1457
1458/*
1459 * Return TRUE if
1460 * - "c" is in 'mouse', or
1461 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1462 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1463 * normal editing mode (not at hit-return message).
1464 */
1465 int
1466mouse_has(int c)
1467{
1468 char_u *p;
1469
1470 for (p = p_mouse; *p; ++p)
1471 switch (*p)
1472 {
1473 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1474 return TRUE;
1475 break;
1476 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1477 return TRUE;
1478 break;
1479 default: if (c == *p) return TRUE; break;
1480 }
1481 return FALSE;
1482}
1483
1484/*
1485 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1486 */
1487 int
1488mouse_model_popup(void)
1489{
1490 return (p_mousem[0] == 'p');
1491}
1492
1493/*
1494 * Move the cursor to the specified row and column on the screen.
1495 * Change current window if necessary. Returns an integer with the
1496 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1497 *
1498 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1499 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1500 *
1501 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1502 * if the mouse is outside the window then the text will scroll, or if the
1503 * mouse was previously on a status line, then the status line may be dragged.
1504 *
1505 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1506 * cursor is moved unless the cursor was on a status line.
1507 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1508 * IN_SEP_LINE depending on where the cursor was clicked.
1509 *
1510 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1511 * the mouse is on the status line of the same window.
1512 *
1513 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1514 * the last call.
1515 *
1516 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1517 * remembered.
1518 */
1519 int
1520jump_to_mouse(
1521 int flags,
1522 int *inclusive, // used for inclusive operator, can be NULL
1523 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1524{
1525 static int on_status_line = 0; // #lines below bottom of window
1526 static int on_sep_line = 0; // on separator right of window
1527#ifdef FEAT_MENU
1528 static int in_winbar = FALSE;
1529#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001530#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001531 static int in_popup_win = FALSE;
1532 static win_T *click_in_popup_win = NULL;
1533#endif
1534 static int prev_row = -1;
1535 static int prev_col = -1;
1536 static win_T *dragwin = NULL; // window being dragged
1537 static int did_drag = FALSE; // drag was noticed
1538
1539 win_T *wp, *old_curwin;
1540 pos_T old_cursor;
1541 int count;
1542 int first;
1543 int row = mouse_row;
1544 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001545 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001546#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001547 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001548#endif
1549
1550 mouse_past_bottom = FALSE;
1551 mouse_past_eol = FALSE;
1552
1553 if (flags & MOUSE_RELEASED)
1554 {
1555 // On button release we may change window focus if positioned on a
1556 // status line and no dragging happened.
1557 if (dragwin != NULL && !did_drag)
1558 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1559 dragwin = NULL;
1560 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001561#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001562 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1563 popup_close_for_mouse_click(click_in_popup_win);
1564
1565 popup_dragwin = NULL;
1566 click_in_popup_win = NULL;
1567#endif
1568 }
1569
1570 if ((flags & MOUSE_DID_MOVE)
1571 && prev_row == mouse_row
1572 && prev_col == mouse_col)
1573 {
1574retnomove:
1575 // before moving the cursor for a left click which is NOT in a status
1576 // line, stop Visual mode
1577 if (on_status_line)
1578 return IN_STATUS_LINE;
1579 if (on_sep_line)
1580 return IN_SEP_LINE;
1581#ifdef FEAT_MENU
1582 if (in_winbar)
1583 {
1584 // A quick second click may arrive as a double-click, but we use it
1585 // as a second click in the WinBar.
1586 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1587 {
1588 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1589 if (wp == NULL)
1590 return IN_UNKNOWN;
1591 winbar_click(wp, col);
1592 }
1593 return IN_OTHER_WIN | MOUSE_WINBAR;
1594 }
1595#endif
1596 if (flags & MOUSE_MAY_STOP_VIS)
1597 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001598 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001599 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001600 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001601#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001602 // Continue a modeless selection in another window.
1603 if (cmdwin_type != 0 && row < curwin->w_winrow)
1604 return IN_OTHER_WIN;
1605#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001606#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001607 // Continue a modeless selection in a popup window or dragging it.
1608 if (in_popup_win)
1609 {
1610 click_in_popup_win = NULL; // don't close it on release
1611 if (popup_dragwin != NULL)
1612 {
1613 // dragging a popup window
1614 popup_drag(popup_dragwin);
1615 return IN_UNKNOWN;
1616 }
1617 return IN_OTHER_WIN;
1618 }
1619#endif
1620 return IN_BUFFER;
1621 }
1622
1623 prev_row = mouse_row;
1624 prev_col = mouse_col;
1625
1626 if (flags & MOUSE_SETPOS)
1627 goto retnomove; // ugly goto...
1628
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001629 old_curwin = curwin;
1630 old_cursor = curwin->w_cursor;
1631
1632 if (!(flags & MOUSE_FOCUS))
1633 {
1634 if (row < 0 || col < 0) // check if it makes sense
1635 return IN_UNKNOWN;
1636
1637 // find the window where the row is in and adjust "row" and "col" to be
1638 // relative to top-left of the window
1639 wp = mouse_find_win(&row, &col, FIND_POPUP);
1640 if (wp == NULL)
1641 return IN_UNKNOWN;
1642 dragwin = NULL;
1643
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001644#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001645 // Click in a popup window may start dragging or modeless selection,
1646 // but not much else.
1647 if (WIN_IS_POPUP(wp))
1648 {
1649 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001650 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001651 in_popup_win = TRUE;
1652 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1653 {
1654 return IN_UNKNOWN;
1655 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001656 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001657 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001658 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001659 {
1660 popup_dragwin = wp;
1661 popup_start_drag(wp, row, col);
1662 return IN_UNKNOWN;
1663 }
1664 // Only close on release, otherwise it's not possible to drag or do
1665 // modeless selection.
1666 else if (wp->w_popup_close == POPCLOSE_CLICK
1667 && which_button == MOUSE_LEFT)
1668 {
1669 click_in_popup_win = wp;
1670 }
1671 else if (which_button == MOUSE_LEFT)
1672 // If the click is in the scrollbar, may scroll up/down.
1673 popup_handle_scrollbar_click(wp, row, col);
1674# ifdef FEAT_CLIPBOARD
1675 return IN_OTHER_WIN;
1676# else
1677 return IN_UNKNOWN;
1678# endif
1679 }
1680 in_popup_win = FALSE;
1681 popup_dragwin = NULL;
1682#endif
1683#ifdef FEAT_MENU
1684 if (row == -1)
1685 {
1686 // A click in the window toolbar does not enter another window or
1687 // change Visual highlighting.
1688 winbar_click(wp, col);
1689 in_winbar = TRUE;
1690 return IN_OTHER_WIN | MOUSE_WINBAR;
1691 }
1692 in_winbar = FALSE;
1693#endif
1694
1695 // winpos and height may change in win_enter()!
1696 if (row >= wp->w_height) // In (or below) status line
1697 {
1698 on_status_line = row - wp->w_height + 1;
1699 dragwin = wp;
1700 }
1701 else
1702 on_status_line = 0;
1703 if (col >= wp->w_width) // In separator line
1704 {
1705 on_sep_line = col - wp->w_width + 1;
1706 dragwin = wp;
1707 }
1708 else
1709 on_sep_line = 0;
1710
1711 // The rightmost character of the status line might be a vertical
1712 // separator character if there is no connecting window to the right.
1713 if (on_status_line && on_sep_line)
1714 {
1715 if (stl_connected(wp))
1716 on_sep_line = 0;
1717 else
1718 on_status_line = 0;
1719 }
1720
1721 // Before jumping to another buffer, or moving the cursor for a left
1722 // click, stop Visual mode.
1723 if (VIsual_active
1724 && (wp->w_buffer != curwin->w_buffer
1725 || (!on_status_line && !on_sep_line
1726#ifdef FEAT_FOLDING
1727 && (
1728# ifdef FEAT_RIGHTLEFT
1729 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1730# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01001731 col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001732 )
1733#endif
1734 && (flags & MOUSE_MAY_STOP_VIS))))
1735 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001736 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001737 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001738 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001739 if (cmdwin_type != 0 && wp != curwin)
1740 {
1741 // A click outside the command-line window: Use modeless
1742 // selection if possible. Allow dragging the status lines.
1743 on_sep_line = 0;
1744# ifdef FEAT_CLIPBOARD
1745 if (on_status_line)
1746 return IN_STATUS_LINE;
1747 return IN_OTHER_WIN;
1748# else
1749 row = 0;
1750 col += wp->w_wincol;
1751 wp = curwin;
1752# endif
1753 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001754#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1755 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1756 // terminal in popup window: don't jump to another window
1757 return IN_OTHER_WIN;
1758#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001759 // Only change window focus when not clicking on or dragging the
1760 // status line. Do change focus when releasing the mouse button
1761 // (MOUSE_FOCUS was set above if we dragged first).
1762 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1763 win_enter(wp, TRUE); // can make wp invalid!
1764
1765 if (curwin != old_curwin)
1766 {
1767#ifdef CHECK_DOUBLE_CLICK
1768 // set topline, to be able to check for double click ourselves
1769 set_mouse_topline(curwin);
1770#endif
1771#ifdef FEAT_TERMINAL
1772 // when entering a terminal window may change state
1773 term_win_entered();
1774#endif
1775 }
1776 if (on_status_line) // In (or below) status line
1777 {
1778 // Don't use start_arrow() if we're in the same window
1779 if (curwin == old_curwin)
1780 return IN_STATUS_LINE;
1781 else
1782 return IN_STATUS_LINE | CURSOR_MOVED;
1783 }
1784 if (on_sep_line) // In (or below) status line
1785 {
1786 // Don't use start_arrow() if we're in the same window
1787 if (curwin == old_curwin)
1788 return IN_SEP_LINE;
1789 else
1790 return IN_SEP_LINE | CURSOR_MOVED;
1791 }
1792
1793 curwin->w_cursor.lnum = curwin->w_topline;
1794#ifdef FEAT_GUI
1795 // remember topline, needed for double click
1796 gui_prev_topline = curwin->w_topline;
1797# ifdef FEAT_DIFF
1798 gui_prev_topfill = curwin->w_topfill;
1799# endif
1800#endif
1801 }
1802 else if (on_status_line && which_button == MOUSE_LEFT)
1803 {
1804 if (dragwin != NULL)
1805 {
1806 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001807 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001808 - on_status_line;
1809 win_drag_status_line(dragwin, count);
1810 did_drag |= count;
1811 }
1812 return IN_STATUS_LINE; // Cursor didn't move
1813 }
1814 else if (on_sep_line && which_button == MOUSE_LEFT)
1815 {
1816 if (dragwin != NULL)
1817 {
1818 // Drag the separator column
1819 count = col - dragwin->w_wincol - dragwin->w_width + 1
1820 - on_sep_line;
1821 win_drag_vsep_line(dragwin, count);
1822 did_drag |= count;
1823 }
1824 return IN_SEP_LINE; // Cursor didn't move
1825 }
1826#ifdef FEAT_MENU
1827 else if (in_winbar)
1828 {
1829 // After a click on the window toolbar don't start Visual mode.
1830 return IN_OTHER_WIN | MOUSE_WINBAR;
1831 }
1832#endif
1833 else // keep_window_focus must be TRUE
1834 {
1835 // before moving the cursor for a left click, stop Visual mode
1836 if (flags & MOUSE_MAY_STOP_VIS)
1837 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001838 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001839 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001840 }
1841
Martin Tournoij7904fa42022-10-04 16:28:45 +01001842#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001843 // Continue a modeless selection in another window.
1844 if (cmdwin_type != 0 && row < curwin->w_winrow)
1845 return IN_OTHER_WIN;
1846#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001847#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001848 if (in_popup_win)
1849 {
1850 if (popup_dragwin != NULL)
1851 {
1852 // dragging a popup window
1853 popup_drag(popup_dragwin);
1854 return IN_UNKNOWN;
1855 }
1856 // continue a modeless selection in a popup window
1857 click_in_popup_win = NULL;
1858 return IN_OTHER_WIN;
1859 }
1860#endif
1861
1862 row -= W_WINROW(curwin);
1863 col -= curwin->w_wincol;
1864
1865 // When clicking beyond the end of the window, scroll the screen.
1866 // Scroll by however many rows outside the window we are.
1867 if (row < 0)
1868 {
1869 count = 0;
1870 for (first = TRUE; curwin->w_topline > 1; )
1871 {
1872#ifdef FEAT_DIFF
1873 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1874 ++count;
1875 else
1876#endif
1877 count += plines(curwin->w_topline - 1);
1878 if (!first && count > -row)
1879 break;
1880 first = FALSE;
1881#ifdef FEAT_FOLDING
1882 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1883#endif
1884#ifdef FEAT_DIFF
1885 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1886 ++curwin->w_topfill;
1887 else
1888#endif
1889 {
1890 --curwin->w_topline;
1891#ifdef FEAT_DIFF
1892 curwin->w_topfill = 0;
1893#endif
1894 }
1895 }
1896#ifdef FEAT_DIFF
1897 check_topfill(curwin, FALSE);
1898#endif
1899 curwin->w_valid &=
1900 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001901 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001902 row = 0;
1903 }
1904 else if (row >= curwin->w_height)
1905 {
1906 count = 0;
1907 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
1908 {
1909#ifdef FEAT_DIFF
1910 if (curwin->w_topfill > 0)
1911 ++count;
1912 else
1913#endif
1914 count += plines(curwin->w_topline);
1915 if (!first && count > row - curwin->w_height + 1)
1916 break;
1917 first = FALSE;
1918#ifdef FEAT_FOLDING
1919 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
1920 && curwin->w_topline == curbuf->b_ml.ml_line_count)
1921 break;
1922#endif
1923#ifdef FEAT_DIFF
1924 if (curwin->w_topfill > 0)
1925 --curwin->w_topfill;
1926 else
1927#endif
1928 {
1929 ++curwin->w_topline;
1930#ifdef FEAT_DIFF
1931 curwin->w_topfill =
1932 diff_check_fill(curwin, curwin->w_topline);
1933#endif
1934 }
1935 }
1936#ifdef FEAT_DIFF
1937 check_topfill(curwin, FALSE);
1938#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001939 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001940 curwin->w_valid &=
1941 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1942 row = curwin->w_height - 1;
1943 }
1944 else if (row == 0)
1945 {
1946 // When dragging the mouse, while the text has been scrolled up as
1947 // far as it goes, moving the mouse in the top line should scroll
1948 // the text down (done later when recomputing w_topline).
1949 if (mouse_dragging > 0
1950 && curwin->w_cursor.lnum
1951 == curwin->w_buffer->b_ml.ml_line_count
1952 && curwin->w_cursor.lnum == curwin->w_topline)
1953 curwin->w_valid &= ~(VALID_TOPLINE);
1954 }
1955 }
1956
Bram Moolenaarb9081882022-07-09 04:56:24 +01001957 if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns
1958 && ScreenLines != NULL)
1959 {
1960 int off = LineOffset[prev_row] + prev_col;
1961
1962 // Only use ScreenCols[] after the window was redrawn. Mainly matters
1963 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01001964 // Do not use when 'virtualedit' is active.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001965 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE && !virtual_active())
Bram Moolenaarb9081882022-07-09 04:56:24 +01001966 col_from_screen = ScreenCols[off];
1967#ifdef FEAT_FOLDING
1968 // Remember the character under the mouse, it might be a '-' or '+' in
1969 // the fold column.
1970 mouse_char = ScreenLines[off];
1971#endif
1972 }
1973
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001974#ifdef FEAT_FOLDING
1975 // Check for position outside of the fold column.
1976 if (
1977# ifdef FEAT_RIGHTLEFT
1978 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
1979# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01001980 col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001981 )
1982 mouse_char = ' ';
1983#endif
1984
1985 // compute the position in the buffer line from the posn on the screen
1986 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
1987 mouse_past_bottom = TRUE;
1988
1989 // Start Visual mode before coladvance(), for when 'sel' != "old"
1990 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
1991 {
1992 check_visual_highlight();
1993 VIsual = old_cursor;
1994 VIsual_active = TRUE;
1995 VIsual_reselect = TRUE;
1996 // if 'selectmode' contains "mouse", start Select mode
1997 may_start_select('o');
1998 setmouse();
1999 if (p_smd && msg_silent == 0)
2000 redraw_cmdline = TRUE; // show visual mode later
2001 }
2002
Bram Moolenaarb9081882022-07-09 04:56:24 +01002003 if (col_from_screen >= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002004 {
Bram Moolenaarb9081882022-07-09 04:56:24 +01002005 // Use the column from ScreenCols[], it is accurate also after
2006 // concealed characters.
2007 curwin->w_cursor.col = col_from_screen;
2008 if (col_from_screen == MAXCOL)
2009 {
2010 curwin->w_curswant = col_from_screen;
2011 curwin->w_set_curswant = FALSE; // May still have been TRUE
2012 mouse_past_eol = TRUE;
2013 if (inclusive != NULL)
2014 *inclusive = TRUE;
2015 }
2016 else
2017 {
2018 curwin->w_set_curswant = TRUE;
2019 if (inclusive != NULL)
2020 *inclusive = FALSE;
2021 }
2022 check_cursor_col();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002023 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002024 else
2025 {
2026 curwin->w_curswant = col;
2027 curwin->w_set_curswant = FALSE; // May still have been TRUE
2028 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2029 {
2030 if (inclusive != NULL)
2031 *inclusive = TRUE;
2032 mouse_past_eol = TRUE;
2033 }
2034 else if (inclusive != NULL)
2035 *inclusive = FALSE;
2036 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002037
2038 count = IN_BUFFER;
2039 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2040 || curwin->w_cursor.col != old_cursor.col)
2041 count |= CURSOR_MOVED; // Cursor has moved
2042
2043# ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002044 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002045 count |= MOUSE_FOLD_OPEN;
2046 else if (mouse_char != ' ')
2047 count |= MOUSE_FOLD_CLOSE;
2048# endif
2049
2050 return count;
2051}
2052
2053/*
LemonBoyc27747e2022-05-07 12:25:40 +01002054 * Mouse scroll wheel: Default action is to scroll mouse_vert_step lines (or
2055 * mouse_hor_step, depending on the scroll direction), or one page when Shift or
2056 * Ctrl is used.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002057 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
2058 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
2059 */
2060 void
2061nv_mousescroll(cmdarg_T *cap)
2062{
2063 win_T *old_curwin = curwin, *wp;
2064
2065 if (mouse_row >= 0 && mouse_col >= 0)
2066 {
2067 int row, col;
2068
2069 row = mouse_row;
2070 col = mouse_col;
2071
2072 // find the window at the pointer coordinates
2073 wp = mouse_find_win(&row, &col, FIND_POPUP);
2074 if (wp == NULL)
2075 return;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002076#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002077 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
2078 return;
2079#endif
2080 curwin = wp;
2081 curbuf = curwin->w_buffer;
2082 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002083 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
2084 {
2085# ifdef FEAT_TERMINAL
2086 if (term_use_loop())
2087 // This window is a terminal window, send the mouse event there.
2088 // Set "typed" to FALSE to avoid an endless loop.
Bram Moolenaar1e814bc2019-11-03 21:19:41 +01002089 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002090 else
2091# endif
LemonBoyc27747e2022-05-07 12:25:40 +01002092 if (mouse_vert_step < 0 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002093 {
2094 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
2095 }
2096 else
2097 {
2098 // Don't scroll more than half the window height.
LemonBoyc27747e2022-05-07 12:25:40 +01002099 if (curwin->w_height < mouse_vert_step * 2)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002100 {
2101 cap->count1 = curwin->w_height / 2;
2102 if (cap->count1 == 0)
2103 cap->count1 = 1;
2104 }
2105 else
LemonBoyc27747e2022-05-07 12:25:40 +01002106 cap->count1 = mouse_vert_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002107 cap->count0 = cap->count1;
2108 nv_scroll_line(cap);
2109 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002110#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002111 if (WIN_IS_POPUP(curwin))
2112 popup_set_firstline(curwin);
2113#endif
2114 }
2115# ifdef FEAT_GUI
2116 else
2117 {
2118 // Horizontal scroll - only allowed when 'wrap' is disabled
2119 if (!curwin->w_p_wrap)
2120 {
LemonBoyc27747e2022-05-07 12:25:40 +01002121 int val, step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002122
LemonBoyc27747e2022-05-07 12:25:40 +01002123 if (mouse_hor_step < 0
2124 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002125 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01002126 else
2127 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002128 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
2129 if (val < 0)
2130 val = 0;
2131
2132 gui_do_horiz_scroll(val, TRUE);
2133 }
2134 }
2135# endif
2136# ifdef FEAT_SYN_HL
2137 if (curwin != old_curwin && curwin->w_p_cul)
2138 redraw_for_cursorline(curwin);
2139# endif
LemonBoy66e13ae2022-04-21 22:52:11 +01002140 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002141
2142 curwin->w_redr_status = TRUE;
2143
2144 curwin = old_curwin;
2145 curbuf = curwin->w_buffer;
2146}
2147
2148/*
2149 * Mouse clicks and drags.
2150 */
2151 void
2152nv_mouse(cmdarg_T *cap)
2153{
2154 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2155}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002156
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002157static int held_button = MOUSE_RELEASE;
2158
2159 void
2160reset_held_button()
2161{
2162 held_button = MOUSE_RELEASE;
2163}
2164
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002165/*
2166 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2167 * modifiers found in typebuf in 'modifiers'.
2168 */
2169 int
2170check_termcode_mouse(
2171 char_u *tp,
2172 int *slen,
2173 char_u *key_name,
2174 char_u *modifiers_start,
2175 int idx,
2176 int *modifiers)
2177{
2178 int j;
2179 char_u *p;
2180# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2181 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2182 char_u bytes[6];
2183 int num_bytes;
2184# endif
2185 int mouse_code = 0; // init for GCC
2186 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002187 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002188 int wheel_code = 0;
2189 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002190 static int orig_num_clicks = 1;
2191 static int orig_mouse_code = 0x0;
2192# ifdef CHECK_DOUBLE_CLICK
2193 static int orig_mouse_col = 0;
2194 static int orig_mouse_row = 0;
2195 static struct timeval orig_mouse_time = {0, 0};
2196 // time of previous mouse click
2197 struct timeval mouse_time; // time of current mouse click
2198 long timediff; // elapsed time in msec
2199# endif
2200
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002201 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002202
2203# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2204 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2205 if (key_name[0] == KS_MOUSE
2206# ifdef FEAT_MOUSE_GPM
2207 || key_name[0] == KS_GPM_MOUSE
2208# endif
2209 )
2210 {
2211 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002212 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002213 * 0x20 = left button down
2214 * 0x21 = middle button down
2215 * 0x22 = right button down
2216 * 0x23 = any button release
2217 * 0x60 = button 4 down (scroll wheel down)
2218 * 0x61 = button 5 down (scroll wheel up)
2219 * add 0x04 for SHIFT
2220 * add 0x08 for ALT
2221 * add 0x10 for CTRL
2222 * add 0x20 for mouse drag (0x40 is drag with left button)
2223 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2224 * 0x43 (drag + release) is also move
2225 * c == column + ' ' + 1 == column + 33
2226 * r == row + ' ' + 1 == row + 33
2227 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002228 * The coordinates are passed on through global variables. Ugly, but
2229 * this avoids trouble with mouse clicks at an unexpected moment and
2230 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002231 */
2232 for (;;)
2233 {
2234# ifdef FEAT_GUI
2235 if (gui.in_use)
2236 {
2237 // GUI uses more bits for columns > 223
2238 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2239 if (num_bytes == -1) // not enough coordinates
2240 return -1;
2241 mouse_code = bytes[0];
2242 mouse_col = 128 * (bytes[1] - ' ' - 1)
2243 + bytes[2] - ' ' - 1;
2244 mouse_row = 128 * (bytes[3] - ' ' - 1)
2245 + bytes[4] - ' ' - 1;
2246 }
2247 else
2248# endif
2249 {
2250 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2251 if (num_bytes == -1) // not enough coordinates
2252 return -1;
2253 mouse_code = bytes[0];
2254 mouse_col = bytes[1] - ' ' - 1;
2255 mouse_row = bytes[2] - ' ' - 1;
2256 }
2257 *slen += num_bytes;
2258
Bram Moolenaar13c04632020-07-12 13:47:42 +02002259 // If the following bytes is also a mouse code and it has the same
2260 // code, dump this one and get the next. This makes dragging a
2261 // whole lot faster.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002262# ifdef FEAT_GUI
2263 if (gui.in_use)
2264 j = 3;
2265 else
2266# endif
2267 j = get_termcode_len(idx);
2268 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2269 && tp[*slen + j] == mouse_code
2270 && tp[*slen + j + 1] != NUL
2271 && tp[*slen + j + 2] != NUL
2272# ifdef FEAT_GUI
2273 && (!gui.in_use
2274 || (tp[*slen + j + 3] != NUL
2275 && tp[*slen + j + 4] != NUL))
2276# endif
2277 )
2278 *slen += j;
2279 else
2280 break;
2281 }
2282 }
2283
2284 if (key_name[0] == KS_URXVT_MOUSE
2285 || key_name[0] == KS_SGR_MOUSE
2286 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2287 {
2288 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002289 // Almost identical to xterm mouse mode, except the values are decimal
2290 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002291 //
2292 // \033[%d;%d;%dM
2293 // ^-- row
2294 // ^----- column
2295 // ^-------- code
2296 //
2297 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002298 // Almost identical to xterm mouse mode, except the values are decimal
2299 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002300 //
2301 // \033[<%d;%d;%dM
2302 // ^-- row
2303 // ^----- column
2304 // ^-------- code
2305 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002306 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002307 // ^-- row
2308 // ^----- column
2309 // ^-------- code
2310 p = modifiers_start;
2311 if (p == NULL)
2312 return -1;
2313
2314 mouse_code = getdigits(&p);
2315 if (*p++ != ';')
2316 return -1;
2317
2318 // when mouse reporting is SGR, add 32 to mouse code
2319 if (key_name[0] == KS_SGR_MOUSE
2320 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2321 mouse_code += 32;
2322
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002323 mouse_col = getdigits(&p) - 1;
2324 if (*p++ != ';')
2325 return -1;
2326
2327 mouse_row = getdigits(&p) - 1;
2328
Bram Moolenaar13c04632020-07-12 13:47:42 +02002329 // The modifiers were the mouse coordinates, not the modifier keys
2330 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002331 *modifiers = 0;
2332 }
2333
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002334 if (key_name[0] == KS_SGR_MOUSE
2335 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2336 {
2337 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002338 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002339 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002340 // This is used below to set held_button.
2341 mouse_code |= MOUSE_RELEASE;
2342 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002343 }
2344 else
2345 {
2346 release_is_ambiguous = TRUE;
2347 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2348 is_release = TRUE;
2349 }
2350
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002351 if (key_name[0] == KS_MOUSE
2352# ifdef FEAT_MOUSE_GPM
2353 || key_name[0] == KS_GPM_MOUSE
2354# endif
2355# ifdef FEAT_MOUSE_URXVT
2356 || key_name[0] == KS_URXVT_MOUSE
2357# endif
2358 || key_name[0] == KS_SGR_MOUSE
2359 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2360 {
2361# if !defined(MSWIN)
2362 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002363 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002364 * Recognize the xterm mouse wheel, but not in the GUI, the
2365 * Linux console with GPM and the MS-DOS or Win32 console
2366 * (multi-clicks use >= 0x60).
2367 */
2368 if (mouse_code >= MOUSEWHEEL_LOW
2369# ifdef FEAT_GUI
2370 && !gui.in_use
2371# endif
2372# ifdef FEAT_MOUSE_GPM
2373 && key_name[0] != KS_GPM_MOUSE
2374# endif
2375 )
2376 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002377# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002378 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2379 // mouse-move event, using MOUSE_DRAG works
2380 mouse_code = MOUSE_DRAG;
2381 else
2382# endif
2383 // Keep the mouse_code before it's changed, so that we
2384 // remember that it was a mouse wheel click.
2385 wheel_code = mouse_code;
2386 }
2387# ifdef FEAT_MOUSE_XTERM
2388 else if (held_button == MOUSE_RELEASE
2389# ifdef FEAT_GUI
2390 && !gui.in_use
2391# endif
2392 && (mouse_code == 0x23 || mouse_code == 0x24
2393 || mouse_code == 0x40 || mouse_code == 0x41))
2394 {
2395 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2396 // And 0x40 and 0x41 are used by some xterm emulator.
2397 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002398 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002399 }
2400# endif
2401
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002402# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002403 else if (use_xterm_mouse() > 1)
2404 {
2405 if (mouse_code & MOUSE_DRAG_XTERM)
2406 mouse_code |= MOUSE_DRAG;
2407 }
2408# endif
2409# ifdef FEAT_XCLIPBOARD
2410 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2411 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002412 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002413 stop_xterm_trace();
2414 else
2415 start_xterm_trace(mouse_code);
2416 }
2417# endif
2418# endif
2419 }
2420# endif // !UNIX || FEAT_MOUSE_XTERM
2421# ifdef FEAT_MOUSE_NET
2422 if (key_name[0] == KS_NETTERM_MOUSE)
2423 {
2424 int mc, mr;
2425
2426 // expect a rather limited sequence like: balancing {
2427 // \033}6,45\r
2428 // '6' is the row, 45 is the column
2429 p = tp + *slen;
2430 mr = getdigits(&p);
2431 if (*p++ != ',')
2432 return -1;
2433 mc = getdigits(&p);
2434 if (*p++ != '\r')
2435 return -1;
2436
2437 mouse_col = mc - 1;
2438 mouse_row = mr - 1;
2439 mouse_code = MOUSE_LEFT;
2440 *slen += (int)(p - (tp + *slen));
2441 }
2442# endif // FEAT_MOUSE_NET
2443# ifdef FEAT_MOUSE_JSB
2444 if (key_name[0] == KS_JSBTERM_MOUSE)
2445 {
2446 int mult, val, iter, button, status;
2447
2448 /*
2449 * JSBTERM Input Model
2450 * \033[0~zw uniq escape sequence
2451 * (L-x) Left button pressed - not pressed x not reporting
2452 * (M-x) Middle button pressed - not pressed x not reporting
2453 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002454 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2455 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002456 * ### X cursor position padded to 3 digits
2457 * ### Y cursor position padded to 3 digits
2458 * (s-x) SHIFT key pressed - not pressed x not reporting
2459 * (c-x) CTRL key pressed - not pressed x not reporting
2460 * \033\\ terminating sequence
2461 */
2462 p = tp + *slen;
2463 button = mouse_code = 0;
2464 switch (*p++)
2465 {
2466 case 'L': button = 1; break;
2467 case '-': break;
2468 case 'x': break; // ignore sequence
2469 default: return -1; // Unknown Result
2470 }
2471 switch (*p++)
2472 {
2473 case 'M': button |= 2; break;
2474 case '-': break;
2475 case 'x': break; // ignore sequence
2476 default: return -1; // Unknown Result
2477 }
2478 switch (*p++)
2479 {
2480 case 'R': button |= 4; break;
2481 case '-': break;
2482 case 'x': break; // ignore sequence
2483 default: return -1; // Unknown Result
2484 }
2485 status = *p++;
2486 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2487 mult /= 10, p++)
2488 if (*p >= '0' && *p <= '9')
2489 val += (*p - '0') * mult;
2490 else
2491 return -1;
2492 mouse_col = val;
2493 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2494 mult /= 10, p++)
2495 if (*p >= '0' && *p <= '9')
2496 val += (*p - '0') * mult;
2497 else
2498 return -1;
2499 mouse_row = val;
2500 switch (*p++)
2501 {
2502 case 's': button |= 8; break; // SHIFT key Pressed
2503 case '-': break; // Not Pressed
2504 case 'x': break; // Not Reporting
2505 default: return -1; // Unknown Result
2506 }
2507 switch (*p++)
2508 {
2509 case 'c': button |= 16; break; // CTRL key Pressed
2510 case '-': break; // Not Pressed
2511 case 'x': break; // Not Reporting
2512 default: return -1; // Unknown Result
2513 }
2514 if (*p++ != '\033')
2515 return -1;
2516 if (*p++ != '\\')
2517 return -1;
2518 switch (status)
2519 {
2520 case 'D': // Double Click
2521 case 'S': // Single Click
2522 if (button & 1) mouse_code |= MOUSE_LEFT;
2523 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2524 if (button & 4) mouse_code |= MOUSE_RIGHT;
2525 if (button & 8) mouse_code |= MOUSE_SHIFT;
2526 if (button & 16) mouse_code |= MOUSE_CTRL;
2527 break;
2528 case 'm': // Mouse move
2529 if (button & 1) mouse_code |= MOUSE_LEFT;
2530 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2531 if (button & 4) mouse_code |= MOUSE_RIGHT;
2532 if (button & 8) mouse_code |= MOUSE_SHIFT;
2533 if (button & 16) mouse_code |= MOUSE_CTRL;
2534 if ((button & 7) != 0)
2535 {
2536 held_button = mouse_code;
2537 mouse_code |= MOUSE_DRAG;
2538 }
2539 is_drag = TRUE;
2540 showmode();
2541 break;
2542 case 'd': // Button Down
2543 if (button & 1) mouse_code |= MOUSE_LEFT;
2544 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2545 if (button & 4) mouse_code |= MOUSE_RIGHT;
2546 if (button & 8) mouse_code |= MOUSE_SHIFT;
2547 if (button & 16) mouse_code |= MOUSE_CTRL;
2548 break;
2549 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002550 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002551 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002552 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002553 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002554 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002555 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002556 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002557 if (button & 8)
2558 mouse_code |= MOUSE_SHIFT;
2559 if (button & 16)
2560 mouse_code |= MOUSE_CTRL;
2561 break;
2562 default: return -1; // Unknown Result
2563 }
2564
2565 *slen += (p - (tp + *slen));
2566 }
2567# endif // FEAT_MOUSE_JSB
2568# ifdef FEAT_MOUSE_DEC
2569 if (key_name[0] == KS_DEC_MOUSE)
2570 {
2571 /*
2572 * The DEC Locator Input Model
2573 * Netterm delivers the code sequence:
2574 * \033[2;4;24;80&w (left button down)
2575 * \033[3;0;24;80&w (left button up)
2576 * \033[6;1;24;80&w (right button down)
2577 * \033[7;0;24;80&w (right button up)
2578 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2579 * Pe is the event code
2580 * Pb is the button code
2581 * Pr is the row coordinate
2582 * Pc is the column coordinate
2583 * Pp is the third coordinate (page number)
2584 * Pe, the event code indicates what event caused this report
2585 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002586 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002587 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002588 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002589 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002590 * 2 - left button down
2591 * 3 - left button up
2592 * 4 - middle button down
2593 * 5 - middle button up
2594 * 6 - right button down
2595 * 7 - right button up
2596 * 8 - fourth button down
2597 * 9 - fourth button up
2598 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002599 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2600 * down if any. The state of the four buttons on the locator
2601 * correspond to the low four bits of the decimal value, "1" means
2602 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002603 * 0 - no buttons down,
2604 * 1 - right,
2605 * 2 - middle,
2606 * 4 - left,
2607 * 8 - fourth
2608 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002609 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2610 * position is undefined (outside the terminal window for example).
2611 * Pc is the column coordinate of the locator position in the page,
2612 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2613 * position is undefined (outside the terminal window for example).
2614 * Pp is the page coordinate of the locator position encoded as an
2615 * ASCII decimal value. The page coordinate may be omitted if the
2616 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002617 */
2618 int Pe, Pb, Pr, Pc;
2619
2620 p = tp + *slen;
2621
2622 // get event status
2623 Pe = getdigits(&p);
2624 if (*p++ != ';')
2625 return -1;
2626
2627 // get button status
2628 Pb = getdigits(&p);
2629 if (*p++ != ';')
2630 return -1;
2631
2632 // get row status
2633 Pr = getdigits(&p);
2634 if (*p++ != ';')
2635 return -1;
2636
2637 // get column status
2638 Pc = getdigits(&p);
2639
2640 // the page parameter is optional
2641 if (*p == ';')
2642 {
2643 p++;
2644 (void)getdigits(&p);
2645 }
2646 if (*p++ != '&')
2647 return -1;
2648 if (*p++ != 'w')
2649 return -1;
2650
2651 mouse_code = 0;
2652 switch (Pe)
2653 {
2654 case 0: return -1; // position request while unavailable
2655 case 1: // a response to a locator position request includes
2656 // the status of all buttons
2657 Pb &= 7; // mask off and ignore fourth button
2658 if (Pb & 4)
2659 mouse_code = MOUSE_LEFT;
2660 if (Pb & 2)
2661 mouse_code = MOUSE_MIDDLE;
2662 if (Pb & 1)
2663 mouse_code = MOUSE_RIGHT;
2664 if (Pb)
2665 {
2666 held_button = mouse_code;
2667 mouse_code |= MOUSE_DRAG;
2668 WantQueryMouse = TRUE;
2669 }
2670 is_drag = TRUE;
2671 showmode();
2672 break;
2673 case 2: mouse_code = MOUSE_LEFT;
2674 WantQueryMouse = TRUE;
2675 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002676 case 3: mouse_code = MOUSE_LEFT;
2677 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002678 break;
2679 case 4: mouse_code = MOUSE_MIDDLE;
2680 WantQueryMouse = TRUE;
2681 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002682 case 5: mouse_code = MOUSE_MIDDLE;
2683 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002684 break;
2685 case 6: mouse_code = MOUSE_RIGHT;
2686 WantQueryMouse = TRUE;
2687 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002688 case 7: mouse_code = MOUSE_RIGHT;
2689 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002690 break;
2691 case 8: return -1; // fourth button down
2692 case 9: return -1; // fourth button up
2693 case 10: return -1; // mouse outside of filter rectangle
2694 default: return -1; // should never occur
2695 }
2696
2697 mouse_col = Pc - 1;
2698 mouse_row = Pr - 1;
2699
2700 *slen += (int)(p - (tp + *slen));
2701 }
2702# endif // FEAT_MOUSE_DEC
2703# ifdef FEAT_MOUSE_PTERM
2704 if (key_name[0] == KS_PTERM_MOUSE)
2705 {
2706 int button, num_clicks, action;
2707
2708 p = tp + *slen;
2709
2710 action = getdigits(&p);
2711 if (*p++ != ';')
2712 return -1;
2713
2714 mouse_row = getdigits(&p);
2715 if (*p++ != ';')
2716 return -1;
2717 mouse_col = getdigits(&p);
2718 if (*p++ != ';')
2719 return -1;
2720
2721 button = getdigits(&p);
2722 mouse_code = 0;
2723
2724 switch (button)
2725 {
2726 case 4: mouse_code = MOUSE_LEFT; break;
2727 case 1: mouse_code = MOUSE_RIGHT; break;
2728 case 2: mouse_code = MOUSE_MIDDLE; break;
2729 default: return -1;
2730 }
2731
2732 switch (action)
2733 {
2734 case 31: // Initial press
2735 if (*p++ != ';')
2736 return -1;
2737
2738 num_clicks = getdigits(&p); // Not used
2739 break;
2740
2741 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002742 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002743 break;
2744
2745 case 33: // Drag
2746 held_button = mouse_code;
2747 mouse_code |= MOUSE_DRAG;
2748 break;
2749
2750 default:
2751 return -1;
2752 }
2753
2754 if (*p++ != 't')
2755 return -1;
2756
2757 *slen += (p - (tp + *slen));
2758 }
2759# endif // FEAT_MOUSE_PTERM
2760
2761 // Interpret the mouse code
2762 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002763 if (is_release)
2764 current_button |= MOUSE_RELEASE;
2765
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002766 if (current_button == MOUSE_RELEASE
2767# ifdef FEAT_MOUSE_XTERM
2768 && wheel_code == 0
2769# endif
2770 )
2771 {
2772 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002773 * If we get a mouse drag or release event when there is no mouse
2774 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2775 * below.
2776 * (can happen when you hold down two buttons and then let them go, or
2777 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002778 */
2779 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2780 is_drag = TRUE;
2781 current_button = held_button;
2782 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002783 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002784 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002785 if (wheel_code == 0)
2786 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002787# ifdef CHECK_DOUBLE_CLICK
2788# ifdef FEAT_MOUSE_GPM
2789 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002790 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2791 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002792 */
2793# ifdef FEAT_GUI
2794 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
2795# else
2796 if (key_name[0] != KS_GPM_MOUSE)
2797# endif
2798# else
2799# ifdef FEAT_GUI
2800 if (!gui.in_use)
2801# endif
2802# endif
2803 {
2804 /*
2805 * Compute the time elapsed since the previous mouse click.
2806 */
2807 gettimeofday(&mouse_time, NULL);
2808 if (orig_mouse_time.tv_sec == 0)
2809 {
2810 /*
2811 * Avoid computing the difference between mouse_time
2812 * and orig_mouse_time for the first click, as the
2813 * difference would be huge and would cause
2814 * multiplication overflow.
2815 */
2816 timediff = p_mouset;
2817 }
2818 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002819 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002820 orig_mouse_time = mouse_time;
2821 if (mouse_code == orig_mouse_code
2822 && timediff < p_mouset
2823 && orig_num_clicks != 4
2824 && orig_mouse_col == mouse_col
2825 && orig_mouse_row == mouse_row
2826 && (is_mouse_topline(curwin)
2827 // Double click in tab pages line also works
2828 // when window contents changes.
2829 || (mouse_row == 0 && firstwin->w_winrow > 0))
2830 )
2831 ++orig_num_clicks;
2832 else
2833 orig_num_clicks = 1;
2834 orig_mouse_col = mouse_col;
2835 orig_mouse_row = mouse_row;
2836 set_mouse_topline(curwin);
2837 }
2838# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
2839 else
2840 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2841# endif
2842# else
2843 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2844# endif
2845 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002846 }
2847 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002848 }
2849 if (!is_drag)
2850 held_button = mouse_code & MOUSE_CLICK_MASK;
2851
2852 /*
2853 * Translate the actual mouse event into a pseudo mouse event.
2854 * First work out what modifiers are to be used.
2855 */
2856 if (orig_mouse_code & MOUSE_SHIFT)
2857 *modifiers |= MOD_MASK_SHIFT;
2858 if (orig_mouse_code & MOUSE_CTRL)
2859 *modifiers |= MOD_MASK_CTRL;
2860 if (orig_mouse_code & MOUSE_ALT)
2861 *modifiers |= MOD_MASK_ALT;
2862 if (orig_num_clicks == 2)
2863 *modifiers |= MOD_MASK_2CLICK;
2864 else if (orig_num_clicks == 3)
2865 *modifiers |= MOD_MASK_3CLICK;
2866 else if (orig_num_clicks == 4)
2867 *modifiers |= MOD_MASK_4CLICK;
2868
Bram Moolenaar13c04632020-07-12 13:47:42 +02002869 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2870 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002871 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002872 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002873 {
2874 if (wheel_code & MOUSE_CTRL)
2875 *modifiers |= MOD_MASK_CTRL;
2876 if (wheel_code & MOUSE_ALT)
2877 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002878
2879 if (wheel_code & 1 && wheel_code & 2)
2880 key_name[1] = (int)KE_MOUSELEFT;
2881 else if (wheel_code & 2)
2882 key_name[1] = (int)KE_MOUSERIGHT;
2883 else if (wheel_code & 1)
2884 key_name[1] = (int)KE_MOUSEUP;
2885 else
2886 key_name[1] = (int)KE_MOUSEDOWN;
2887
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002888 held_button = MOUSE_RELEASE;
2889 }
2890 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002891 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002892
Bram Moolenaar13c04632020-07-12 13:47:42 +02002893
2894 // Make sure the mouse position is valid. Some terminals may return weird
2895 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002896 if (mouse_col >= Columns)
2897 mouse_col = Columns - 1;
2898 if (mouse_row >= Rows)
2899 mouse_row = Rows - 1;
2900
2901 return 0;
2902}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002903
2904// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002905
2906/*
2907 * Compute the buffer line position from the screen position "rowp" / "colp" in
2908 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02002909 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
2910 * caches the plines_win() result from a previous call. Entry is zero if not
2911 * computed yet. There must be no text or setting changes since the entry is
2912 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002913 * Returns TRUE if the position is below the last line.
2914 */
2915 int
2916mouse_comp_pos(
2917 win_T *win,
2918 int *rowp,
2919 int *colp,
2920 linenr_T *lnump,
2921 int *plines_cache)
2922{
2923 int col = *colp;
2924 int row = *rowp;
2925 linenr_T lnum;
2926 int retval = FALSE;
2927 int off;
2928 int count;
2929
2930#ifdef FEAT_RIGHTLEFT
2931 if (win->w_p_rl)
2932 col = win->w_width - 1 - col;
2933#endif
2934
2935 lnum = win->w_topline;
2936
2937 while (row > 0)
2938 {
2939 int cache_idx = lnum - win->w_topline;
2940
Bram Moolenaar452143c2020-07-15 17:38:21 +02002941 // Only "Rows" lines are cached, with folding we'll run out of entries
2942 // and use the slow way.
2943 if (plines_cache != NULL && cache_idx < Rows
2944 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002945 count = plines_cache[cache_idx];
2946 else
2947 {
2948#ifdef FEAT_DIFF
2949 // Don't include filler lines in "count"
2950 if (win->w_p_diff
2951# ifdef FEAT_FOLDING
2952 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
2953# endif
2954 )
2955 {
2956 if (lnum == win->w_topline)
2957 row -= win->w_topfill;
2958 else
2959 row -= diff_check_fill(win, lnum);
2960 count = plines_win_nofill(win, lnum, TRUE);
2961 }
2962 else
2963#endif
2964 count = plines_win(win, lnum, TRUE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02002965 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002966 plines_cache[cache_idx] = count;
2967 }
2968 if (count > row)
2969 break; // Position is in this buffer line.
2970#ifdef FEAT_FOLDING
2971 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
2972#endif
2973 if (lnum == win->w_buffer->b_ml.ml_line_count)
2974 {
2975 retval = TRUE;
2976 break; // past end of file
2977 }
2978 row -= count;
2979 ++lnum;
2980 }
2981
2982 if (!retval)
2983 {
2984 // Compute the column without wrapping.
2985 off = win_col_off(win) - win_col_off2(win);
2986 if (col < off)
2987 col = off;
2988 col += row * (win->w_width - off);
2989 // add skip column (for long wrapping line)
2990 col += win->w_skipcol;
2991 }
2992
2993 if (!win->w_p_wrap)
2994 col += win->w_leftcol;
2995
2996 // skip line number and fold column in front of the line
2997 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01002998 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002999 {
3000#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003001 // if mouse is clicked on the gutter, then inform the netbeans server
3002 if (*colp < win_col_off(win))
3003 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003004#endif
3005 col = 0;
3006 }
3007
3008 *colp = col;
3009 *rowp = row;
3010 *lnump = lnum;
3011 return retval;
3012}
3013
3014/*
3015 * Find the window at screen position "*rowp" and "*colp". The positions are
3016 * updated to become relative to the top-left of the window.
3017 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3018 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3019 * windows.
3020 * Returns NULL when something is wrong.
3021 */
3022 win_T *
3023mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3024{
3025 frame_T *fp;
3026 win_T *wp;
3027
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003028#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003029 win_T *pwp = NULL;
3030
3031 if (popup != IGNORE_POPUP)
3032 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003033 popup_reset_handled(POPUP_HANDLED_1);
3034 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003035 {
3036 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3037 && *colp >= wp->w_wincol
3038 && *colp < wp->w_wincol + popup_width(wp))
3039 pwp = wp;
3040 }
3041 if (pwp != NULL)
3042 {
3043 if (popup == FAIL_POPUP)
3044 return NULL;
3045 *rowp -= pwp->w_winrow;
3046 *colp -= pwp->w_wincol;
3047 return pwp;
3048 }
3049 }
3050#endif
3051
3052 fp = topframe;
3053 *rowp -= firstwin->w_winrow;
3054 for (;;)
3055 {
3056 if (fp->fr_layout == FR_LEAF)
3057 break;
3058 if (fp->fr_layout == FR_ROW)
3059 {
3060 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3061 {
3062 if (*colp < fp->fr_width)
3063 break;
3064 *colp -= fp->fr_width;
3065 }
3066 }
3067 else // fr_layout == FR_COL
3068 {
3069 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3070 {
3071 if (*rowp < fp->fr_height)
3072 break;
3073 *rowp -= fp->fr_height;
3074 }
3075 }
3076 }
3077 // When using a timer that closes a window the window might not actually
3078 // exist.
3079 FOR_ALL_WINDOWS(wp)
3080 if (wp == fp->fr_win)
3081 {
3082#ifdef FEAT_MENU
3083 *rowp -= wp->w_winbar_height;
3084#endif
3085 return wp;
3086 }
3087 return NULL;
3088}
3089
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003090#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003091 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003092/*
3093 * Convert a virtual (screen) column to a character column.
3094 * The first column is one.
3095 */
3096 int
3097vcol2col(win_T *wp, linenr_T lnum, int vcol)
3098{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003099 char_u *line;
3100 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003101
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003102 // try to advance to the specified column
3103 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3104 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3105 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003106 {
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003107 cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
3108 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003109 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003110 clear_chartabsize_arg(&cts);
3111
3112 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003113}
3114#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003115
3116#if defined(FEAT_EVAL) || defined(PROTO)
3117 void
3118f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3119{
3120 dict_T *d;
3121 win_T *wp;
3122 int row = mouse_row;
3123 int col = mouse_col;
3124 varnumber_T winid = 0;
3125 varnumber_T winrow = 0;
3126 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003127 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003128 varnumber_T column = 0;
3129
Bram Moolenaar93a10962022-06-16 11:42:09 +01003130 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003131 return;
3132 d = rettv->vval.v_dict;
3133
3134 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3135 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3136
3137 wp = mouse_find_win(&row, &col, FIND_POPUP);
3138 if (wp != NULL)
3139 {
3140 int top_off = 0;
3141 int left_off = 0;
3142 int height = wp->w_height + wp->w_status_height;
3143
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003144#ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003145 if (WIN_IS_POPUP(wp))
3146 {
3147 top_off = popup_top_extra(wp);
3148 left_off = popup_left_extra(wp);
3149 height = popup_height(wp);
3150 }
3151#endif
3152 if (row < height)
3153 {
3154 winid = wp->w_id;
3155 winrow = row + 1;
3156 wincol = col + 1;
3157 row -= top_off;
3158 col -= left_off;
3159 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3160 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003161 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
3162 col = vcol2col(wp, lnum, col);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003163 column = col + 1;
3164 }
3165 }
3166 }
3167 dict_add_number(d, "winid", winid);
3168 dict_add_number(d, "winrow", winrow);
3169 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003170 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003171 dict_add_number(d, "column", column);
3172}
3173#endif