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