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