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