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