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