comparison src/mouse.c @ 18135:1868ec23360e v8.1.2062

patch 8.1.2062: the mouse code is spread out Commit: https://github.com/vim/vim/commit/b20b9e14ddd8db111e886ad0494e15b955159426 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 21 20:48:04 2019 +0200 patch 8.1.2062: the mouse code is spread out Problem: The mouse code is spread out. Solution: Move all the mouse code to mouse.c. (Yegappan Lakshmanan, closes #4959)
author Bram Moolenaar <Bram@vim.org>
date Sat, 21 Sep 2019 21:00:07 +0200
parents
children 2cc67e54edf8
comparison
equal deleted inserted replaced
18134:c06a2bc8144f 18135:1868ec23360e
1 /* 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
16 #if defined(FEAT_MOUSE) || defined(PROTO)
17
18 static int get_fpos_of_mouse(pos_T *mpos);
19
20 /*
21 * Get class of a character for selection: same class means same word.
22 * 0: blank
23 * 1: punctuation groups
24 * 2: normal word character
25 * >2: multi-byte word character.
26 */
27 static int
28 get_mouse_class(char_u *p)
29 {
30 int c;
31
32 if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
33 return mb_get_class(p);
34
35 c = *p;
36 if (c == ' ' || c == '\t')
37 return 0;
38
39 if (vim_iswordc(c))
40 return 2;
41
42 // There are a few special cases where we want certain combinations of
43 // characters to be considered as a single word. These are things like
44 // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
45 // character is in its own class.
46 if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
47 return 1;
48 return c;
49 }
50
51 /*
52 * Move "pos" back to the start of the word it's in.
53 */
54 static void
55 find_start_of_word(pos_T *pos)
56 {
57 char_u *line;
58 int cclass;
59 int col;
60
61 line = ml_get(pos->lnum);
62 cclass = get_mouse_class(line + pos->col);
63
64 while (pos->col > 0)
65 {
66 col = pos->col - 1;
67 col -= (*mb_head_off)(line, line + col);
68 if (get_mouse_class(line + col) != cclass)
69 break;
70 pos->col = col;
71 }
72 }
73
74 /*
75 * Move "pos" forward to the end of the word it's in.
76 * When 'selection' is "exclusive", the position is just after the word.
77 */
78 static void
79 find_end_of_word(pos_T *pos)
80 {
81 char_u *line;
82 int cclass;
83 int col;
84
85 line = ml_get(pos->lnum);
86 if (*p_sel == 'e' && pos->col > 0)
87 {
88 --pos->col;
89 pos->col -= (*mb_head_off)(line, line + pos->col);
90 }
91 cclass = get_mouse_class(line + pos->col);
92 while (line[pos->col] != NUL)
93 {
94 col = pos->col + (*mb_ptr2len)(line + pos->col);
95 if (get_mouse_class(line + col) != cclass)
96 {
97 if (*p_sel == 'e')
98 pos->col = col;
99 break;
100 }
101 pos->col = col;
102 }
103 }
104
105 /*
106 * Do the appropriate action for the current mouse click in the current mode.
107 * Not used for Command-line mode.
108 *
109 * Normal and Visual Mode:
110 * event modi- position visual change action
111 * fier cursor window
112 * left press - yes end yes
113 * left press C yes end yes "^]" (2)
114 * left press S yes end (popup: extend) yes "*" (2)
115 * left drag - yes start if moved no
116 * left relse - yes start if moved no
117 * middle press - yes if not active no put register
118 * middle press - yes if active no yank and put
119 * right press - yes start or extend yes
120 * right press S yes no change yes "#" (2)
121 * right drag - yes extend no
122 * right relse - yes extend no
123 *
124 * Insert or Replace Mode:
125 * event modi- position visual change action
126 * fier cursor window
127 * left press - yes (cannot be active) yes
128 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
129 * left press S yes (cannot be active) yes "CTRL-O*" (2)
130 * left drag - yes start or extend (1) no CTRL-O (1)
131 * left relse - yes start or extend (1) no CTRL-O (1)
132 * middle press - no (cannot be active) no put register
133 * right press - yes start or extend yes CTRL-O
134 * right press S yes (cannot be active) yes "CTRL-O#" (2)
135 *
136 * (1) only if mouse pointer moved since press
137 * (2) only if click is in same buffer
138 *
139 * Return TRUE if start_arrow() should be called for edit mode.
140 */
141 int
142 do_mouse(
143 oparg_T *oap, // operator argument, can be NULL
144 int c, // K_LEFTMOUSE, etc
145 int dir, // Direction to 'put' if necessary
146 long count,
147 int fixindent) // PUT_FIXINDENT if fixing indent necessary
148 {
149 static int do_always = FALSE; // ignore 'mouse' setting next time
150 static int got_click = FALSE; // got a click some time back
151
152 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
153 int is_click = FALSE; // If FALSE it's a drag or release event
154 int is_drag = FALSE; // If TRUE it's a drag event
155 int jump_flags = 0; // flags for jump_to_mouse()
156 pos_T start_visual;
157 int moved; // Has cursor moved?
158 int in_status_line; // mouse in status line
159 static int in_tab_line = FALSE; // mouse clicked in tab line
160 int in_sep_line; // mouse in vertical separator line
161 int c1, c2;
162 #if defined(FEAT_FOLDING)
163 pos_T save_cursor;
164 #endif
165 win_T *old_curwin = curwin;
166 static pos_T orig_cursor;
167 colnr_T leftcol, rightcol;
168 pos_T end_visual;
169 int diff;
170 int old_active = VIsual_active;
171 int old_mode = VIsual_mode;
172 int regname;
173
174 #if defined(FEAT_FOLDING)
175 save_cursor = curwin->w_cursor;
176 #endif
177
178 // When GUI is active, always recognize mouse events, otherwise:
179 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
180 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
181 // - For command line and insert mode 'mouse' is checked before calling
182 // do_mouse().
183 if (do_always)
184 do_always = FALSE;
185 else
186 #ifdef FEAT_GUI
187 if (!gui.in_use)
188 #endif
189 {
190 if (VIsual_active)
191 {
192 if (!mouse_has(MOUSE_VISUAL))
193 return FALSE;
194 }
195 else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
196 return FALSE;
197 }
198
199 for (;;)
200 {
201 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
202 if (is_drag)
203 {
204 // If the next character is the same mouse event then use that
205 // one. Speeds up dragging the status line.
206 if (vpeekc() != NUL)
207 {
208 int nc;
209 int save_mouse_row = mouse_row;
210 int save_mouse_col = mouse_col;
211
212 // Need to get the character, peeking doesn't get the actual
213 // one.
214 nc = safe_vgetc();
215 if (c == nc)
216 continue;
217 vungetc(nc);
218 mouse_row = save_mouse_row;
219 mouse_col = save_mouse_col;
220 }
221 }
222 break;
223 }
224
225 if (c == K_MOUSEMOVE)
226 {
227 // Mouse moved without a button pressed.
228 #ifdef FEAT_BEVAL_TERM
229 ui_may_remove_balloon();
230 if (p_bevalterm)
231 {
232 profile_setlimit(p_bdlay, &bevalexpr_due);
233 bevalexpr_due_set = TRUE;
234 }
235 #endif
236 #ifdef FEAT_TEXT_PROP
237 popup_handle_mouse_moved();
238 #endif
239 return FALSE;
240 }
241
242 #ifdef FEAT_MOUSESHAPE
243 // May have stopped dragging the status or separator line. The pointer is
244 // most likely still on the status or separator line.
245 if (!is_drag && drag_status_line)
246 {
247 drag_status_line = FALSE;
248 update_mouseshape(SHAPE_IDX_STATUS);
249 }
250 if (!is_drag && drag_sep_line)
251 {
252 drag_sep_line = FALSE;
253 update_mouseshape(SHAPE_IDX_VSEP);
254 }
255 #endif
256
257 // Ignore drag and release events if we didn't get a click.
258 if (is_click)
259 got_click = TRUE;
260 else
261 {
262 if (!got_click) // didn't get click, ignore
263 return FALSE;
264 if (!is_drag) // release, reset got_click
265 {
266 got_click = FALSE;
267 if (in_tab_line)
268 {
269 in_tab_line = FALSE;
270 return FALSE;
271 }
272 }
273 }
274
275 // CTRL right mouse button does CTRL-T
276 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
277 {
278 if (State & INSERT)
279 stuffcharReadbuff(Ctrl_O);
280 if (count > 1)
281 stuffnumReadbuff(count);
282 stuffcharReadbuff(Ctrl_T);
283 got_click = FALSE; // ignore drag&release now
284 return FALSE;
285 }
286
287 // CTRL only works with left mouse button
288 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
289 return FALSE;
290
291 // When a modifier is down, ignore drag and release events, as well as
292 // multiple clicks and the middle mouse button.
293 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
294 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
295 | MOD_MASK_META))
296 && (!is_click
297 || (mod_mask & MOD_MASK_MULTI_CLICK)
298 || which_button == MOUSE_MIDDLE)
299 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
300 && mouse_model_popup()
301 && which_button == MOUSE_LEFT)
302 && !((mod_mask & MOD_MASK_ALT)
303 && !mouse_model_popup()
304 && which_button == MOUSE_RIGHT)
305 )
306 return FALSE;
307
308 // If the button press was used as the movement command for an operator
309 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
310 // drag/release events.
311 if (!is_click && which_button == MOUSE_MIDDLE)
312 return FALSE;
313
314 if (oap != NULL)
315 regname = oap->regname;
316 else
317 regname = 0;
318
319 // Middle mouse button does a 'put' of the selected text
320 if (which_button == MOUSE_MIDDLE)
321 {
322 if (State == NORMAL)
323 {
324 // If an operator was pending, we don't know what the user wanted
325 // to do. Go back to normal mode: Clear the operator and beep().
326 if (oap != NULL && oap->op_type != OP_NOP)
327 {
328 clearopbeep(oap);
329 return FALSE;
330 }
331
332 // If visual was active, yank the highlighted text and put it
333 // before the mouse pointer position.
334 // In Select mode replace the highlighted text with the clipboard.
335 if (VIsual_active)
336 {
337 if (VIsual_select)
338 {
339 stuffcharReadbuff(Ctrl_G);
340 stuffReadbuff((char_u *)"\"+p");
341 }
342 else
343 {
344 stuffcharReadbuff('y');
345 stuffcharReadbuff(K_MIDDLEMOUSE);
346 }
347 do_always = TRUE; // ignore 'mouse' setting next time
348 return FALSE;
349 }
350 // The rest is below jump_to_mouse()
351 }
352
353 else if ((State & INSERT) == 0)
354 return FALSE;
355
356 // Middle click in insert mode doesn't move the mouse, just insert the
357 // contents of a register. '.' register is special, can't insert that
358 // with do_put().
359 // Also paste at the cursor if the current mode isn't in 'mouse' (only
360 // happens for the GUI).
361 if ((State & INSERT) || !mouse_has(MOUSE_NORMAL))
362 {
363 if (regname == '.')
364 insert_reg(regname, TRUE);
365 else
366 {
367 #ifdef FEAT_CLIPBOARD
368 if (clip_star.available && regname == 0)
369 regname = '*';
370 #endif
371 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
372 insert_reg(regname, TRUE);
373 else
374 {
375 do_put(regname, BACKWARD, 1L, fixindent | PUT_CURSEND);
376
377 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
378 AppendCharToRedobuff(Ctrl_R);
379 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
380 AppendCharToRedobuff(regname == 0 ? '"' : regname);
381 }
382 }
383 return FALSE;
384 }
385 }
386
387 // When dragging or button-up stay in the same window.
388 if (!is_click)
389 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
390
391 start_visual.lnum = 0;
392
393 // Check for clicking in the tab page line.
394 if (mouse_row == 0 && firstwin->w_winrow > 0)
395 {
396 if (is_drag)
397 {
398 if (in_tab_line)
399 {
400 c1 = TabPageIdxs[mouse_col];
401 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
402 ? c1 - 1 : c1);
403 }
404 return FALSE;
405 }
406
407 // click in a tab selects that tab page
408 if (is_click
409 # ifdef FEAT_CMDWIN
410 && cmdwin_type == 0
411 # endif
412 && mouse_col < Columns)
413 {
414 in_tab_line = TRUE;
415 c1 = TabPageIdxs[mouse_col];
416 if (c1 >= 0)
417 {
418 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
419 {
420 // double click opens new page
421 end_visual_mode();
422 tabpage_new();
423 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
424 }
425 else
426 {
427 // Go to specified tab page, or next one if not clicking
428 // on a label.
429 goto_tabpage(c1);
430
431 // It's like clicking on the status line of a window.
432 if (curwin != old_curwin)
433 end_visual_mode();
434 }
435 }
436 else
437 {
438 tabpage_T *tp;
439
440 // Close the current or specified tab page.
441 if (c1 == -999)
442 tp = curtab;
443 else
444 tp = find_tabpage(-c1);
445 if (tp == curtab)
446 {
447 if (first_tabpage->tp_next != NULL)
448 tabpage_close(FALSE);
449 }
450 else if (tp != NULL)
451 tabpage_close_other(tp, FALSE);
452 }
453 }
454 return TRUE;
455 }
456 else if (is_drag && in_tab_line)
457 {
458 c1 = TabPageIdxs[mouse_col];
459 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
460 return FALSE;
461 }
462
463 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
464 // right button up -> pop-up menu
465 // shift-left button -> right button
466 // alt-left button -> alt-right button
467 if (mouse_model_popup())
468 {
469 if (which_button == MOUSE_RIGHT
470 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
471 {
472 #if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
473 || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
474 || defined(FEAT_GUI_MAC) || defined(FEAT_GUI_PHOTON) \
475 || defined(FEAT_TERM_POPUP_MENU)
476 # ifdef FEAT_GUI
477 if (gui.in_use)
478 {
479 # if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
480 || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC)
481 if (!is_click)
482 // Ignore right button release events, only shows the popup
483 // menu on the button down event.
484 return FALSE;
485 # endif
486 # if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN)
487 if (is_click || is_drag)
488 // Ignore right button down and drag mouse events. Windows
489 // only shows the popup menu on the button up event.
490 return FALSE;
491 # endif
492 }
493 # endif
494 # if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
495 else
496 # endif
497 # if defined(FEAT_TERM_POPUP_MENU)
498 if (!is_click)
499 // Ignore right button release events, only shows the popup
500 // menu on the button down event.
501 return FALSE;
502 #endif
503
504 jump_flags = 0;
505 if (STRCMP(p_mousem, "popup_setpos") == 0)
506 {
507 // First set the cursor position before showing the popup
508 // menu.
509 if (VIsual_active)
510 {
511 pos_T m_pos;
512
513 // set MOUSE_MAY_STOP_VIS if we are outside the
514 // selection or the current window (might have false
515 // negative here)
516 if (mouse_row < curwin->w_winrow
517 || mouse_row
518 > (curwin->w_winrow + curwin->w_height))
519 jump_flags = MOUSE_MAY_STOP_VIS;
520 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
521 jump_flags = MOUSE_MAY_STOP_VIS;
522 else
523 {
524 if ((LT_POS(curwin->w_cursor, VIsual)
525 && (LT_POS(m_pos, curwin->w_cursor)
526 || LT_POS(VIsual, m_pos)))
527 || (LT_POS(VIsual, curwin->w_cursor)
528 && (LT_POS(m_pos, VIsual)
529 || LT_POS(curwin->w_cursor, m_pos))))
530 {
531 jump_flags = MOUSE_MAY_STOP_VIS;
532 }
533 else if (VIsual_mode == Ctrl_V)
534 {
535 getvcols(curwin, &curwin->w_cursor, &VIsual,
536 &leftcol, &rightcol);
537 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
538 if (m_pos.col < leftcol || m_pos.col > rightcol)
539 jump_flags = MOUSE_MAY_STOP_VIS;
540 }
541 }
542 }
543 else
544 jump_flags = MOUSE_MAY_STOP_VIS;
545 }
546 if (jump_flags)
547 {
548 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
549 update_curbuf(VIsual_active ? INVERTED : VALID);
550 setcursor();
551 out_flush(); // Update before showing popup menu
552 }
553 # ifdef FEAT_MENU
554 show_popupmenu();
555 got_click = FALSE; // ignore release events
556 # endif
557 return (jump_flags & CURSOR_MOVED) != 0;
558 #else
559 return FALSE;
560 #endif
561 }
562 if (which_button == MOUSE_LEFT
563 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
564 {
565 which_button = MOUSE_RIGHT;
566 mod_mask &= ~MOD_MASK_SHIFT;
567 }
568 }
569
570 if ((State & (NORMAL | INSERT))
571 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
572 {
573 if (which_button == MOUSE_LEFT)
574 {
575 if (is_click)
576 {
577 // stop Visual mode for a left click in a window, but not when
578 // on a status line
579 if (VIsual_active)
580 jump_flags |= MOUSE_MAY_STOP_VIS;
581 }
582 else if (mouse_has(MOUSE_VISUAL))
583 jump_flags |= MOUSE_MAY_VIS;
584 }
585 else if (which_button == MOUSE_RIGHT)
586 {
587 if (is_click && VIsual_active)
588 {
589 // Remember the start and end of visual before moving the
590 // cursor.
591 if (LT_POS(curwin->w_cursor, VIsual))
592 {
593 start_visual = curwin->w_cursor;
594 end_visual = VIsual;
595 }
596 else
597 {
598 start_visual = VIsual;
599 end_visual = curwin->w_cursor;
600 }
601 }
602 jump_flags |= MOUSE_FOCUS;
603 if (mouse_has(MOUSE_VISUAL))
604 jump_flags |= MOUSE_MAY_VIS;
605 }
606 }
607
608 // If an operator is pending, ignore all drags and releases until the
609 // next mouse click.
610 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
611 {
612 got_click = FALSE;
613 oap->motion_type = MCHAR;
614 }
615
616 // When releasing the button let jump_to_mouse() know.
617 if (!is_click && !is_drag)
618 jump_flags |= MOUSE_RELEASED;
619
620 // JUMP!
621 jump_flags = jump_to_mouse(jump_flags,
622 oap == NULL ? NULL : &(oap->inclusive), which_button);
623
624 #ifdef FEAT_MENU
625 // A click in the window toolbar has no side effects.
626 if (jump_flags & MOUSE_WINBAR)
627 return FALSE;
628 #endif
629 moved = (jump_flags & CURSOR_MOVED);
630 in_status_line = (jump_flags & IN_STATUS_LINE);
631 in_sep_line = (jump_flags & IN_SEP_LINE);
632
633 #ifdef FEAT_NETBEANS_INTG
634 if (isNetbeansBuffer(curbuf)
635 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
636 {
637 int key = KEY2TERMCAP1(c);
638
639 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
640 || key == (int)KE_RIGHTRELEASE)
641 netbeans_button_release(which_button);
642 }
643 #endif
644
645 // When jumping to another window, clear a pending operator. That's a bit
646 // friendlier than beeping and not jumping to that window.
647 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
648 clearop(oap);
649
650 #ifdef FEAT_FOLDING
651 if (mod_mask == 0
652 && !is_drag
653 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
654 && which_button == MOUSE_LEFT)
655 {
656 // open or close a fold at this line
657 if (jump_flags & MOUSE_FOLD_OPEN)
658 openFold(curwin->w_cursor.lnum, 1L);
659 else
660 closeFold(curwin->w_cursor.lnum, 1L);
661 // don't move the cursor if still in the same window
662 if (curwin == old_curwin)
663 curwin->w_cursor = save_cursor;
664 }
665 #endif
666
667 #if defined(FEAT_CLIPBOARD) && defined(FEAT_CMDWIN)
668 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
669 {
670 clip_modeless(which_button, is_click, is_drag);
671 return FALSE;
672 }
673 #endif
674
675 // Set global flag that we are extending the Visual area with mouse
676 // dragging; temporarily minimize 'scrolloff'.
677 if (VIsual_active && is_drag && get_scrolloff_value())
678 {
679 // In the very first line, allow scrolling one line
680 if (mouse_row == 0)
681 mouse_dragging = 2;
682 else
683 mouse_dragging = 1;
684 }
685
686 // When dragging the mouse above the window, scroll down.
687 if (is_drag && mouse_row < 0 && !in_status_line)
688 {
689 scroll_redraw(FALSE, 1L);
690 mouse_row = 0;
691 }
692
693 if (start_visual.lnum) // right click in visual mode
694 {
695 // When ALT is pressed make Visual mode blockwise.
696 if (mod_mask & MOD_MASK_ALT)
697 VIsual_mode = Ctrl_V;
698
699 // In Visual-block mode, divide the area in four, pick up the corner
700 // that is in the quarter that the cursor is in.
701 if (VIsual_mode == Ctrl_V)
702 {
703 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
704 if (curwin->w_curswant > (leftcol + rightcol) / 2)
705 end_visual.col = leftcol;
706 else
707 end_visual.col = rightcol;
708 if (curwin->w_cursor.lnum >=
709 (start_visual.lnum + end_visual.lnum) / 2)
710 end_visual.lnum = start_visual.lnum;
711
712 // move VIsual to the right column
713 start_visual = curwin->w_cursor; // save the cursor pos
714 curwin->w_cursor = end_visual;
715 coladvance(end_visual.col);
716 VIsual = curwin->w_cursor;
717 curwin->w_cursor = start_visual; // restore the cursor
718 }
719 else
720 {
721 // If the click is before the start of visual, change the start.
722 // If the click is after the end of visual, change the end. If
723 // the click is inside the visual, change the closest side.
724 if (LT_POS(curwin->w_cursor, start_visual))
725 VIsual = end_visual;
726 else if (LT_POS(end_visual, curwin->w_cursor))
727 VIsual = start_visual;
728 else
729 {
730 // In the same line, compare column number
731 if (end_visual.lnum == start_visual.lnum)
732 {
733 if (curwin->w_cursor.col - start_visual.col >
734 end_visual.col - curwin->w_cursor.col)
735 VIsual = start_visual;
736 else
737 VIsual = end_visual;
738 }
739
740 // In different lines, compare line number
741 else
742 {
743 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
744 (end_visual.lnum - curwin->w_cursor.lnum);
745
746 if (diff > 0) // closest to end
747 VIsual = start_visual;
748 else if (diff < 0) // closest to start
749 VIsual = end_visual;
750 else // in the middle line
751 {
752 if (curwin->w_cursor.col <
753 (start_visual.col + end_visual.col) / 2)
754 VIsual = end_visual;
755 else
756 VIsual = start_visual;
757 }
758 }
759 }
760 }
761 }
762 // If Visual mode started in insert mode, execute "CTRL-O"
763 else if ((State & INSERT) && VIsual_active)
764 stuffcharReadbuff(Ctrl_O);
765
766 // Middle mouse click: Put text before cursor.
767 if (which_button == MOUSE_MIDDLE)
768 {
769 #ifdef FEAT_CLIPBOARD
770 if (clip_star.available && regname == 0)
771 regname = '*';
772 #endif
773 if (yank_register_mline(regname))
774 {
775 if (mouse_past_bottom)
776 dir = FORWARD;
777 }
778 else if (mouse_past_eol)
779 dir = FORWARD;
780
781 if (fixindent)
782 {
783 c1 = (dir == BACKWARD) ? '[' : ']';
784 c2 = 'p';
785 }
786 else
787 {
788 c1 = (dir == FORWARD) ? 'p' : 'P';
789 c2 = NUL;
790 }
791 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
792
793 // Remember where the paste started, so in edit() Insstart can be set
794 // to this position
795 if (restart_edit != 0)
796 where_paste_started = curwin->w_cursor;
797 do_put(regname, dir, count, fixindent | PUT_CURSEND);
798 }
799
800 #if defined(FEAT_QUICKFIX)
801 // Ctrl-Mouse click or double click in a quickfix window jumps to the
802 // error under the mouse pointer.
803 else if (((mod_mask & MOD_MASK_CTRL)
804 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
805 && bt_quickfix(curbuf))
806 {
807 if (curwin->w_llist_ref == NULL) // quickfix window
808 do_cmdline_cmd((char_u *)".cc");
809 else // location list window
810 do_cmdline_cmd((char_u *)".ll");
811 got_click = FALSE; // ignore drag&release now
812 }
813 #endif
814
815 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
816 // under the mouse pointer.
817 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
818 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
819 {
820 if (State & INSERT)
821 stuffcharReadbuff(Ctrl_O);
822 stuffcharReadbuff(Ctrl_RSB);
823 got_click = FALSE; // ignore drag&release now
824 }
825
826 // Shift-Mouse click searches for the next occurrence of the word under
827 // the mouse pointer
828 else if ((mod_mask & MOD_MASK_SHIFT))
829 {
830 if ((State & INSERT) || (VIsual_active && VIsual_select))
831 stuffcharReadbuff(Ctrl_O);
832 if (which_button == MOUSE_LEFT)
833 stuffcharReadbuff('*');
834 else // MOUSE_RIGHT
835 stuffcharReadbuff('#');
836 }
837
838 // Handle double clicks, unless on status line
839 else if (in_status_line)
840 {
841 #ifdef FEAT_MOUSESHAPE
842 if ((is_drag || is_click) && !drag_status_line)
843 {
844 drag_status_line = TRUE;
845 update_mouseshape(-1);
846 }
847 #endif
848 }
849 else if (in_sep_line)
850 {
851 #ifdef FEAT_MOUSESHAPE
852 if ((is_drag || is_click) && !drag_sep_line)
853 {
854 drag_sep_line = TRUE;
855 update_mouseshape(-1);
856 }
857 #endif
858 }
859 else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))
860 && mouse_has(MOUSE_VISUAL))
861 {
862 if (is_click || !VIsual_active)
863 {
864 if (VIsual_active)
865 orig_cursor = VIsual;
866 else
867 {
868 check_visual_highlight();
869 VIsual = curwin->w_cursor;
870 orig_cursor = VIsual;
871 VIsual_active = TRUE;
872 VIsual_reselect = TRUE;
873 // start Select mode if 'selectmode' contains "mouse"
874 may_start_select('o');
875 setmouse();
876 }
877 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
878 {
879 // Double click with ALT pressed makes it blockwise.
880 if (mod_mask & MOD_MASK_ALT)
881 VIsual_mode = Ctrl_V;
882 else
883 VIsual_mode = 'v';
884 }
885 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
886 VIsual_mode = 'V';
887 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
888 VIsual_mode = Ctrl_V;
889 #ifdef FEAT_CLIPBOARD
890 // Make sure the clipboard gets updated. Needed because start and
891 // end may still be the same, and the selection needs to be owned
892 clip_star.vmode = NUL;
893 #endif
894 }
895 // A double click selects a word or a block.
896 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
897 {
898 pos_T *pos = NULL;
899 int gc;
900
901 if (is_click)
902 {
903 // If the character under the cursor (skipping white space) is
904 // not a word character, try finding a match and select a (),
905 // {}, [], #if/#endif, etc. block.
906 end_visual = curwin->w_cursor;
907 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
908 inc(&end_visual);
909 if (oap != NULL)
910 oap->motion_type = MCHAR;
911 if (oap != NULL
912 && VIsual_mode == 'v'
913 && !vim_iswordc(gchar_pos(&end_visual))
914 && EQUAL_POS(curwin->w_cursor, VIsual)
915 && (pos = findmatch(oap, NUL)) != NULL)
916 {
917 curwin->w_cursor = *pos;
918 if (oap->motion_type == MLINE)
919 VIsual_mode = 'V';
920 else if (*p_sel == 'e')
921 {
922 if (LT_POS(curwin->w_cursor, VIsual))
923 ++VIsual.col;
924 else
925 ++curwin->w_cursor.col;
926 }
927 }
928 }
929
930 if (pos == NULL && (is_click || is_drag))
931 {
932 // When not found a match or when dragging: extend to include
933 // a word.
934 if (LT_POS(curwin->w_cursor, orig_cursor))
935 {
936 find_start_of_word(&curwin->w_cursor);
937 find_end_of_word(&VIsual);
938 }
939 else
940 {
941 find_start_of_word(&VIsual);
942 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
943 curwin->w_cursor.col +=
944 (*mb_ptr2len)(ml_get_cursor());
945 find_end_of_word(&curwin->w_cursor);
946 }
947 }
948 curwin->w_set_curswant = TRUE;
949 }
950 if (is_click)
951 redraw_curbuf_later(INVERTED); // update the inversion
952 }
953 else if (VIsual_active && !old_active)
954 {
955 if (mod_mask & MOD_MASK_ALT)
956 VIsual_mode = Ctrl_V;
957 else
958 VIsual_mode = 'v';
959 }
960
961 // If Visual mode changed show it later.
962 if ((!VIsual_active && old_active && mode_displayed)
963 || (VIsual_active && p_smd && msg_silent == 0
964 && (!old_active || VIsual_mode != old_mode)))
965 redraw_cmdline = TRUE;
966
967 return moved;
968 }
969
970 void
971 ins_mouse(int c)
972 {
973 pos_T tpos;
974 win_T *old_curwin = curwin;
975
976 # ifdef FEAT_GUI
977 // When GUI is active, also move/paste when 'mouse' is empty
978 if (!gui.in_use)
979 # endif
980 if (!mouse_has(MOUSE_INSERT))
981 return;
982
983 undisplay_dollar();
984 tpos = curwin->w_cursor;
985 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
986 {
987 win_T *new_curwin = curwin;
988
989 if (curwin != old_curwin && win_valid(old_curwin))
990 {
991 // Mouse took us to another window. We need to go back to the
992 // previous one to stop insert there properly.
993 curwin = old_curwin;
994 curbuf = curwin->w_buffer;
995 #ifdef FEAT_JOB_CHANNEL
996 if (bt_prompt(curbuf))
997 // Restart Insert mode when re-entering the prompt buffer.
998 curbuf->b_prompt_insert = 'A';
999 #endif
1000 }
1001 start_arrow(curwin == old_curwin ? &tpos : NULL);
1002 if (curwin != new_curwin && win_valid(new_curwin))
1003 {
1004 curwin = new_curwin;
1005 curbuf = curwin->w_buffer;
1006 }
1007 # ifdef FEAT_CINDENT
1008 set_can_cindent(TRUE);
1009 # endif
1010 }
1011
1012 // redraw status lines (in case another window became active)
1013 redraw_statuslines();
1014 }
1015
1016 void
1017 ins_mousescroll(int dir)
1018 {
1019 pos_T tpos;
1020 win_T *old_curwin = curwin, *wp;
1021 int did_scroll = FALSE;
1022
1023 tpos = curwin->w_cursor;
1024
1025 if (mouse_row >= 0 && mouse_col >= 0)
1026 {
1027 int row, col;
1028
1029 row = mouse_row;
1030 col = mouse_col;
1031
1032 // find the window at the pointer coordinates
1033 wp = mouse_find_win(&row, &col, FIND_POPUP);
1034 if (wp == NULL)
1035 return;
1036 curwin = wp;
1037 curbuf = curwin->w_buffer;
1038 }
1039 if (curwin == old_curwin)
1040 undisplay_dollar();
1041
1042 // Don't scroll the window in which completion is being done.
1043 if (!pum_visible() || curwin != old_curwin)
1044 {
1045 if (dir == MSCR_DOWN || dir == MSCR_UP)
1046 {
1047 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1048 scroll_redraw(dir,
1049 (long)(curwin->w_botline - curwin->w_topline));
1050 else
1051 scroll_redraw(dir, 3L);
1052 # ifdef FEAT_TEXT_PROP
1053 if (WIN_IS_POPUP(curwin))
1054 popup_set_firstline(curwin);
1055 # endif
1056 }
1057 #ifdef FEAT_GUI
1058 else
1059 {
1060 int val, step = 6;
1061
1062 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1063 step = curwin->w_width;
1064 val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
1065 if (val < 0)
1066 val = 0;
1067 gui_do_horiz_scroll(val, TRUE);
1068 }
1069 #endif
1070 did_scroll = TRUE;
1071 }
1072
1073 curwin->w_redr_status = TRUE;
1074
1075 curwin = old_curwin;
1076 curbuf = curwin->w_buffer;
1077
1078 // The popup menu may overlay the window, need to redraw it.
1079 // TODO: Would be more efficient to only redraw the windows that are
1080 // overlapped by the popup menu.
1081 if (pum_visible() && did_scroll)
1082 {
1083 redraw_all_later(NOT_VALID);
1084 ins_compl_show_pum();
1085 }
1086
1087 if (!EQUAL_POS(curwin->w_cursor, tpos))
1088 {
1089 start_arrow(&tpos);
1090 # ifdef FEAT_CINDENT
1091 set_can_cindent(TRUE);
1092 # endif
1093 }
1094 }
1095
1096 /*
1097 * Return TRUE if "c" is a mouse key.
1098 */
1099 int
1100 is_mouse_key(int c)
1101 {
1102 return c == K_LEFTMOUSE
1103 || c == K_LEFTMOUSE_NM
1104 || c == K_LEFTDRAG
1105 || c == K_LEFTRELEASE
1106 || c == K_LEFTRELEASE_NM
1107 || c == K_MOUSEMOVE
1108 || c == K_MIDDLEMOUSE
1109 || c == K_MIDDLEDRAG
1110 || c == K_MIDDLERELEASE
1111 || c == K_RIGHTMOUSE
1112 || c == K_RIGHTDRAG
1113 || c == K_RIGHTRELEASE
1114 || c == K_MOUSEDOWN
1115 || c == K_MOUSEUP
1116 || c == K_MOUSELEFT
1117 || c == K_MOUSERIGHT
1118 || c == K_X1MOUSE
1119 || c == K_X1DRAG
1120 || c == K_X1RELEASE
1121 || c == K_X2MOUSE
1122 || c == K_X2DRAG
1123 || c == K_X2RELEASE;
1124 }
1125
1126 static struct mousetable
1127 {
1128 int pseudo_code; // Code for pseudo mouse event
1129 int button; // Which mouse button is it?
1130 int is_click; // Is it a mouse button click event?
1131 int is_drag; // Is it a mouse drag event?
1132 } mouse_table[] =
1133 {
1134 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1135 #ifdef FEAT_GUI
1136 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1137 #endif
1138 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1139 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1140 #ifdef FEAT_GUI
1141 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1142 #endif
1143 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1144 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1145 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1146 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1147 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1148 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1149 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1150 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1151 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1152 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1153 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1154 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1155 // DRAG without CLICK
1156 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1157 // RELEASE without CLICK
1158 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1159 {0, 0, 0, 0},
1160 };
1161
1162 /*
1163 * Look up the given mouse code to return the relevant information in the other
1164 * arguments. Return which button is down or was released.
1165 */
1166 int
1167 get_mouse_button(int code, int *is_click, int *is_drag)
1168 {
1169 int i;
1170
1171 for (i = 0; mouse_table[i].pseudo_code; i++)
1172 if (code == mouse_table[i].pseudo_code)
1173 {
1174 *is_click = mouse_table[i].is_click;
1175 *is_drag = mouse_table[i].is_drag;
1176 return mouse_table[i].button;
1177 }
1178 return 0; // Shouldn't get here
1179 }
1180
1181 /*
1182 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1183 * the given information about which mouse button is down, and whether the
1184 * mouse was clicked, dragged or released.
1185 */
1186 int
1187 get_pseudo_mouse_code(
1188 int button, // eg MOUSE_LEFT
1189 int is_click,
1190 int is_drag)
1191 {
1192 int i;
1193
1194 for (i = 0; mouse_table[i].pseudo_code; i++)
1195 if (button == mouse_table[i].button
1196 && is_click == mouse_table[i].is_click
1197 && is_drag == mouse_table[i].is_drag)
1198 {
1199 #ifdef FEAT_GUI
1200 // Trick: a non mappable left click and release has mouse_col -1
1201 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1202 // gui_mouse_moved()
1203 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1204 {
1205 if (mouse_col < 0)
1206 mouse_col = 0;
1207 else
1208 mouse_col -= MOUSE_COLOFF;
1209 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1210 return (int)KE_LEFTMOUSE_NM;
1211 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1212 return (int)KE_LEFTRELEASE_NM;
1213 }
1214 #endif
1215 return mouse_table[i].pseudo_code;
1216 }
1217 return (int)KE_IGNORE; // not recognized, ignore it
1218 }
1219
1220 # ifdef FEAT_MOUSE_TTY
1221 # define HMT_NORMAL 1
1222 # define HMT_NETTERM 2
1223 # define HMT_DEC 4
1224 # define HMT_JSBTERM 8
1225 # define HMT_PTERM 16
1226 # define HMT_URXVT 32
1227 # define HMT_GPM 64
1228 # define HMT_SGR 128
1229 # define HMT_SGR_REL 256
1230 static int has_mouse_termcode = 0;
1231 # endif
1232
1233 # if (!defined(UNIX) || defined(FEAT_MOUSE_TTY)) || defined(PROTO)
1234 void
1235 set_mouse_termcode(
1236 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1237 char_u *s)
1238 {
1239 char_u name[2];
1240
1241 name[0] = n;
1242 name[1] = KE_FILLER;
1243 add_termcode(name, s, FALSE);
1244 # ifdef FEAT_MOUSE_TTY
1245 # ifdef FEAT_MOUSE_JSB
1246 if (n == KS_JSBTERM_MOUSE)
1247 has_mouse_termcode |= HMT_JSBTERM;
1248 else
1249 # endif
1250 # ifdef FEAT_MOUSE_NET
1251 if (n == KS_NETTERM_MOUSE)
1252 has_mouse_termcode |= HMT_NETTERM;
1253 else
1254 # endif
1255 # ifdef FEAT_MOUSE_DEC
1256 if (n == KS_DEC_MOUSE)
1257 has_mouse_termcode |= HMT_DEC;
1258 else
1259 # endif
1260 # ifdef FEAT_MOUSE_PTERM
1261 if (n == KS_PTERM_MOUSE)
1262 has_mouse_termcode |= HMT_PTERM;
1263 else
1264 # endif
1265 # ifdef FEAT_MOUSE_URXVT
1266 if (n == KS_URXVT_MOUSE)
1267 has_mouse_termcode |= HMT_URXVT;
1268 else
1269 # endif
1270 # ifdef FEAT_MOUSE_GPM
1271 if (n == KS_GPM_MOUSE)
1272 has_mouse_termcode |= HMT_GPM;
1273 else
1274 # endif
1275 if (n == KS_SGR_MOUSE)
1276 has_mouse_termcode |= HMT_SGR;
1277 else if (n == KS_SGR_MOUSE_RELEASE)
1278 has_mouse_termcode |= HMT_SGR_REL;
1279 else
1280 has_mouse_termcode |= HMT_NORMAL;
1281 # endif
1282 }
1283 # endif
1284
1285 # if ((defined(UNIX) || defined(VMS)) \
1286 && defined(FEAT_MOUSE_TTY)) || defined(PROTO)
1287 void
1288 del_mouse_termcode(
1289 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1290 {
1291 char_u name[2];
1292
1293 name[0] = n;
1294 name[1] = KE_FILLER;
1295 del_termcode(name);
1296 # ifdef FEAT_MOUSE_TTY
1297 # ifdef FEAT_MOUSE_JSB
1298 if (n == KS_JSBTERM_MOUSE)
1299 has_mouse_termcode &= ~HMT_JSBTERM;
1300 else
1301 # endif
1302 # ifdef FEAT_MOUSE_NET
1303 if (n == KS_NETTERM_MOUSE)
1304 has_mouse_termcode &= ~HMT_NETTERM;
1305 else
1306 # endif
1307 # ifdef FEAT_MOUSE_DEC
1308 if (n == KS_DEC_MOUSE)
1309 has_mouse_termcode &= ~HMT_DEC;
1310 else
1311 # endif
1312 # ifdef FEAT_MOUSE_PTERM
1313 if (n == KS_PTERM_MOUSE)
1314 has_mouse_termcode &= ~HMT_PTERM;
1315 else
1316 # endif
1317 # ifdef FEAT_MOUSE_URXVT
1318 if (n == KS_URXVT_MOUSE)
1319 has_mouse_termcode &= ~HMT_URXVT;
1320 else
1321 # endif
1322 # ifdef FEAT_MOUSE_GPM
1323 if (n == KS_GPM_MOUSE)
1324 has_mouse_termcode &= ~HMT_GPM;
1325 else
1326 # endif
1327 if (n == KS_SGR_MOUSE)
1328 has_mouse_termcode &= ~HMT_SGR;
1329 else if (n == KS_SGR_MOUSE_RELEASE)
1330 has_mouse_termcode &= ~HMT_SGR_REL;
1331 else
1332 has_mouse_termcode &= ~HMT_NORMAL;
1333 # endif
1334 }
1335 # endif
1336
1337 /*
1338 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1339 */
1340 void
1341 setmouse(void)
1342 {
1343 # ifdef FEAT_MOUSE_TTY
1344 int checkfor;
1345 # endif
1346
1347 # ifdef FEAT_MOUSESHAPE
1348 update_mouseshape(-1);
1349 # endif
1350
1351 # ifdef FEAT_MOUSE_TTY // Should be outside proc, but may break MOUSESHAPE
1352 # ifdef FEAT_GUI
1353 // In the GUI the mouse is always enabled.
1354 if (gui.in_use)
1355 return;
1356 # endif
1357 // be quick when mouse is off
1358 if (*p_mouse == NUL || has_mouse_termcode == 0)
1359 return;
1360
1361 // don't switch mouse on when not in raw mode (Ex mode)
1362 if (cur_tmode != TMODE_RAW)
1363 {
1364 mch_setmouse(FALSE);
1365 return;
1366 }
1367
1368 if (VIsual_active)
1369 checkfor = MOUSE_VISUAL;
1370 else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE)
1371 checkfor = MOUSE_RETURN;
1372 else if (State & INSERT)
1373 checkfor = MOUSE_INSERT;
1374 else if (State & CMDLINE)
1375 checkfor = MOUSE_COMMAND;
1376 else if (State == CONFIRM || State == EXTERNCMD)
1377 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1378 else
1379 checkfor = MOUSE_NORMAL; // assume normal mode
1380
1381 if (mouse_has(checkfor))
1382 mch_setmouse(TRUE);
1383 else
1384 mch_setmouse(FALSE);
1385 # endif
1386 }
1387
1388 /*
1389 * Return TRUE if
1390 * - "c" is in 'mouse', or
1391 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1392 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1393 * normal editing mode (not at hit-return message).
1394 */
1395 int
1396 mouse_has(int c)
1397 {
1398 char_u *p;
1399
1400 for (p = p_mouse; *p; ++p)
1401 switch (*p)
1402 {
1403 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1404 return TRUE;
1405 break;
1406 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1407 return TRUE;
1408 break;
1409 default: if (c == *p) return TRUE; break;
1410 }
1411 return FALSE;
1412 }
1413
1414 /*
1415 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1416 */
1417 int
1418 mouse_model_popup(void)
1419 {
1420 return (p_mousem[0] == 'p');
1421 }
1422
1423 /*
1424 * Move the cursor to the specified row and column on the screen.
1425 * Change current window if necessary. Returns an integer with the
1426 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1427 *
1428 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1429 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1430 *
1431 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1432 * if the mouse is outside the window then the text will scroll, or if the
1433 * mouse was previously on a status line, then the status line may be dragged.
1434 *
1435 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1436 * cursor is moved unless the cursor was on a status line.
1437 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1438 * IN_SEP_LINE depending on where the cursor was clicked.
1439 *
1440 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1441 * the mouse is on the status line of the same window.
1442 *
1443 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1444 * the last call.
1445 *
1446 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1447 * remembered.
1448 */
1449 int
1450 jump_to_mouse(
1451 int flags,
1452 int *inclusive, // used for inclusive operator, can be NULL
1453 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1454 {
1455 static int on_status_line = 0; // #lines below bottom of window
1456 static int on_sep_line = 0; // on separator right of window
1457 #ifdef FEAT_MENU
1458 static int in_winbar = FALSE;
1459 #endif
1460 #ifdef FEAT_TEXT_PROP
1461 static int in_popup_win = FALSE;
1462 static win_T *click_in_popup_win = NULL;
1463 #endif
1464 static int prev_row = -1;
1465 static int prev_col = -1;
1466 static win_T *dragwin = NULL; // window being dragged
1467 static int did_drag = FALSE; // drag was noticed
1468
1469 win_T *wp, *old_curwin;
1470 pos_T old_cursor;
1471 int count;
1472 int first;
1473 int row = mouse_row;
1474 int col = mouse_col;
1475 #ifdef FEAT_FOLDING
1476 int mouse_char;
1477 #endif
1478
1479 mouse_past_bottom = FALSE;
1480 mouse_past_eol = FALSE;
1481
1482 if (flags & MOUSE_RELEASED)
1483 {
1484 // On button release we may change window focus if positioned on a
1485 // status line and no dragging happened.
1486 if (dragwin != NULL && !did_drag)
1487 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1488 dragwin = NULL;
1489 did_drag = FALSE;
1490 #ifdef FEAT_TEXT_PROP
1491 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1492 popup_close_for_mouse_click(click_in_popup_win);
1493
1494 popup_dragwin = NULL;
1495 click_in_popup_win = NULL;
1496 #endif
1497 }
1498
1499 if ((flags & MOUSE_DID_MOVE)
1500 && prev_row == mouse_row
1501 && prev_col == mouse_col)
1502 {
1503 retnomove:
1504 // before moving the cursor for a left click which is NOT in a status
1505 // line, stop Visual mode
1506 if (on_status_line)
1507 return IN_STATUS_LINE;
1508 if (on_sep_line)
1509 return IN_SEP_LINE;
1510 #ifdef FEAT_MENU
1511 if (in_winbar)
1512 {
1513 // A quick second click may arrive as a double-click, but we use it
1514 // as a second click in the WinBar.
1515 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1516 {
1517 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1518 if (wp == NULL)
1519 return IN_UNKNOWN;
1520 winbar_click(wp, col);
1521 }
1522 return IN_OTHER_WIN | MOUSE_WINBAR;
1523 }
1524 #endif
1525 if (flags & MOUSE_MAY_STOP_VIS)
1526 {
1527 end_visual_mode();
1528 redraw_curbuf_later(INVERTED); // delete the inversion
1529 }
1530 #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1531 // Continue a modeless selection in another window.
1532 if (cmdwin_type != 0 && row < curwin->w_winrow)
1533 return IN_OTHER_WIN;
1534 #endif
1535 #ifdef FEAT_TEXT_PROP
1536 // Continue a modeless selection in a popup window or dragging it.
1537 if (in_popup_win)
1538 {
1539 click_in_popup_win = NULL; // don't close it on release
1540 if (popup_dragwin != NULL)
1541 {
1542 // dragging a popup window
1543 popup_drag(popup_dragwin);
1544 return IN_UNKNOWN;
1545 }
1546 return IN_OTHER_WIN;
1547 }
1548 #endif
1549 return IN_BUFFER;
1550 }
1551
1552 prev_row = mouse_row;
1553 prev_col = mouse_col;
1554
1555 if (flags & MOUSE_SETPOS)
1556 goto retnomove; // ugly goto...
1557
1558 #ifdef FEAT_FOLDING
1559 // Remember the character under the mouse, it might be a '-' or '+' in the
1560 // fold column.
1561 if (row >= 0 && row < Rows && col >= 0 && col <= Columns
1562 && ScreenLines != NULL)
1563 mouse_char = ScreenLines[LineOffset[row] + col];
1564 else
1565 mouse_char = ' ';
1566 #endif
1567
1568 old_curwin = curwin;
1569 old_cursor = curwin->w_cursor;
1570
1571 if (!(flags & MOUSE_FOCUS))
1572 {
1573 if (row < 0 || col < 0) // check if it makes sense
1574 return IN_UNKNOWN;
1575
1576 // find the window where the row is in and adjust "row" and "col" to be
1577 // relative to top-left of the window
1578 wp = mouse_find_win(&row, &col, FIND_POPUP);
1579 if (wp == NULL)
1580 return IN_UNKNOWN;
1581 dragwin = NULL;
1582
1583 #ifdef FEAT_TEXT_PROP
1584 // Click in a popup window may start dragging or modeless selection,
1585 // but not much else.
1586 if (WIN_IS_POPUP(wp))
1587 {
1588 on_sep_line = 0;
1589 in_popup_win = TRUE;
1590 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1591 {
1592 return IN_UNKNOWN;
1593 }
1594 else if ((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
1595 && popup_on_border(wp, row, col))
1596 {
1597 popup_dragwin = wp;
1598 popup_start_drag(wp, row, col);
1599 return IN_UNKNOWN;
1600 }
1601 // Only close on release, otherwise it's not possible to drag or do
1602 // modeless selection.
1603 else if (wp->w_popup_close == POPCLOSE_CLICK
1604 && which_button == MOUSE_LEFT)
1605 {
1606 click_in_popup_win = wp;
1607 }
1608 else if (which_button == MOUSE_LEFT)
1609 // If the click is in the scrollbar, may scroll up/down.
1610 popup_handle_scrollbar_click(wp, row, col);
1611 # ifdef FEAT_CLIPBOARD
1612 return IN_OTHER_WIN;
1613 # else
1614 return IN_UNKNOWN;
1615 # endif
1616 }
1617 in_popup_win = FALSE;
1618 popup_dragwin = NULL;
1619 #endif
1620 #ifdef FEAT_MENU
1621 if (row == -1)
1622 {
1623 // A click in the window toolbar does not enter another window or
1624 // change Visual highlighting.
1625 winbar_click(wp, col);
1626 in_winbar = TRUE;
1627 return IN_OTHER_WIN | MOUSE_WINBAR;
1628 }
1629 in_winbar = FALSE;
1630 #endif
1631
1632 // winpos and height may change in win_enter()!
1633 if (row >= wp->w_height) // In (or below) status line
1634 {
1635 on_status_line = row - wp->w_height + 1;
1636 dragwin = wp;
1637 }
1638 else
1639 on_status_line = 0;
1640 if (col >= wp->w_width) // In separator line
1641 {
1642 on_sep_line = col - wp->w_width + 1;
1643 dragwin = wp;
1644 }
1645 else
1646 on_sep_line = 0;
1647
1648 // The rightmost character of the status line might be a vertical
1649 // separator character if there is no connecting window to the right.
1650 if (on_status_line && on_sep_line)
1651 {
1652 if (stl_connected(wp))
1653 on_sep_line = 0;
1654 else
1655 on_status_line = 0;
1656 }
1657
1658 // Before jumping to another buffer, or moving the cursor for a left
1659 // click, stop Visual mode.
1660 if (VIsual_active
1661 && (wp->w_buffer != curwin->w_buffer
1662 || (!on_status_line && !on_sep_line
1663 #ifdef FEAT_FOLDING
1664 && (
1665 # ifdef FEAT_RIGHTLEFT
1666 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1667 # endif
1668 col >= wp->w_p_fdc
1669 # ifdef FEAT_CMDWIN
1670 + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
1671 # endif
1672 )
1673 #endif
1674 && (flags & MOUSE_MAY_STOP_VIS))))
1675 {
1676 end_visual_mode();
1677 redraw_curbuf_later(INVERTED); // delete the inversion
1678 }
1679 #ifdef FEAT_CMDWIN
1680 if (cmdwin_type != 0 && wp != curwin)
1681 {
1682 // A click outside the command-line window: Use modeless
1683 // selection if possible. Allow dragging the status lines.
1684 on_sep_line = 0;
1685 # ifdef FEAT_CLIPBOARD
1686 if (on_status_line)
1687 return IN_STATUS_LINE;
1688 return IN_OTHER_WIN;
1689 # else
1690 row = 0;
1691 col += wp->w_wincol;
1692 wp = curwin;
1693 # endif
1694 }
1695 #endif
1696 // Only change window focus when not clicking on or dragging the
1697 // status line. Do change focus when releasing the mouse button
1698 // (MOUSE_FOCUS was set above if we dragged first).
1699 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1700 win_enter(wp, TRUE); // can make wp invalid!
1701
1702 if (curwin != old_curwin)
1703 {
1704 #ifdef CHECK_DOUBLE_CLICK
1705 // set topline, to be able to check for double click ourselves
1706 set_mouse_topline(curwin);
1707 #endif
1708 #ifdef FEAT_TERMINAL
1709 // when entering a terminal window may change state
1710 term_win_entered();
1711 #endif
1712 }
1713 if (on_status_line) // In (or below) status line
1714 {
1715 // Don't use start_arrow() if we're in the same window
1716 if (curwin == old_curwin)
1717 return IN_STATUS_LINE;
1718 else
1719 return IN_STATUS_LINE | CURSOR_MOVED;
1720 }
1721 if (on_sep_line) // In (or below) status line
1722 {
1723 // Don't use start_arrow() if we're in the same window
1724 if (curwin == old_curwin)
1725 return IN_SEP_LINE;
1726 else
1727 return IN_SEP_LINE | CURSOR_MOVED;
1728 }
1729
1730 curwin->w_cursor.lnum = curwin->w_topline;
1731 #ifdef FEAT_GUI
1732 // remember topline, needed for double click
1733 gui_prev_topline = curwin->w_topline;
1734 # ifdef FEAT_DIFF
1735 gui_prev_topfill = curwin->w_topfill;
1736 # endif
1737 #endif
1738 }
1739 else if (on_status_line && which_button == MOUSE_LEFT)
1740 {
1741 if (dragwin != NULL)
1742 {
1743 // Drag the status line
1744 count = row - dragwin->w_winrow - dragwin->w_height + 1
1745 - on_status_line;
1746 win_drag_status_line(dragwin, count);
1747 did_drag |= count;
1748 }
1749 return IN_STATUS_LINE; // Cursor didn't move
1750 }
1751 else if (on_sep_line && which_button == MOUSE_LEFT)
1752 {
1753 if (dragwin != NULL)
1754 {
1755 // Drag the separator column
1756 count = col - dragwin->w_wincol - dragwin->w_width + 1
1757 - on_sep_line;
1758 win_drag_vsep_line(dragwin, count);
1759 did_drag |= count;
1760 }
1761 return IN_SEP_LINE; // Cursor didn't move
1762 }
1763 #ifdef FEAT_MENU
1764 else if (in_winbar)
1765 {
1766 // After a click on the window toolbar don't start Visual mode.
1767 return IN_OTHER_WIN | MOUSE_WINBAR;
1768 }
1769 #endif
1770 else // keep_window_focus must be TRUE
1771 {
1772 // before moving the cursor for a left click, stop Visual mode
1773 if (flags & MOUSE_MAY_STOP_VIS)
1774 {
1775 end_visual_mode();
1776 redraw_curbuf_later(INVERTED); // delete the inversion
1777 }
1778
1779 #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
1780 // Continue a modeless selection in another window.
1781 if (cmdwin_type != 0 && row < curwin->w_winrow)
1782 return IN_OTHER_WIN;
1783 #endif
1784 #ifdef FEAT_TEXT_PROP
1785 if (in_popup_win)
1786 {
1787 if (popup_dragwin != NULL)
1788 {
1789 // dragging a popup window
1790 popup_drag(popup_dragwin);
1791 return IN_UNKNOWN;
1792 }
1793 // continue a modeless selection in a popup window
1794 click_in_popup_win = NULL;
1795 return IN_OTHER_WIN;
1796 }
1797 #endif
1798
1799 row -= W_WINROW(curwin);
1800 col -= curwin->w_wincol;
1801
1802 // When clicking beyond the end of the window, scroll the screen.
1803 // Scroll by however many rows outside the window we are.
1804 if (row < 0)
1805 {
1806 count = 0;
1807 for (first = TRUE; curwin->w_topline > 1; )
1808 {
1809 #ifdef FEAT_DIFF
1810 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1811 ++count;
1812 else
1813 #endif
1814 count += plines(curwin->w_topline - 1);
1815 if (!first && count > -row)
1816 break;
1817 first = FALSE;
1818 #ifdef FEAT_FOLDING
1819 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1820 #endif
1821 #ifdef FEAT_DIFF
1822 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1823 ++curwin->w_topfill;
1824 else
1825 #endif
1826 {
1827 --curwin->w_topline;
1828 #ifdef FEAT_DIFF
1829 curwin->w_topfill = 0;
1830 #endif
1831 }
1832 }
1833 #ifdef FEAT_DIFF
1834 check_topfill(curwin, FALSE);
1835 #endif
1836 curwin->w_valid &=
1837 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1838 redraw_later(VALID);
1839 row = 0;
1840 }
1841 else if (row >= curwin->w_height)
1842 {
1843 count = 0;
1844 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
1845 {
1846 #ifdef FEAT_DIFF
1847 if (curwin->w_topfill > 0)
1848 ++count;
1849 else
1850 #endif
1851 count += plines(curwin->w_topline);
1852 if (!first && count > row - curwin->w_height + 1)
1853 break;
1854 first = FALSE;
1855 #ifdef FEAT_FOLDING
1856 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
1857 && curwin->w_topline == curbuf->b_ml.ml_line_count)
1858 break;
1859 #endif
1860 #ifdef FEAT_DIFF
1861 if (curwin->w_topfill > 0)
1862 --curwin->w_topfill;
1863 else
1864 #endif
1865 {
1866 ++curwin->w_topline;
1867 #ifdef FEAT_DIFF
1868 curwin->w_topfill =
1869 diff_check_fill(curwin, curwin->w_topline);
1870 #endif
1871 }
1872 }
1873 #ifdef FEAT_DIFF
1874 check_topfill(curwin, FALSE);
1875 #endif
1876 redraw_later(VALID);
1877 curwin->w_valid &=
1878 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1879 row = curwin->w_height - 1;
1880 }
1881 else if (row == 0)
1882 {
1883 // When dragging the mouse, while the text has been scrolled up as
1884 // far as it goes, moving the mouse in the top line should scroll
1885 // the text down (done later when recomputing w_topline).
1886 if (mouse_dragging > 0
1887 && curwin->w_cursor.lnum
1888 == curwin->w_buffer->b_ml.ml_line_count
1889 && curwin->w_cursor.lnum == curwin->w_topline)
1890 curwin->w_valid &= ~(VALID_TOPLINE);
1891 }
1892 }
1893
1894 #ifdef FEAT_FOLDING
1895 // Check for position outside of the fold column.
1896 if (
1897 # ifdef FEAT_RIGHTLEFT
1898 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
1899 # endif
1900 col >= curwin->w_p_fdc
1901 # ifdef FEAT_CMDWIN
1902 + (cmdwin_type == 0 ? 0 : 1)
1903 # endif
1904 )
1905 mouse_char = ' ';
1906 #endif
1907
1908 // compute the position in the buffer line from the posn on the screen
1909 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
1910 mouse_past_bottom = TRUE;
1911
1912 // Start Visual mode before coladvance(), for when 'sel' != "old"
1913 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
1914 {
1915 check_visual_highlight();
1916 VIsual = old_cursor;
1917 VIsual_active = TRUE;
1918 VIsual_reselect = TRUE;
1919 // if 'selectmode' contains "mouse", start Select mode
1920 may_start_select('o');
1921 setmouse();
1922 if (p_smd && msg_silent == 0)
1923 redraw_cmdline = TRUE; // show visual mode later
1924 }
1925
1926 curwin->w_curswant = col;
1927 curwin->w_set_curswant = FALSE; // May still have been TRUE
1928 if (coladvance(col) == FAIL) // Mouse click beyond end of line
1929 {
1930 if (inclusive != NULL)
1931 *inclusive = TRUE;
1932 mouse_past_eol = TRUE;
1933 }
1934 else if (inclusive != NULL)
1935 *inclusive = FALSE;
1936
1937 count = IN_BUFFER;
1938 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
1939 || curwin->w_cursor.col != old_cursor.col)
1940 count |= CURSOR_MOVED; // Cursor has moved
1941
1942 # ifdef FEAT_FOLDING
1943 if (mouse_char == '+')
1944 count |= MOUSE_FOLD_OPEN;
1945 else if (mouse_char != ' ')
1946 count |= MOUSE_FOLD_CLOSE;
1947 # endif
1948
1949 return count;
1950 }
1951
1952 /*
1953 * Mouse scroll wheel: Default action is to scroll three lines, or one page
1954 * when Shift or Ctrl is used.
1955 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
1956 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
1957 */
1958 void
1959 nv_mousescroll(cmdarg_T *cap)
1960 {
1961 win_T *old_curwin = curwin, *wp;
1962
1963 if (mouse_row >= 0 && mouse_col >= 0)
1964 {
1965 int row, col;
1966
1967 row = mouse_row;
1968 col = mouse_col;
1969
1970 // find the window at the pointer coordinates
1971 wp = mouse_find_win(&row, &col, FIND_POPUP);
1972 if (wp == NULL)
1973 return;
1974 #ifdef FEAT_TEXT_PROP
1975 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
1976 return;
1977 #endif
1978 curwin = wp;
1979 curbuf = curwin->w_buffer;
1980 }
1981
1982 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
1983 {
1984 # ifdef FEAT_TERMINAL
1985 if (term_use_loop())
1986 // This window is a terminal window, send the mouse event there.
1987 // Set "typed" to FALSE to avoid an endless loop.
1988 send_keys_to_term(curbuf->b_term, cap->cmdchar, FALSE);
1989 else
1990 # endif
1991 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1992 {
1993 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
1994 }
1995 else
1996 {
1997 // Don't scroll more than half the window height.
1998 if (curwin->w_height < 6)
1999 {
2000 cap->count1 = curwin->w_height / 2;
2001 if (cap->count1 == 0)
2002 cap->count1 = 1;
2003 }
2004 else
2005 cap->count1 = 3;
2006 cap->count0 = cap->count1;
2007 nv_scroll_line(cap);
2008 }
2009 #ifdef FEAT_TEXT_PROP
2010 if (WIN_IS_POPUP(curwin))
2011 popup_set_firstline(curwin);
2012 #endif
2013 }
2014 # ifdef FEAT_GUI
2015 else
2016 {
2017 // Horizontal scroll - only allowed when 'wrap' is disabled
2018 if (!curwin->w_p_wrap)
2019 {
2020 int val, step = 6;
2021
2022 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
2023 step = curwin->w_width;
2024 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
2025 if (val < 0)
2026 val = 0;
2027
2028 gui_do_horiz_scroll(val, TRUE);
2029 }
2030 }
2031 # endif
2032 # ifdef FEAT_SYN_HL
2033 if (curwin != old_curwin && curwin->w_p_cul)
2034 redraw_for_cursorline(curwin);
2035 # endif
2036
2037 curwin->w_redr_status = TRUE;
2038
2039 curwin = old_curwin;
2040 curbuf = curwin->w_buffer;
2041 }
2042
2043 /*
2044 * Mouse clicks and drags.
2045 */
2046 void
2047 nv_mouse(cmdarg_T *cap)
2048 {
2049 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2050 }
2051 #endif // FEAT_MOUSE
2052
2053 // Functions also used for popup windows.
2054 #if defined(FEAT_MOUSE) || defined(FEAT_TEXT_PROP) || defined(PROTO)
2055
2056 /*
2057 * Compute the buffer line position from the screen position "rowp" / "colp" in
2058 * window "win".
2059 * "plines_cache" can be NULL (no cache) or an array with "win->w_height"
2060 * entries that caches the plines_win() result from a previous call. Entry is
2061 * zero if not computed yet. There must be no text or setting changes since
2062 * the entry is put in the cache.
2063 * Returns TRUE if the position is below the last line.
2064 */
2065 int
2066 mouse_comp_pos(
2067 win_T *win,
2068 int *rowp,
2069 int *colp,
2070 linenr_T *lnump,
2071 int *plines_cache)
2072 {
2073 int col = *colp;
2074 int row = *rowp;
2075 linenr_T lnum;
2076 int retval = FALSE;
2077 int off;
2078 int count;
2079
2080 #ifdef FEAT_RIGHTLEFT
2081 if (win->w_p_rl)
2082 col = win->w_width - 1 - col;
2083 #endif
2084
2085 lnum = win->w_topline;
2086
2087 while (row > 0)
2088 {
2089 int cache_idx = lnum - win->w_topline;
2090
2091 if (plines_cache != NULL && plines_cache[cache_idx] > 0)
2092 count = plines_cache[cache_idx];
2093 else
2094 {
2095 #ifdef FEAT_DIFF
2096 // Don't include filler lines in "count"
2097 if (win->w_p_diff
2098 # ifdef FEAT_FOLDING
2099 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
2100 # endif
2101 )
2102 {
2103 if (lnum == win->w_topline)
2104 row -= win->w_topfill;
2105 else
2106 row -= diff_check_fill(win, lnum);
2107 count = plines_win_nofill(win, lnum, TRUE);
2108 }
2109 else
2110 #endif
2111 count = plines_win(win, lnum, TRUE);
2112 if (plines_cache != NULL)
2113 plines_cache[cache_idx] = count;
2114 }
2115 if (count > row)
2116 break; // Position is in this buffer line.
2117 #ifdef FEAT_FOLDING
2118 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
2119 #endif
2120 if (lnum == win->w_buffer->b_ml.ml_line_count)
2121 {
2122 retval = TRUE;
2123 break; // past end of file
2124 }
2125 row -= count;
2126 ++lnum;
2127 }
2128
2129 if (!retval)
2130 {
2131 // Compute the column without wrapping.
2132 off = win_col_off(win) - win_col_off2(win);
2133 if (col < off)
2134 col = off;
2135 col += row * (win->w_width - off);
2136 // add skip column (for long wrapping line)
2137 col += win->w_skipcol;
2138 }
2139
2140 if (!win->w_p_wrap)
2141 col += win->w_leftcol;
2142
2143 // skip line number and fold column in front of the line
2144 col -= win_col_off(win);
2145 if (col < 0)
2146 {
2147 #ifdef FEAT_NETBEANS_INTG
2148 netbeans_gutter_click(lnum);
2149 #endif
2150 col = 0;
2151 }
2152
2153 *colp = col;
2154 *rowp = row;
2155 *lnump = lnum;
2156 return retval;
2157 }
2158
2159 /*
2160 * Find the window at screen position "*rowp" and "*colp". The positions are
2161 * updated to become relative to the top-left of the window.
2162 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
2163 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
2164 * windows.
2165 * Returns NULL when something is wrong.
2166 */
2167 win_T *
2168 mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
2169 {
2170 frame_T *fp;
2171 win_T *wp;
2172
2173 #ifdef FEAT_TEXT_PROP
2174 win_T *pwp = NULL;
2175
2176 if (popup != IGNORE_POPUP)
2177 {
2178 popup_reset_handled();
2179 while ((wp = find_next_popup(TRUE)) != NULL)
2180 {
2181 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
2182 && *colp >= wp->w_wincol
2183 && *colp < wp->w_wincol + popup_width(wp))
2184 pwp = wp;
2185 }
2186 if (pwp != NULL)
2187 {
2188 if (popup == FAIL_POPUP)
2189 return NULL;
2190 *rowp -= pwp->w_winrow;
2191 *colp -= pwp->w_wincol;
2192 return pwp;
2193 }
2194 }
2195 #endif
2196
2197 fp = topframe;
2198 *rowp -= firstwin->w_winrow;
2199 for (;;)
2200 {
2201 if (fp->fr_layout == FR_LEAF)
2202 break;
2203 if (fp->fr_layout == FR_ROW)
2204 {
2205 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
2206 {
2207 if (*colp < fp->fr_width)
2208 break;
2209 *colp -= fp->fr_width;
2210 }
2211 }
2212 else // fr_layout == FR_COL
2213 {
2214 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
2215 {
2216 if (*rowp < fp->fr_height)
2217 break;
2218 *rowp -= fp->fr_height;
2219 }
2220 }
2221 }
2222 // When using a timer that closes a window the window might not actually
2223 // exist.
2224 FOR_ALL_WINDOWS(wp)
2225 if (wp == fp->fr_win)
2226 {
2227 #ifdef FEAT_MENU
2228 *rowp -= wp->w_winbar_height;
2229 #endif
2230 return wp;
2231 }
2232 return NULL;
2233 }
2234
2235 #if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MAC) \
2236 || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
2237 || defined(FEAT_GUI_PHOTON) || defined(FEAT_TERM_POPUP_MENU) \
2238 || defined(PROTO)
2239 # define NEED_VCOL2COL
2240
2241 /*
2242 * Translate window coordinates to buffer position without any side effects
2243 */
2244 static int
2245 get_fpos_of_mouse(pos_T *mpos)
2246 {
2247 win_T *wp;
2248 int row = mouse_row;
2249 int col = mouse_col;
2250
2251 if (row < 0 || col < 0) // check if it makes sense
2252 return IN_UNKNOWN;
2253
2254 // find the window where the row is in
2255 wp = mouse_find_win(&row, &col, FAIL_POPUP);
2256 if (wp == NULL)
2257 return IN_UNKNOWN;
2258 // winpos and height may change in win_enter()!
2259 if (row >= wp->w_height) // In (or below) status line
2260 return IN_STATUS_LINE;
2261 if (col >= wp->w_width) // In vertical separator line
2262 return IN_SEP_LINE;
2263
2264 if (wp != curwin)
2265 return IN_UNKNOWN;
2266
2267 // compute the position in the buffer line from the posn on the screen
2268 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
2269 return IN_STATUS_LINE; // past bottom
2270
2271 mpos->col = vcol2col(wp, mpos->lnum, col);
2272
2273 if (mpos->col > 0)
2274 --mpos->col;
2275 mpos->coladd = 0;
2276 return IN_BUFFER;
2277 }
2278 #endif
2279
2280 #if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) \
2281 || defined(PROTO)
2282 /*
2283 * Convert a virtual (screen) column to a character column.
2284 * The first column is one.
2285 */
2286 int
2287 vcol2col(win_T *wp, linenr_T lnum, int vcol)
2288 {
2289 // try to advance to the specified column
2290 int count = 0;
2291 char_u *ptr;
2292 char_u *line;
2293
2294 line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
2295 while (count < vcol && *ptr != NUL)
2296 {
2297 count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
2298 MB_PTR_ADV(ptr);
2299 }
2300 return (int)(ptr - line);
2301 }
2302 #endif
2303
2304 #else // FEAT_MOUSE
2305
2306 /*
2307 * Dummy implementation of setmouse() to avoid lots of #ifdefs.
2308 */
2309 void
2310 setmouse(void)
2311 {
2312 }
2313
2314 #endif // FEAT_MOUSE