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 // Only change window focus when not clicking on or dragging the 1740 // status line. Do change focus when releasing the mouse button 1741 // (MOUSE_FOCUS was set above if we dragged first). 1742 if (dragwin == NULL || (flags & MOUSE_RELEASED)) 1743 win_enter(wp, TRUE); // can make wp invalid! 1744 1745 if (curwin != old_curwin) 1746 { 1747 #ifdef CHECK_DOUBLE_CLICK 1748 // set topline, to be able to check for double click ourselves 1749 set_mouse_topline(curwin); 1750 #endif 1751 #ifdef FEAT_TERMINAL 1752 // when entering a terminal window may change state 1753 term_win_entered(); 1754 #endif 1755 } 1756 if (on_status_line) // In (or below) status line 1757 { 1758 // Don't use start_arrow() if we're in the same window 1759 if (curwin == old_curwin) 1760 return IN_STATUS_LINE; 1761 else 1762 return IN_STATUS_LINE | CURSOR_MOVED; 1763 } 1764 if (on_sep_line) // In (or below) status line 1765 { 1766 // Don't use start_arrow() if we're in the same window 1767 if (curwin == old_curwin) 1768 return IN_SEP_LINE; 1769 else 1770 return IN_SEP_LINE | CURSOR_MOVED; 1771 } 1772 1773 curwin->w_cursor.lnum = curwin->w_topline; 1774 #ifdef FEAT_GUI 1775 // remember topline, needed for double click 1776 gui_prev_topline = curwin->w_topline; 1777 # ifdef FEAT_DIFF 1778 gui_prev_topfill = curwin->w_topfill; 1779 # endif 1780 #endif 1781 } 1782 else if (on_status_line && which_button == MOUSE_LEFT) 1783 { 1784 if (dragwin != NULL) 1785 { 1786 // Drag the status line 1787 count = row - dragwin->w_winrow - dragwin->w_height + 1 1788 - on_status_line; 1789 win_drag_status_line(dragwin, count); 1790 did_drag |= count; 1791 } 1792 return IN_STATUS_LINE; // Cursor didn't move 1793 } 1794 else if (on_sep_line && which_button == MOUSE_LEFT) 1795 { 1796 if (dragwin != NULL) 1797 { 1798 // Drag the separator column 1799 count = col - dragwin->w_wincol - dragwin->w_width + 1 1800 - on_sep_line; 1801 win_drag_vsep_line(dragwin, count); 1802 did_drag |= count; 1803 } 1804 return IN_SEP_LINE; // Cursor didn't move 1805 } 1806 #ifdef FEAT_MENU 1807 else if (in_winbar) 1808 { 1809 // After a click on the window toolbar don't start Visual mode. 1810 return IN_OTHER_WIN | MOUSE_WINBAR; 1811 } 1812 #endif 1813 else // keep_window_focus must be TRUE 1814 { 1815 // before moving the cursor for a left click, stop Visual mode 1816 if (flags & MOUSE_MAY_STOP_VIS) 1817 { 1818 end_visual_mode(); 1819 redraw_curbuf_later(INVERTED); // delete the inversion 1820 } 1821 1822 #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD) 1823 // Continue a modeless selection in another window. 1824 if (cmdwin_type != 0 && row < curwin->w_winrow) 1825 return IN_OTHER_WIN; 1826 #endif 1827 #ifdef FEAT_PROP_POPUP 1828 if (in_popup_win) 1829 { 1830 if (popup_dragwin != NULL) 1831 { 1832 // dragging a popup window 1833 popup_drag(popup_dragwin); 1834 return IN_UNKNOWN; 1835 } 1836 // continue a modeless selection in a popup window 1837 click_in_popup_win = NULL; 1838 return IN_OTHER_WIN; 1839 } 1840 #endif 1841 1842 row -= W_WINROW(curwin); 1843 col -= curwin->w_wincol; 1844 1845 // When clicking beyond the end of the window, scroll the screen. 1846 // Scroll by however many rows outside the window we are. 1847 if (row < 0) 1848 { 1849 count = 0; 1850 for (first = TRUE; curwin->w_topline > 1; ) 1851 { 1852 #ifdef FEAT_DIFF 1853 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) 1854 ++count; 1855 else 1856 #endif 1857 count += plines(curwin->w_topline - 1); 1858 if (!first && count > -row) 1859 break; 1860 first = FALSE; 1861 #ifdef FEAT_FOLDING 1862 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1863 #endif 1864 #ifdef FEAT_DIFF 1865 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) 1866 ++curwin->w_topfill; 1867 else 1868 #endif 1869 { 1870 --curwin->w_topline; 1871 #ifdef FEAT_DIFF 1872 curwin->w_topfill = 0; 1873 #endif 1874 } 1875 } 1876 #ifdef FEAT_DIFF 1877 check_topfill(curwin, FALSE); 1878 #endif 1879 curwin->w_valid &= 1880 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 1881 redraw_later(VALID); 1882 row = 0; 1883 } 1884 else if (row >= curwin->w_height) 1885 { 1886 count = 0; 1887 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; ) 1888 { 1889 #ifdef FEAT_DIFF 1890 if (curwin->w_topfill > 0) 1891 ++count; 1892 else 1893 #endif 1894 count += plines(curwin->w_topline); 1895 if (!first && count > row - curwin->w_height + 1) 1896 break; 1897 first = FALSE; 1898 #ifdef FEAT_FOLDING 1899 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline) 1900 && curwin->w_topline == curbuf->b_ml.ml_line_count) 1901 break; 1902 #endif 1903 #ifdef FEAT_DIFF 1904 if (curwin->w_topfill > 0) 1905 --curwin->w_topfill; 1906 else 1907 #endif 1908 { 1909 ++curwin->w_topline; 1910 #ifdef FEAT_DIFF 1911 curwin->w_topfill = 1912 diff_check_fill(curwin, curwin->w_topline); 1913 #endif 1914 } 1915 } 1916 #ifdef FEAT_DIFF 1917 check_topfill(curwin, FALSE); 1918 #endif 1919 redraw_later(VALID); 1920 curwin->w_valid &= 1921 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 1922 row = curwin->w_height - 1; 1923 } 1924 else if (row == 0) 1925 { 1926 // When dragging the mouse, while the text has been scrolled up as 1927 // far as it goes, moving the mouse in the top line should scroll 1928 // the text down (done later when recomputing w_topline). 1929 if (mouse_dragging > 0 1930 && curwin->w_cursor.lnum 1931 == curwin->w_buffer->b_ml.ml_line_count 1932 && curwin->w_cursor.lnum == curwin->w_topline) 1933 curwin->w_valid &= ~(VALID_TOPLINE); 1934 } 1935 } 1936 1937 #ifdef FEAT_FOLDING 1938 // Check for position outside of the fold column. 1939 if ( 1940 # ifdef FEAT_RIGHTLEFT 1941 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc : 1942 # endif 1943 col >= curwin->w_p_fdc 1944 # ifdef FEAT_CMDWIN 1945 + (cmdwin_type == 0 ? 0 : 1) 1946 # endif 1947 ) 1948 mouse_char = ' '; 1949 #endif 1950 1951 // compute the position in the buffer line from the posn on the screen 1952 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL)) 1953 mouse_past_bottom = TRUE; 1954 1955 // Start Visual mode before coladvance(), for when 'sel' != "old" 1956 if ((flags & MOUSE_MAY_VIS) && !VIsual_active) 1957 { 1958 check_visual_highlight(); 1959 VIsual = old_cursor; 1960 VIsual_active = TRUE; 1961 VIsual_reselect = TRUE; 1962 // if 'selectmode' contains "mouse", start Select mode 1963 may_start_select('o'); 1964 setmouse(); 1965 if (p_smd && msg_silent == 0) 1966 redraw_cmdline = TRUE; // show visual mode later 1967 } 1968 1969 curwin->w_curswant = col; 1970 curwin->w_set_curswant = FALSE; // May still have been TRUE 1971 if (coladvance(col) == FAIL) // Mouse click beyond end of line 1972 { 1973 if (inclusive != NULL) 1974 *inclusive = TRUE; 1975 mouse_past_eol = TRUE; 1976 } 1977 else if (inclusive != NULL) 1978 *inclusive = FALSE; 1979 1980 count = IN_BUFFER; 1981 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum 1982 || curwin->w_cursor.col != old_cursor.col) 1983 count |= CURSOR_MOVED; // Cursor has moved 1984 1985 # ifdef FEAT_FOLDING 1986 if (mouse_char == '+') 1987 count |= MOUSE_FOLD_OPEN; 1988 else if (mouse_char != ' ') 1989 count |= MOUSE_FOLD_CLOSE; 1990 # endif 1991 1992 return count; 1993 } 1994 1995 /* 1996 * Mouse scroll wheel: Default action is to scroll three lines, or one page 1997 * when Shift or Ctrl is used. 1998 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or 1999 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2) 2000 */ 2001 void 2002 nv_mousescroll(cmdarg_T *cap) 2003 { 2004 win_T *old_curwin = curwin, *wp; 2005 2006 if (mouse_row >= 0 && mouse_col >= 0) 2007 { 2008 int row, col; 2009 2010 row = mouse_row; 2011 col = mouse_col; 2012 2013 // find the window at the pointer coordinates 2014 wp = mouse_find_win(&row, &col, FIND_POPUP); 2015 if (wp == NULL) 2016 return; 2017 #ifdef FEAT_PROP_POPUP 2018 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar) 2019 return; 2020 #endif 2021 curwin = wp; 2022 curbuf = curwin->w_buffer; 2023 } 2024 2025 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) 2026 { 2027 # ifdef FEAT_TERMINAL 2028 if (term_use_loop()) 2029 // This window is a terminal window, send the mouse event there. 2030 // Set "typed" to FALSE to avoid an endless loop. 2031 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE); 2032 else 2033 # endif 2034 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) 2035 { 2036 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L); 2037 } 2038 else 2039 { 2040 // Don't scroll more than half the window height. 2041 if (curwin->w_height < 6) 2042 { 2043 cap->count1 = curwin->w_height / 2; 2044 if (cap->count1 == 0) 2045 cap->count1 = 1; 2046 } 2047 else 2048 cap->count1 = 3; 2049 cap->count0 = cap->count1; 2050 nv_scroll_line(cap); 2051 } 2052 #ifdef FEAT_PROP_POPUP 2053 if (WIN_IS_POPUP(curwin)) 2054 popup_set_firstline(curwin); 2055 #endif 2056 } 2057 # ifdef FEAT_GUI 2058 else 2059 { 2060 // Horizontal scroll - only allowed when 'wrap' is disabled 2061 if (!curwin->w_p_wrap) 2062 { 2063 int val, step = 6; 2064 2065 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) 2066 step = curwin->w_width; 2067 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step); 2068 if (val < 0) 2069 val = 0; 2070 2071 gui_do_horiz_scroll(val, TRUE); 2072 } 2073 } 2074 # endif 2075 # ifdef FEAT_SYN_HL 2076 if (curwin != old_curwin && curwin->w_p_cul) 2077 redraw_for_cursorline(curwin); 2078 # endif 2079 2080 curwin->w_redr_status = TRUE; 2081 2082 curwin = old_curwin; 2083 curbuf = curwin->w_buffer; 2084 } 2085 2086 /* 2087 * Mouse clicks and drags. 2088 */ 2089 void 2090 nv_mouse(cmdarg_T *cap) 2091 { 2092 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0); 2093 } 2094 2095 /* 2096 * Check if typebuf 'tp' contains a terminal mouse code and returns the 2097 * modifiers found in typebuf in 'modifiers'. 2098 */ 2099 int 2100 check_termcode_mouse( 2101 char_u *tp, 2102 int *slen, 2103 char_u *key_name, 2104 char_u *modifiers_start, 2105 int idx, 2106 int *modifiers) 2107 { 2108 int j; 2109 char_u *p; 2110 # if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \ 2111 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) 2112 char_u bytes[6]; 2113 int num_bytes; 2114 # endif 2115 int mouse_code = 0; // init for GCC 2116 int is_click, is_drag; 2117 int wheel_code = 0; 2118 int current_button; 2119 static int held_button = MOUSE_RELEASE; 2120 static int orig_num_clicks = 1; 2121 static int orig_mouse_code = 0x0; 2122 # ifdef CHECK_DOUBLE_CLICK 2123 static int orig_mouse_col = 0; 2124 static int orig_mouse_row = 0; 2125 static struct timeval orig_mouse_time = {0, 0}; 2126 // time of previous mouse click 2127 struct timeval mouse_time; // time of current mouse click 2128 long timediff; // elapsed time in msec 2129 # endif 2130 2131 is_click = is_drag = FALSE; 2132 2133 # if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \ 2134 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) 2135 if (key_name[0] == KS_MOUSE 2136 # ifdef FEAT_MOUSE_GPM 2137 || key_name[0] == KS_GPM_MOUSE 2138 # endif 2139 ) 2140 { 2141 /* 2142 * For xterm we get "<t_mouse>scr", where 2143 * s == encoded button state: 2144 * 0x20 = left button down 2145 * 0x21 = middle button down 2146 * 0x22 = right button down 2147 * 0x23 = any button release 2148 * 0x60 = button 4 down (scroll wheel down) 2149 * 0x61 = button 5 down (scroll wheel up) 2150 * add 0x04 for SHIFT 2151 * add 0x08 for ALT 2152 * add 0x10 for CTRL 2153 * add 0x20 for mouse drag (0x40 is drag with left button) 2154 * add 0x40 for mouse move (0x80 is move, 0x81 too) 2155 * 0x43 (drag + release) is also move 2156 * c == column + ' ' + 1 == column + 33 2157 * r == row + ' ' + 1 == row + 33 2158 * 2159 * The coordinates are passed on through global variables. 2160 * Ugly, but this avoids trouble with mouse clicks at an 2161 * unexpected moment and allows for mapping them. 2162 */ 2163 for (;;) 2164 { 2165 # ifdef FEAT_GUI 2166 if (gui.in_use) 2167 { 2168 // GUI uses more bits for columns > 223 2169 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5); 2170 if (num_bytes == -1) // not enough coordinates 2171 return -1; 2172 mouse_code = bytes[0]; 2173 mouse_col = 128 * (bytes[1] - ' ' - 1) 2174 + bytes[2] - ' ' - 1; 2175 mouse_row = 128 * (bytes[3] - ' ' - 1) 2176 + bytes[4] - ' ' - 1; 2177 } 2178 else 2179 # endif 2180 { 2181 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3); 2182 if (num_bytes == -1) // not enough coordinates 2183 return -1; 2184 mouse_code = bytes[0]; 2185 mouse_col = bytes[1] - ' ' - 1; 2186 mouse_row = bytes[2] - ' ' - 1; 2187 } 2188 *slen += num_bytes; 2189 2190 // If the following bytes is also a mouse code and it has 2191 // the same code, dump this one and get the next. This 2192 // makes dragging a whole lot faster. 2193 # ifdef FEAT_GUI 2194 if (gui.in_use) 2195 j = 3; 2196 else 2197 # endif 2198 j = get_termcode_len(idx); 2199 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0 2200 && tp[*slen + j] == mouse_code 2201 && tp[*slen + j + 1] != NUL 2202 && tp[*slen + j + 2] != NUL 2203 # ifdef FEAT_GUI 2204 && (!gui.in_use 2205 || (tp[*slen + j + 3] != NUL 2206 && tp[*slen + j + 4] != NUL)) 2207 # endif 2208 ) 2209 *slen += j; 2210 else 2211 break; 2212 } 2213 } 2214 2215 if (key_name[0] == KS_URXVT_MOUSE 2216 || key_name[0] == KS_SGR_MOUSE 2217 || key_name[0] == KS_SGR_MOUSE_RELEASE) 2218 { 2219 // URXVT 1015 mouse reporting mode: 2220 // Almost identical to xterm mouse mode, except the values 2221 // are decimal instead of bytes. 2222 // 2223 // \033[%d;%d;%dM 2224 // ^-- row 2225 // ^----- column 2226 // ^-------- code 2227 // 2228 // SGR 1006 mouse reporting mode: 2229 // Almost identical to xterm mouse mode, except the values 2230 // are decimal instead of bytes. 2231 // 2232 // \033[<%d;%d;%dM 2233 // ^-- row 2234 // ^----- column 2235 // ^-------- code 2236 // 2237 // \033[<%d;%d;%dm : mouse release event 2238 // ^-- row 2239 // ^----- column 2240 // ^-------- code 2241 p = modifiers_start; 2242 if (p == NULL) 2243 return -1; 2244 2245 mouse_code = getdigits(&p); 2246 if (*p++ != ';') 2247 return -1; 2248 2249 // when mouse reporting is SGR, add 32 to mouse code 2250 if (key_name[0] == KS_SGR_MOUSE 2251 || key_name[0] == KS_SGR_MOUSE_RELEASE) 2252 mouse_code += 32; 2253 2254 if (key_name[0] == KS_SGR_MOUSE_RELEASE) 2255 mouse_code |= MOUSE_RELEASE; 2256 2257 mouse_col = getdigits(&p) - 1; 2258 if (*p++ != ';') 2259 return -1; 2260 2261 mouse_row = getdigits(&p) - 1; 2262 2263 // The modifiers were the mouse coordinates, not the 2264 // modifier keys (alt/shift/ctrl/meta) state. 2265 *modifiers = 0; 2266 } 2267 2268 if (key_name[0] == KS_MOUSE 2269 # ifdef FEAT_MOUSE_GPM 2270 || key_name[0] == KS_GPM_MOUSE 2271 # endif 2272 # ifdef FEAT_MOUSE_URXVT 2273 || key_name[0] == KS_URXVT_MOUSE 2274 # endif 2275 || key_name[0] == KS_SGR_MOUSE 2276 || key_name[0] == KS_SGR_MOUSE_RELEASE) 2277 { 2278 # if !defined(MSWIN) 2279 /* 2280 * Handle mouse events. 2281 * Recognize the xterm mouse wheel, but not in the GUI, the 2282 * Linux console with GPM and the MS-DOS or Win32 console 2283 * (multi-clicks use >= 0x60). 2284 */ 2285 if (mouse_code >= MOUSEWHEEL_LOW 2286 # ifdef FEAT_GUI 2287 && !gui.in_use 2288 # endif 2289 # ifdef FEAT_MOUSE_GPM 2290 && key_name[0] != KS_GPM_MOUSE 2291 # endif 2292 ) 2293 { 2294 # if defined(UNIX) 2295 if (use_xterm_mouse() > 1 && mouse_code >= 0x80) 2296 // mouse-move event, using MOUSE_DRAG works 2297 mouse_code = MOUSE_DRAG; 2298 else 2299 # endif 2300 // Keep the mouse_code before it's changed, so that we 2301 // remember that it was a mouse wheel click. 2302 wheel_code = mouse_code; 2303 } 2304 # ifdef FEAT_MOUSE_XTERM 2305 else if (held_button == MOUSE_RELEASE 2306 # ifdef FEAT_GUI 2307 && !gui.in_use 2308 # endif 2309 && (mouse_code == 0x23 || mouse_code == 0x24 2310 || mouse_code == 0x40 || mouse_code == 0x41)) 2311 { 2312 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel. 2313 // And 0x40 and 0x41 are used by some xterm emulator. 2314 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23) 2315 + MOUSEWHEEL_LOW; 2316 } 2317 # endif 2318 2319 # if defined(UNIX) 2320 else if (use_xterm_mouse() > 1) 2321 { 2322 if (mouse_code & MOUSE_DRAG_XTERM) 2323 mouse_code |= MOUSE_DRAG; 2324 } 2325 # endif 2326 # ifdef FEAT_XCLIPBOARD 2327 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK)) 2328 { 2329 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE) 2330 stop_xterm_trace(); 2331 else 2332 start_xterm_trace(mouse_code); 2333 } 2334 # endif 2335 # endif 2336 } 2337 # endif // !UNIX || FEAT_MOUSE_XTERM 2338 # ifdef FEAT_MOUSE_NET 2339 if (key_name[0] == KS_NETTERM_MOUSE) 2340 { 2341 int mc, mr; 2342 2343 // expect a rather limited sequence like: balancing { 2344 // \033}6,45\r 2345 // '6' is the row, 45 is the column 2346 p = tp + *slen; 2347 mr = getdigits(&p); 2348 if (*p++ != ',') 2349 return -1; 2350 mc = getdigits(&p); 2351 if (*p++ != '\r') 2352 return -1; 2353 2354 mouse_col = mc - 1; 2355 mouse_row = mr - 1; 2356 mouse_code = MOUSE_LEFT; 2357 *slen += (int)(p - (tp + *slen)); 2358 } 2359 # endif // FEAT_MOUSE_NET 2360 # ifdef FEAT_MOUSE_JSB 2361 if (key_name[0] == KS_JSBTERM_MOUSE) 2362 { 2363 int mult, val, iter, button, status; 2364 2365 /* 2366 * JSBTERM Input Model 2367 * \033[0~zw uniq escape sequence 2368 * (L-x) Left button pressed - not pressed x not reporting 2369 * (M-x) Middle button pressed - not pressed x not reporting 2370 * (R-x) Right button pressed - not pressed x not reporting 2371 * (SDmdu) Single , Double click, m mouse move d button down 2372 * u button up 2373 * ### X cursor position padded to 3 digits 2374 * ### Y cursor position padded to 3 digits 2375 * (s-x) SHIFT key pressed - not pressed x not reporting 2376 * (c-x) CTRL key pressed - not pressed x not reporting 2377 * \033\\ terminating sequence 2378 */ 2379 p = tp + *slen; 2380 button = mouse_code = 0; 2381 switch (*p++) 2382 { 2383 case 'L': button = 1; break; 2384 case '-': break; 2385 case 'x': break; // ignore sequence 2386 default: return -1; // Unknown Result 2387 } 2388 switch (*p++) 2389 { 2390 case 'M': button |= 2; break; 2391 case '-': break; 2392 case 'x': break; // ignore sequence 2393 default: return -1; // Unknown Result 2394 } 2395 switch (*p++) 2396 { 2397 case 'R': button |= 4; break; 2398 case '-': break; 2399 case 'x': break; // ignore sequence 2400 default: return -1; // Unknown Result 2401 } 2402 status = *p++; 2403 for (val = 0, mult = 100, iter = 0; iter < 3; iter++, 2404 mult /= 10, p++) 2405 if (*p >= '0' && *p <= '9') 2406 val += (*p - '0') * mult; 2407 else 2408 return -1; 2409 mouse_col = val; 2410 for (val = 0, mult = 100, iter = 0; iter < 3; iter++, 2411 mult /= 10, p++) 2412 if (*p >= '0' && *p <= '9') 2413 val += (*p - '0') * mult; 2414 else 2415 return -1; 2416 mouse_row = val; 2417 switch (*p++) 2418 { 2419 case 's': button |= 8; break; // SHIFT key Pressed 2420 case '-': break; // Not Pressed 2421 case 'x': break; // Not Reporting 2422 default: return -1; // Unknown Result 2423 } 2424 switch (*p++) 2425 { 2426 case 'c': button |= 16; break; // CTRL key Pressed 2427 case '-': break; // Not Pressed 2428 case 'x': break; // Not Reporting 2429 default: return -1; // Unknown Result 2430 } 2431 if (*p++ != '\033') 2432 return -1; 2433 if (*p++ != '\\') 2434 return -1; 2435 switch (status) 2436 { 2437 case 'D': // Double Click 2438 case 'S': // Single Click 2439 if (button & 1) mouse_code |= MOUSE_LEFT; 2440 if (button & 2) mouse_code |= MOUSE_MIDDLE; 2441 if (button & 4) mouse_code |= MOUSE_RIGHT; 2442 if (button & 8) mouse_code |= MOUSE_SHIFT; 2443 if (button & 16) mouse_code |= MOUSE_CTRL; 2444 break; 2445 case 'm': // Mouse move 2446 if (button & 1) mouse_code |= MOUSE_LEFT; 2447 if (button & 2) mouse_code |= MOUSE_MIDDLE; 2448 if (button & 4) mouse_code |= MOUSE_RIGHT; 2449 if (button & 8) mouse_code |= MOUSE_SHIFT; 2450 if (button & 16) mouse_code |= MOUSE_CTRL; 2451 if ((button & 7) != 0) 2452 { 2453 held_button = mouse_code; 2454 mouse_code |= MOUSE_DRAG; 2455 } 2456 is_drag = TRUE; 2457 showmode(); 2458 break; 2459 case 'd': // Button Down 2460 if (button & 1) mouse_code |= MOUSE_LEFT; 2461 if (button & 2) mouse_code |= MOUSE_MIDDLE; 2462 if (button & 4) mouse_code |= MOUSE_RIGHT; 2463 if (button & 8) mouse_code |= MOUSE_SHIFT; 2464 if (button & 16) mouse_code |= MOUSE_CTRL; 2465 break; 2466 case 'u': // Button Up 2467 if (button & 1) 2468 mouse_code |= MOUSE_LEFT | MOUSE_RELEASE; 2469 if (button & 2) 2470 mouse_code |= MOUSE_MIDDLE | MOUSE_RELEASE; 2471 if (button & 4) 2472 mouse_code |= MOUSE_RIGHT | MOUSE_RELEASE; 2473 if (button & 8) 2474 mouse_code |= MOUSE_SHIFT; 2475 if (button & 16) 2476 mouse_code |= MOUSE_CTRL; 2477 break; 2478 default: return -1; // Unknown Result 2479 } 2480 2481 *slen += (p - (tp + *slen)); 2482 } 2483 # endif // FEAT_MOUSE_JSB 2484 # ifdef FEAT_MOUSE_DEC 2485 if (key_name[0] == KS_DEC_MOUSE) 2486 { 2487 /* 2488 * The DEC Locator Input Model 2489 * Netterm delivers the code sequence: 2490 * \033[2;4;24;80&w (left button down) 2491 * \033[3;0;24;80&w (left button up) 2492 * \033[6;1;24;80&w (right button down) 2493 * \033[7;0;24;80&w (right button up) 2494 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w 2495 * Pe is the event code 2496 * Pb is the button code 2497 * Pr is the row coordinate 2498 * Pc is the column coordinate 2499 * Pp is the third coordinate (page number) 2500 * Pe, the event code indicates what event caused this report 2501 * The following event codes are defined: 2502 * 0 - request, the terminal received an explicit request 2503 * for a locator report, but the locator is unavailable 2504 * 1 - request, the terminal received an explicit request 2505 * for a locator report 2506 * 2 - left button down 2507 * 3 - left button up 2508 * 4 - middle button down 2509 * 5 - middle button up 2510 * 6 - right button down 2511 * 7 - right button up 2512 * 8 - fourth button down 2513 * 9 - fourth button up 2514 * 10 - locator outside filter rectangle 2515 * Pb, the button code, ASCII decimal 0-15 indicating which 2516 * buttons are down if any. The state of the four buttons 2517 * on the locator correspond to the low four bits of the 2518 * decimal value, 2519 * "1" means button depressed 2520 * 0 - no buttons down, 2521 * 1 - right, 2522 * 2 - middle, 2523 * 4 - left, 2524 * 8 - fourth 2525 * Pr is the row coordinate of the locator position in the page, 2526 * encoded as an ASCII decimal value. 2527 * If Pr is omitted, the locator position is undefined 2528 * (outside the terminal window for example). 2529 * Pc is the column coordinate of the locator position in the 2530 * page, encoded as an ASCII decimal value. 2531 * If Pc is omitted, the locator position is undefined 2532 * (outside the terminal window for example). 2533 * Pp is the page coordinate of the locator position 2534 * encoded as an ASCII decimal value. 2535 * The page coordinate may be omitted if the locator is on 2536 * page one (the default). We ignore it anyway. 2537 */ 2538 int Pe, Pb, Pr, Pc; 2539 2540 p = tp + *slen; 2541 2542 // get event status 2543 Pe = getdigits(&p); 2544 if (*p++ != ';') 2545 return -1; 2546 2547 // get button status 2548 Pb = getdigits(&p); 2549 if (*p++ != ';') 2550 return -1; 2551 2552 // get row status 2553 Pr = getdigits(&p); 2554 if (*p++ != ';') 2555 return -1; 2556 2557 // get column status 2558 Pc = getdigits(&p); 2559 2560 // the page parameter is optional 2561 if (*p == ';') 2562 { 2563 p++; 2564 (void)getdigits(&p); 2565 } 2566 if (*p++ != '&') 2567 return -1; 2568 if (*p++ != 'w') 2569 return -1; 2570 2571 mouse_code = 0; 2572 switch (Pe) 2573 { 2574 case 0: return -1; // position request while unavailable 2575 case 1: // a response to a locator position request includes 2576 // the status of all buttons 2577 Pb &= 7; // mask off and ignore fourth button 2578 if (Pb & 4) 2579 mouse_code = MOUSE_LEFT; 2580 if (Pb & 2) 2581 mouse_code = MOUSE_MIDDLE; 2582 if (Pb & 1) 2583 mouse_code = MOUSE_RIGHT; 2584 if (Pb) 2585 { 2586 held_button = mouse_code; 2587 mouse_code |= MOUSE_DRAG; 2588 WantQueryMouse = TRUE; 2589 } 2590 is_drag = TRUE; 2591 showmode(); 2592 break; 2593 case 2: mouse_code = MOUSE_LEFT; 2594 WantQueryMouse = TRUE; 2595 break; 2596 case 3: mouse_code = MOUSE_RELEASE | MOUSE_LEFT; 2597 break; 2598 case 4: mouse_code = MOUSE_MIDDLE; 2599 WantQueryMouse = TRUE; 2600 break; 2601 case 5: mouse_code = MOUSE_RELEASE | MOUSE_MIDDLE; 2602 break; 2603 case 6: mouse_code = MOUSE_RIGHT; 2604 WantQueryMouse = TRUE; 2605 break; 2606 case 7: mouse_code = MOUSE_RELEASE | MOUSE_RIGHT; 2607 break; 2608 case 8: return -1; // fourth button down 2609 case 9: return -1; // fourth button up 2610 case 10: return -1; // mouse outside of filter rectangle 2611 default: return -1; // should never occur 2612 } 2613 2614 mouse_col = Pc - 1; 2615 mouse_row = Pr - 1; 2616 2617 *slen += (int)(p - (tp + *slen)); 2618 } 2619 # endif // FEAT_MOUSE_DEC 2620 # ifdef FEAT_MOUSE_PTERM 2621 if (key_name[0] == KS_PTERM_MOUSE) 2622 { 2623 int button, num_clicks, action; 2624 2625 p = tp + *slen; 2626 2627 action = getdigits(&p); 2628 if (*p++ != ';') 2629 return -1; 2630 2631 mouse_row = getdigits(&p); 2632 if (*p++ != ';') 2633 return -1; 2634 mouse_col = getdigits(&p); 2635 if (*p++ != ';') 2636 return -1; 2637 2638 button = getdigits(&p); 2639 mouse_code = 0; 2640 2641 switch (button) 2642 { 2643 case 4: mouse_code = MOUSE_LEFT; break; 2644 case 1: mouse_code = MOUSE_RIGHT; break; 2645 case 2: mouse_code = MOUSE_MIDDLE; break; 2646 default: return -1; 2647 } 2648 2649 switch (action) 2650 { 2651 case 31: // Initial press 2652 if (*p++ != ';') 2653 return -1; 2654 2655 num_clicks = getdigits(&p); // Not used 2656 break; 2657 2658 case 32: // Release 2659 mouse_code |= MOUSE_RELEASE; 2660 break; 2661 2662 case 33: // Drag 2663 held_button = mouse_code; 2664 mouse_code |= MOUSE_DRAG; 2665 break; 2666 2667 default: 2668 return -1; 2669 } 2670 2671 if (*p++ != 't') 2672 return -1; 2673 2674 *slen += (p - (tp + *slen)); 2675 } 2676 # endif // FEAT_MOUSE_PTERM 2677 2678 // Interpret the mouse code 2679 current_button = (mouse_code & MOUSE_CLICK_MASK); 2680 if (current_button == MOUSE_RELEASE 2681 # ifdef FEAT_MOUSE_XTERM 2682 && wheel_code == 0 2683 # endif 2684 ) 2685 { 2686 /* 2687 * If we get a mouse drag or release event when 2688 * there is no mouse button held down (held_button == 2689 * MOUSE_RELEASE), produce a K_IGNORE below. 2690 * (can happen when you hold down two buttons 2691 * and then let them go, or click in the menu bar, but not 2692 * on a menu, and drag into the text). 2693 */ 2694 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG) 2695 is_drag = TRUE; 2696 current_button = held_button; 2697 } 2698 else if (wheel_code == 0) 2699 { 2700 # ifdef CHECK_DOUBLE_CLICK 2701 # ifdef FEAT_MOUSE_GPM 2702 /* 2703 * Only for Unix, when GUI not active, we handle 2704 * multi-clicks here, but not for GPM mouse events. 2705 */ 2706 # ifdef FEAT_GUI 2707 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use) 2708 # else 2709 if (key_name[0] != KS_GPM_MOUSE) 2710 # endif 2711 # else 2712 # ifdef FEAT_GUI 2713 if (!gui.in_use) 2714 # endif 2715 # endif 2716 { 2717 /* 2718 * Compute the time elapsed since the previous mouse click. 2719 */ 2720 gettimeofday(&mouse_time, NULL); 2721 if (orig_mouse_time.tv_sec == 0) 2722 { 2723 /* 2724 * Avoid computing the difference between mouse_time 2725 * and orig_mouse_time for the first click, as the 2726 * difference would be huge and would cause 2727 * multiplication overflow. 2728 */ 2729 timediff = p_mouset; 2730 } 2731 else 2732 timediff = time_diff_ms(&orig_mouse_time, &mouse_time); 2733 orig_mouse_time = mouse_time; 2734 if (mouse_code == orig_mouse_code 2735 && timediff < p_mouset 2736 && orig_num_clicks != 4 2737 && orig_mouse_col == mouse_col 2738 && orig_mouse_row == mouse_row 2739 && (is_mouse_topline(curwin) 2740 // Double click in tab pages line also works 2741 // when window contents changes. 2742 || (mouse_row == 0 && firstwin->w_winrow > 0)) 2743 ) 2744 ++orig_num_clicks; 2745 else 2746 orig_num_clicks = 1; 2747 orig_mouse_col = mouse_col; 2748 orig_mouse_row = mouse_row; 2749 set_mouse_topline(curwin); 2750 } 2751 # if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM) 2752 else 2753 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code); 2754 # endif 2755 # else 2756 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code); 2757 # endif 2758 is_click = TRUE; 2759 orig_mouse_code = mouse_code; 2760 } 2761 if (!is_drag) 2762 held_button = mouse_code & MOUSE_CLICK_MASK; 2763 2764 /* 2765 * Translate the actual mouse event into a pseudo mouse event. 2766 * First work out what modifiers are to be used. 2767 */ 2768 if (orig_mouse_code & MOUSE_SHIFT) 2769 *modifiers |= MOD_MASK_SHIFT; 2770 if (orig_mouse_code & MOUSE_CTRL) 2771 *modifiers |= MOD_MASK_CTRL; 2772 if (orig_mouse_code & MOUSE_ALT) 2773 *modifiers |= MOD_MASK_ALT; 2774 if (orig_num_clicks == 2) 2775 *modifiers |= MOD_MASK_2CLICK; 2776 else if (orig_num_clicks == 3) 2777 *modifiers |= MOD_MASK_3CLICK; 2778 else if (orig_num_clicks == 4) 2779 *modifiers |= MOD_MASK_4CLICK; 2780 2781 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets 2782 // added, then it's not mouse up/down. 2783 key_name[0] = KS_EXTRA; 2784 if (wheel_code != 0 2785 && (wheel_code & MOUSE_RELEASE) != MOUSE_RELEASE) 2786 { 2787 if (wheel_code & MOUSE_CTRL) 2788 *modifiers |= MOD_MASK_CTRL; 2789 if (wheel_code & MOUSE_ALT) 2790 *modifiers |= MOD_MASK_ALT; 2791 key_name[1] = (wheel_code & 1) 2792 ? (int)KE_MOUSEUP : (int)KE_MOUSEDOWN; 2793 held_button = MOUSE_RELEASE; 2794 } 2795 else 2796 key_name[1] = get_pseudo_mouse_code(current_button, 2797 is_click, is_drag); 2798 2799 // Make sure the mouse position is valid. Some terminals may 2800 // return weird values. 2801 if (mouse_col >= Columns) 2802 mouse_col = Columns - 1; 2803 if (mouse_row >= Rows) 2804 mouse_row = Rows - 1; 2805 2806 return 0; 2807 } 2808 2809 // Functions also used for popup windows. 2810 2811 /* 2812 * Compute the buffer line position from the screen position "rowp" / "colp" in 2813 * window "win". 2814 * "plines_cache" can be NULL (no cache) or an array with "win->w_height" 2815 * entries that caches the plines_win() result from a previous call. Entry is 2816 * zero if not computed yet. There must be no text or setting changes since 2817 * the entry is put in the cache. 2818 * Returns TRUE if the position is below the last line. 2819 */ 2820 int 2821 mouse_comp_pos( 2822 win_T *win, 2823 int *rowp, 2824 int *colp, 2825 linenr_T *lnump, 2826 int *plines_cache) 2827 { 2828 int col = *colp; 2829 int row = *rowp; 2830 linenr_T lnum; 2831 int retval = FALSE; 2832 int off; 2833 int count; 2834 2835 #ifdef FEAT_RIGHTLEFT 2836 if (win->w_p_rl) 2837 col = win->w_width - 1 - col; 2838 #endif 2839 2840 lnum = win->w_topline; 2841 2842 while (row > 0) 2843 { 2844 int cache_idx = lnum - win->w_topline; 2845 2846 if (plines_cache != NULL && plines_cache[cache_idx] > 0) 2847 count = plines_cache[cache_idx]; 2848 else 2849 { 2850 #ifdef FEAT_DIFF 2851 // Don't include filler lines in "count" 2852 if (win->w_p_diff 2853 # ifdef FEAT_FOLDING 2854 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL) 2855 # endif 2856 ) 2857 { 2858 if (lnum == win->w_topline) 2859 row -= win->w_topfill; 2860 else 2861 row -= diff_check_fill(win, lnum); 2862 count = plines_win_nofill(win, lnum, TRUE); 2863 } 2864 else 2865 #endif 2866 count = plines_win(win, lnum, TRUE); 2867 if (plines_cache != NULL) 2868 plines_cache[cache_idx] = count; 2869 } 2870 if (count > row) 2871 break; // Position is in this buffer line. 2872 #ifdef FEAT_FOLDING 2873 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL); 2874 #endif 2875 if (lnum == win->w_buffer->b_ml.ml_line_count) 2876 { 2877 retval = TRUE; 2878 break; // past end of file 2879 } 2880 row -= count; 2881 ++lnum; 2882 } 2883 2884 if (!retval) 2885 { 2886 // Compute the column without wrapping. 2887 off = win_col_off(win) - win_col_off2(win); 2888 if (col < off) 2889 col = off; 2890 col += row * (win->w_width - off); 2891 // add skip column (for long wrapping line) 2892 col += win->w_skipcol; 2893 } 2894 2895 if (!win->w_p_wrap) 2896 col += win->w_leftcol; 2897 2898 // skip line number and fold column in front of the line 2899 col -= win_col_off(win); 2900 if (col < 0) 2901 { 2902 #ifdef FEAT_NETBEANS_INTG 2903 netbeans_gutter_click(lnum); 2904 #endif 2905 col = 0; 2906 } 2907 2908 *colp = col; 2909 *rowp = row; 2910 *lnump = lnum; 2911 return retval; 2912 } 2913 2914 /* 2915 * Find the window at screen position "*rowp" and "*colp". The positions are 2916 * updated to become relative to the top-left of the window. 2917 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL 2918 * is returned. When "popup" is IGNORE_POPUP then do not even check popup 2919 * windows. 2920 * Returns NULL when something is wrong. 2921 */ 2922 win_T * 2923 mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED) 2924 { 2925 frame_T *fp; 2926 win_T *wp; 2927 2928 #ifdef FEAT_PROP_POPUP 2929 win_T *pwp = NULL; 2930 2931 if (popup != IGNORE_POPUP) 2932 { 2933 popup_reset_handled(POPUP_HANDLED_1); 2934 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL) 2935 { 2936 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp) 2937 && *colp >= wp->w_wincol 2938 && *colp < wp->w_wincol + popup_width(wp)) 2939 pwp = wp; 2940 } 2941 if (pwp != NULL) 2942 { 2943 if (popup == FAIL_POPUP) 2944 return NULL; 2945 *rowp -= pwp->w_winrow; 2946 *colp -= pwp->w_wincol; 2947 return pwp; 2948 } 2949 } 2950 #endif 2951 2952 fp = topframe; 2953 *rowp -= firstwin->w_winrow; 2954 for (;;) 2955 { 2956 if (fp->fr_layout == FR_LEAF) 2957 break; 2958 if (fp->fr_layout == FR_ROW) 2959 { 2960 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next) 2961 { 2962 if (*colp < fp->fr_width) 2963 break; 2964 *colp -= fp->fr_width; 2965 } 2966 } 2967 else // fr_layout == FR_COL 2968 { 2969 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next) 2970 { 2971 if (*rowp < fp->fr_height) 2972 break; 2973 *rowp -= fp->fr_height; 2974 } 2975 } 2976 } 2977 // When using a timer that closes a window the window might not actually 2978 // exist. 2979 FOR_ALL_WINDOWS(wp) 2980 if (wp == fp->fr_win) 2981 { 2982 #ifdef FEAT_MENU 2983 *rowp -= wp->w_winbar_height; 2984 #endif 2985 return wp; 2986 } 2987 return NULL; 2988 } 2989 2990 #if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \ 2991 || defined(PROTO) 2992 /* 2993 * Convert a virtual (screen) column to a character column. 2994 * The first column is one. 2995 */ 2996 int 2997 vcol2col(win_T *wp, linenr_T lnum, int vcol) 2998 { 2999 // try to advance to the specified column 3000 int count = 0; 3001 char_u *ptr; 3002 char_u *line; 3003 3004 line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE); 3005 while (count < vcol && *ptr != NUL) 3006 { 3007 count += win_lbr_chartabsize(wp, line, ptr, count, NULL); 3008 MB_PTR_ADV(ptr); 3009 } 3010 return (int)(ptr - line); 3011 } 3012 #endif 3013 3014 #if defined(FEAT_EVAL) || defined(PROTO) 3015 void 3016 f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv) 3017 { 3018 dict_T *d; 3019 win_T *wp; 3020 int row = mouse_row; 3021 int col = mouse_col; 3022 varnumber_T winid = 0; 3023 varnumber_T winrow = 0; 3024 varnumber_T wincol = 0; 3025 linenr_T line = 0; 3026 varnumber_T column = 0; 3027 3028 if (rettv_dict_alloc(rettv) != OK) 3029 return; 3030 d = rettv->vval.v_dict; 3031 3032 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1); 3033 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1); 3034 3035 wp = mouse_find_win(&row, &col, FIND_POPUP); 3036 if (wp != NULL) 3037 { 3038 int top_off = 0; 3039 int left_off = 0; 3040 int height = wp->w_height + wp->w_status_height; 3041 3042 #ifdef FEAT_PROP_POPUP 3043 if (WIN_IS_POPUP(wp)) 3044 { 3045 top_off = popup_top_extra(wp); 3046 left_off = popup_left_extra(wp); 3047 height = popup_height(wp); 3048 } 3049 #endif 3050 if (row < height) 3051 { 3052 winid = wp->w_id; 3053 winrow = row + 1; 3054 wincol = col + 1; 3055 row -= top_off; 3056 col -= left_off; 3057 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) 3058 { 3059 char_u *p; 3060 int count; 3061 3062 mouse_comp_pos(wp, &row, &col, &line, NULL); 3063 3064 // limit to text length plus one 3065 p = ml_get_buf(wp->w_buffer, line, FALSE); 3066 count = (int)STRLEN(p); 3067 if (col > count) 3068 col = count; 3069 3070 column = col + 1; 3071 } 3072 } 3073 } 3074 dict_add_number(d, "winid", winid); 3075 dict_add_number(d, "winrow", winrow); 3076 dict_add_number(d, "wincol", wincol); 3077 dict_add_number(d, "line", (varnumber_T)line); 3078 dict_add_number(d, "column", column); 3079 } 3080 #endif 3081