Mercurial > vim
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 |