1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * GUI/Motif support by Robert Webb 5 * 6 * Do ":help uganda" in Vim to read copying and usage conditions. 7 * Do ":help credits" in Vim to see a list of people who contributed. 8 * See README.txt for an overview of the Vim source code. 9 */ 10 11 #include "vim.h" 12 13 /* Structure containing all the GUI information */ 14 gui_T gui; 15 16 #if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) 17 static void set_guifontwide __ARGS((char_u *font_name)); 18 #endif 19 static void gui_check_pos __ARGS((void)); 20 static void gui_position_components __ARGS((int)); 21 static void gui_outstr __ARGS((char_u *, int)); 22 static int gui_screenchar __ARGS((int off, int flags, guicolor_T fg, guicolor_T bg, int back)); 23 #ifdef HAVE_GTK2 24 static int gui_screenstr __ARGS((int off, int len, int flags, guicolor_T fg, guicolor_T bg, int back)); 25 #endif 26 static void gui_delete_lines __ARGS((int row, int count)); 27 static void gui_insert_lines __ARGS((int row, int count)); 28 static void fill_mouse_coord __ARGS((char_u *p, int col, int row)); 29 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 30 static int gui_has_tabline __ARGS((void)); 31 #endif 32 static void gui_do_scrollbar __ARGS((win_T *wp, int which, int enable)); 33 static colnr_T scroll_line_len __ARGS((linenr_T lnum)); 34 static void gui_update_horiz_scrollbar __ARGS((int)); 35 static void gui_set_fg_color __ARGS((char_u *name)); 36 static void gui_set_bg_color __ARGS((char_u *name)); 37 static win_T *xy2win __ARGS((int x, int y)); 38 39 static int can_update_cursor = TRUE; /* can display the cursor */ 40 41 /* 42 * The Athena scrollbars can move the thumb to after the end of the scrollbar, 43 * this makes the thumb indicate the part of the text that is shown. Motif 44 * can't do this. 45 */ 46 #if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MAC) 47 # define SCROLL_PAST_END 48 #endif 49 50 /* 51 * gui_start -- Called when user wants to start the GUI. 52 * 53 * Careful: This function can be called recursively when there is a ":gui" 54 * command in the .gvimrc file. Only the first call should fork, not the 55 * recursive call. 56 */ 57 void 58 gui_start() 59 { 60 char_u *old_term; 61 #if defined(UNIX) && !defined(__BEOS__) && !defined(MACOS_X) 62 # define MAY_FORK 63 int dofork = TRUE; 64 #endif 65 static int recursive = 0; 66 67 old_term = vim_strsave(T_NAME); 68 69 /* 70 * Set_termname() will call gui_init() to start the GUI. 71 * Set the "starting" flag, to indicate that the GUI will start. 72 * 73 * We don't want to open the GUI shell until after we've read .gvimrc, 74 * otherwise we don't know what font we will use, and hence we don't know 75 * what size the shell should be. So if there are errors in the .gvimrc 76 * file, they will have to go to the terminal: Set full_screen to FALSE. 77 * full_screen will be set to TRUE again by a successful termcapinit(). 78 */ 79 settmode(TMODE_COOK); /* stop RAW mode */ 80 if (full_screen) 81 cursor_on(); /* needed for ":gui" in .vimrc */ 82 gui.starting = TRUE; 83 full_screen = FALSE; 84 85 #ifdef MAY_FORK 86 if (!gui.dofork || vim_strchr(p_go, GO_FORG) || recursive) 87 dofork = FALSE; 88 #endif 89 ++recursive; 90 91 termcapinit((char_u *)"builtin_gui"); 92 gui.starting = recursive - 1; 93 94 if (!gui.in_use) /* failed to start GUI */ 95 { 96 termcapinit(old_term); /* back to old term settings */ 97 settmode(TMODE_RAW); /* restart RAW mode */ 98 #ifdef FEAT_TITLE 99 set_title_defaults(); /* set 'title' and 'icon' again */ 100 #endif 101 } 102 103 vim_free(old_term); 104 105 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11) 106 if (gui.in_use) 107 /* Display error messages in a dialog now. */ 108 display_errors(); 109 #endif 110 111 #if defined(MAY_FORK) && !defined(__QNXNTO__) 112 /* 113 * Quit the current process and continue in the child. 114 * Makes "gvim file" disconnect from the shell it was started in. 115 * Don't do this when Vim was started with "-f" or the 'f' flag is present 116 * in 'guioptions'. 117 */ 118 if (gui.in_use && dofork) 119 { 120 int pipefd[2]; /* pipe between parent and child */ 121 int pipe_error; 122 char dummy; 123 pid_t pid = -1; 124 125 /* Setup a pipe between the child and the parent, so that the parent 126 * knows when the child has done the setsid() call and is allowed to 127 * exit. */ 128 pipe_error = (pipe(pipefd) < 0); 129 pid = fork(); 130 if (pid > 0) /* Parent */ 131 { 132 /* Give the child some time to do the setsid(), otherwise the 133 * exit() may kill the child too (when starting gvim from inside a 134 * gvim). */ 135 if (pipe_error) 136 ui_delay(300L, TRUE); 137 else 138 { 139 /* The read returns when the child closes the pipe (or when 140 * the child dies for some reason). */ 141 close(pipefd[1]); 142 (void)read(pipefd[0], &dummy, (size_t)1); 143 close(pipefd[0]); 144 } 145 146 /* When swapping screens we may need to go to the next line, e.g., 147 * after a hit-enter prompt and using ":gui". */ 148 if (newline_on_exit) 149 mch_errmsg("\r\n"); 150 151 /* 152 * The parent must skip the normal exit() processing, the child 153 * will do it. For example, GTK messes up signals when exiting. 154 */ 155 _exit(0); 156 } 157 158 # if defined(HAVE_SETSID) || defined(HAVE_SETPGID) 159 /* 160 * Change our process group. On some systems/shells a CTRL-C in the 161 * shell where Vim was started would otherwise kill gvim! 162 */ 163 if (pid == 0) /* child */ 164 # if defined(HAVE_SETSID) 165 (void)setsid(); 166 # else 167 (void)setpgid(0, 0); 168 # endif 169 # endif 170 if (!pipe_error) 171 { 172 close(pipefd[0]); 173 close(pipefd[1]); 174 } 175 176 # if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 177 /* Tell the session manager our new PID */ 178 gui_mch_forked(); 179 # endif 180 } 181 #else 182 # if defined(__QNXNTO__) 183 if (gui.in_use && dofork) 184 procmgr_daemon(0, PROCMGR_DAEMON_KEEPUMASK | PROCMGR_DAEMON_NOCHDIR | 185 PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL); 186 # endif 187 #endif 188 189 #ifdef FEAT_AUTOCMD 190 /* If the GUI started successfully, trigger the GUIEnter event */ 191 if (gui.in_use) 192 apply_autocmds(EVENT_GUIENTER, NULL, NULL, FALSE, curbuf); 193 #endif 194 195 --recursive; 196 } 197 198 /* 199 * Call this when vim starts up, whether or not the GUI is started 200 */ 201 void 202 gui_prepare(argc, argv) 203 int *argc; 204 char **argv; 205 { 206 gui.in_use = FALSE; /* No GUI yet (maybe later) */ 207 gui.starting = FALSE; /* No GUI yet (maybe later) */ 208 gui_mch_prepare(argc, argv); 209 } 210 211 /* 212 * Try initializing the GUI and check if it can be started. 213 * Used from main() to check early if "vim -g" can start the GUI. 214 * Used from gui_init() to prepare for starting the GUI. 215 * Returns FAIL or OK. 216 */ 217 int 218 gui_init_check() 219 { 220 static int result = MAYBE; 221 222 if (result != MAYBE) 223 { 224 if (result == FAIL) 225 EMSG(_("E229: Cannot start the GUI")); 226 return result; 227 } 228 229 gui.shell_created = FALSE; 230 gui.dying = FALSE; 231 gui.in_focus = TRUE; /* so the guicursor setting works */ 232 gui.dragged_sb = SBAR_NONE; 233 gui.dragged_wp = NULL; 234 gui.pointer_hidden = FALSE; 235 gui.col = 0; 236 gui.row = 0; 237 gui.num_cols = Columns; 238 gui.num_rows = Rows; 239 240 gui.cursor_is_valid = FALSE; 241 gui.scroll_region_top = 0; 242 gui.scroll_region_bot = Rows - 1; 243 gui.scroll_region_left = 0; 244 gui.scroll_region_right = Columns - 1; 245 gui.highlight_mask = HL_NORMAL; 246 gui.char_width = 1; 247 gui.char_height = 1; 248 gui.char_ascent = 0; 249 gui.border_width = 0; 250 251 gui.norm_font = NOFONT; 252 #ifndef HAVE_GTK2 253 gui.bold_font = NOFONT; 254 gui.ital_font = NOFONT; 255 gui.boldital_font = NOFONT; 256 # ifdef FEAT_XFONTSET 257 gui.fontset = NOFONTSET; 258 # endif 259 #endif 260 261 #ifdef FEAT_MENU 262 # ifndef HAVE_GTK2 263 # ifdef FONTSET_ALWAYS 264 gui.menu_fontset = NOFONTSET; 265 # else 266 gui.menu_font = NOFONT; 267 # endif 268 # endif 269 gui.menu_is_active = TRUE; /* default: include menu */ 270 # ifndef FEAT_GUI_GTK 271 gui.menu_height = MENU_DEFAULT_HEIGHT; 272 gui.menu_width = 0; 273 # endif 274 #endif 275 #if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) 276 gui.toolbar_height = 0; 277 #endif 278 #if defined(FEAT_FOOTER) && defined(FEAT_GUI_MOTIF) 279 gui.footer_height = 0; 280 #endif 281 #ifdef FEAT_BEVAL_TIP 282 gui.tooltip_fontset = NOFONTSET; 283 #endif 284 285 gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH; 286 gui.prev_wrap = -1; 287 288 #ifdef ALWAYS_USE_GUI 289 result = OK; 290 #else 291 result = gui_mch_init_check(); 292 #endif 293 return result; 294 } 295 296 /* 297 * This is the call which starts the GUI. 298 */ 299 void 300 gui_init() 301 { 302 win_T *wp; 303 static int recursive = 0; 304 305 /* 306 * It's possible to use ":gui" in a .gvimrc file. The first halve of this 307 * function will then be executed at the first call, the rest by the 308 * recursive call. This allow the shell to be opened halfway reading a 309 * gvimrc file. 310 */ 311 if (!recursive) 312 { 313 ++recursive; 314 315 clip_init(TRUE); 316 317 /* If can't initialize, don't try doing the rest */ 318 if (gui_init_check() == FAIL) 319 { 320 --recursive; 321 clip_init(FALSE); 322 return; 323 } 324 325 /* 326 * Reset 'paste'. It's useful in the terminal, but not in the GUI. It 327 * breaks the Paste toolbar button. 328 */ 329 set_option_value((char_u *)"paste", 0L, NULL, 0); 330 331 /* 332 * Set up system-wide default menus. 333 */ 334 #if defined(SYS_MENU_FILE) && defined(FEAT_MENU) 335 if (vim_strchr(p_go, GO_NOSYSMENU) == NULL) 336 { 337 sys_menu = TRUE; 338 do_source((char_u *)SYS_MENU_FILE, FALSE, FALSE); 339 sys_menu = FALSE; 340 } 341 #endif 342 343 /* 344 * Switch on the mouse by default, unless the user changed it already. 345 * This can then be changed in the .gvimrc. 346 */ 347 if (!option_was_set((char_u *)"mouse")) 348 set_string_option_direct((char_u *)"mouse", -1, 349 (char_u *)"a", OPT_FREE, SID_NONE); 350 351 /* 352 * If -U option given, use only the initializations from that file and 353 * nothing else. Skip all initializations for "-U NONE" or "-u NORC". 354 */ 355 if (use_gvimrc != NULL) 356 { 357 if (STRCMP(use_gvimrc, "NONE") != 0 358 && STRCMP(use_gvimrc, "NORC") != 0 359 && do_source(use_gvimrc, FALSE, FALSE) != OK) 360 EMSG2(_("E230: Cannot read from \"%s\""), use_gvimrc); 361 } 362 else 363 { 364 /* 365 * Get system wide defaults for gvim, only when file name defined. 366 */ 367 #ifdef SYS_GVIMRC_FILE 368 do_source((char_u *)SYS_GVIMRC_FILE, FALSE, FALSE); 369 #endif 370 371 /* 372 * Try to read GUI initialization commands from the following 373 * places: 374 * - environment variable GVIMINIT 375 * - the user gvimrc file (~/.gvimrc) 376 * - the second user gvimrc file ($VIM/.gvimrc for Dos) 377 * - the third user gvimrc file ($VIM/.gvimrc for Amiga) 378 * The first that exists is used, the rest is ignored. 379 */ 380 if (process_env((char_u *)"GVIMINIT", FALSE) == FAIL 381 && do_source((char_u *)USR_GVIMRC_FILE, TRUE, TRUE) == FAIL 382 #ifdef USR_GVIMRC_FILE2 383 && do_source((char_u *)USR_GVIMRC_FILE2, TRUE, TRUE) == FAIL 384 #endif 385 ) 386 { 387 #ifdef USR_GVIMRC_FILE3 388 (void)do_source((char_u *)USR_GVIMRC_FILE3, TRUE, TRUE); 389 #endif 390 } 391 392 /* 393 * Read initialization commands from ".gvimrc" in current 394 * directory. This is only done if the 'exrc' option is set. 395 * Because of security reasons we disallow shell and write 396 * commands now, except for unix if the file is owned by the user 397 * or 'secure' option has been reset in environment of global 398 * ".gvimrc". 399 * Only do this if GVIMRC_FILE is not the same as USR_GVIMRC_FILE, 400 * USR_GVIMRC_FILE2, USR_GVIMRC_FILE3 or SYS_GVIMRC_FILE. 401 */ 402 if (p_exrc) 403 { 404 #ifdef UNIX 405 { 406 struct stat s; 407 408 /* if ".gvimrc" file is not owned by user, set 'secure' 409 * mode */ 410 if (mch_stat(GVIMRC_FILE, &s) || s.st_uid != getuid()) 411 secure = p_secure; 412 } 413 #else 414 secure = p_secure; 415 #endif 416 417 if ( fullpathcmp((char_u *)USR_GVIMRC_FILE, 418 (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME 419 #ifdef SYS_GVIMRC_FILE 420 && fullpathcmp((char_u *)SYS_GVIMRC_FILE, 421 (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME 422 #endif 423 #ifdef USR_GVIMRC_FILE2 424 && fullpathcmp((char_u *)USR_GVIMRC_FILE2, 425 (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME 426 #endif 427 #ifdef USR_GVIMRC_FILE3 428 && fullpathcmp((char_u *)USR_GVIMRC_FILE3, 429 (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME 430 #endif 431 ) 432 do_source((char_u *)GVIMRC_FILE, TRUE, TRUE); 433 434 if (secure == 2) 435 need_wait_return = TRUE; 436 secure = 0; 437 } 438 } 439 440 if (need_wait_return || msg_didany) 441 wait_return(TRUE); 442 443 --recursive; 444 } 445 446 /* If recursive call opened the shell, return here from the first call */ 447 if (gui.in_use) 448 return; 449 450 /* 451 * Create the GUI shell. 452 */ 453 gui.in_use = TRUE; /* Must be set after menus have been set up */ 454 if (gui_mch_init() == FAIL) 455 goto error; 456 457 /* Avoid a delay for an error message that was printed in the terminal 458 * where Vim was started. */ 459 emsg_on_display = FALSE; 460 msg_scrolled = 0; 461 clear_sb_text(); 462 need_wait_return = FALSE; 463 msg_didany = FALSE; 464 465 /* 466 * Check validity of any generic resources that may have been loaded. 467 */ 468 if (gui.border_width < 0) 469 gui.border_width = 0; 470 471 /* 472 * Set up the fonts. First use a font specified with "-fn" or "-font". 473 */ 474 if (font_argument != NULL) 475 set_option_value((char_u *)"gfn", 0L, (char_u *)font_argument, 0); 476 if ( 477 #ifdef FEAT_XFONTSET 478 (*p_guifontset == NUL 479 || gui_init_font(p_guifontset, TRUE) == FAIL) && 480 #endif 481 gui_init_font(*p_guifont == NUL ? hl_get_font_name() 482 : p_guifont, FALSE) == FAIL) 483 { 484 EMSG(_("E665: Cannot start GUI, no valid font found")); 485 goto error2; 486 } 487 #ifdef FEAT_MBYTE 488 if (gui_get_wide_font() == FAIL) 489 EMSG(_("E231: 'guifontwide' invalid")); 490 #endif 491 492 gui.num_cols = Columns; 493 gui.num_rows = Rows; 494 gui_reset_scroll_region(); 495 496 /* Create initial scrollbars */ 497 FOR_ALL_WINDOWS(wp) 498 { 499 gui_create_scrollbar(&wp->w_scrollbars[SBAR_LEFT], SBAR_LEFT, wp); 500 gui_create_scrollbar(&wp->w_scrollbars[SBAR_RIGHT], SBAR_RIGHT, wp); 501 } 502 gui_create_scrollbar(&gui.bottom_sbar, SBAR_BOTTOM, NULL); 503 504 #ifdef FEAT_MENU 505 gui_create_initial_menus(root_menu); 506 #endif 507 #ifdef FEAT_SUN_WORKSHOP 508 if (usingSunWorkShop) 509 workshop_init(); 510 #endif 511 #ifdef FEAT_SIGN_ICONS 512 sign_gui_started(); 513 #endif 514 515 /* Configure the desired menu and scrollbars */ 516 gui_init_which_components(NULL); 517 518 /* All components of the GUI have been created now */ 519 gui.shell_created = TRUE; 520 521 #ifndef FEAT_GUI_GTK 522 /* Set the shell size, adjusted for the screen size. For GTK this only 523 * works after the shell has been opened, thus it is further down. */ 524 gui_set_shellsize(FALSE, TRUE); 525 #endif 526 #if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) 527 /* Need to set the size of the menubar after all the menus have been 528 * created. */ 529 gui_mch_compute_menu_height((Widget)0); 530 #endif 531 532 /* 533 * Actually open the GUI shell. 534 */ 535 if (gui_mch_open() != FAIL) 536 { 537 #ifdef FEAT_TITLE 538 maketitle(); 539 resettitle(); 540 #endif 541 init_gui_options(); 542 #ifdef FEAT_ARABIC 543 /* Our GUI can't do bidi. */ 544 p_tbidi = FALSE; 545 #endif 546 #if defined(FEAT_GUI_GTK) 547 /* Give GTK+ a chance to put all widget's into place. */ 548 gui_mch_update(); 549 /* Now make sure the shell fits on the screen. */ 550 gui_set_shellsize(FALSE, TRUE); 551 #endif 552 /* When 'lines' was set while starting up the topframe may have to be 553 * resized. */ 554 win_new_shellsize(); 555 556 #ifdef FEAT_BEVAL 557 /* Always create the Balloon Evaluation area, but disable it when 558 * 'ballooneval' is off */ 559 # ifdef FEAT_GUI_GTK 560 balloonEval = gui_mch_create_beval_area(gui.drawarea, NULL, 561 &general_beval_cb, NULL); 562 # else 563 # if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA) 564 { 565 extern Widget textArea; 566 balloonEval = gui_mch_create_beval_area(textArea, NULL, 567 &general_beval_cb, NULL); 568 } 569 # else 570 # ifdef FEAT_GUI_W32 571 balloonEval = gui_mch_create_beval_area(NULL, NULL, 572 &general_beval_cb, NULL); 573 # endif 574 # endif 575 # endif 576 if (!p_beval) 577 gui_mch_disable_beval_area(balloonEval); 578 #endif 579 580 #ifdef FEAT_NETBEANS_INTG 581 if (starting == 0 && usingNetbeans) 582 /* Tell the client that it can start sending commands. */ 583 netbeans_startup_done(); 584 #endif 585 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) 586 if (!im_xim_isvalid_imactivate()) 587 EMSG(_("E599: Value of 'imactivatekey' is invalid")); 588 #endif 589 /* When 'cmdheight' was set during startup it may not have taken 590 * effect yet. */ 591 if (p_ch != 1L) 592 command_height(-1L); 593 594 return; 595 } 596 597 error2: 598 #ifdef FEAT_GUI_X11 599 /* undo gui_mch_init() */ 600 gui_mch_uninit(); 601 #endif 602 603 error: 604 gui.in_use = FALSE; 605 clip_init(FALSE); 606 } 607 608 609 void 610 gui_exit(rc) 611 int rc; 612 { 613 #ifndef __BEOS__ 614 /* don't free the fonts, it leads to a BUS error 615 * [email protected] Jul 99 */ 616 free_highlight_fonts(); 617 #endif 618 gui.in_use = FALSE; 619 gui_mch_exit(rc); 620 } 621 622 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_MSWIN) \ 623 || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) || defined(PROTO) 624 /* 625 * Called when the GUI shell is closed by the user. If there are no changed 626 * files Vim exits, otherwise there will be a dialog to ask the user what to 627 * do. 628 * When this function returns, Vim should NOT exit! 629 */ 630 void 631 gui_shell_closed() 632 { 633 cmdmod_T save_cmdmod; 634 635 save_cmdmod = cmdmod; 636 637 /* Only exit when there are no changed files */ 638 exiting = TRUE; 639 # ifdef FEAT_BROWSE 640 cmdmod.browse = TRUE; 641 # endif 642 # if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 643 cmdmod.confirm = TRUE; 644 # endif 645 /* If there are changed buffers, present the user with a dialog if 646 * possible, otherwise give an error message. */ 647 if (!check_changed_any(FALSE)) 648 getout(0); 649 650 exiting = FALSE; 651 cmdmod = save_cmdmod; 652 setcursor(); /* position cursor */ 653 out_flush(); 654 } 655 #endif 656 657 /* 658 * Set the font. "font_list" is a a comma separated list of font names. The 659 * first font name that works is used. If none is found, use the default 660 * font. 661 * If "fontset" is TRUE, the "font_list" is used as one name for the fontset. 662 * Return OK when able to set the font. When it failed FAIL is returned and 663 * the fonts are unchanged. 664 */ 665 /*ARGSUSED*/ 666 int 667 gui_init_font(font_list, fontset) 668 char_u *font_list; 669 int fontset; 670 { 671 #define FONTLEN 320 672 char_u font_name[FONTLEN]; 673 int font_list_empty = FALSE; 674 int ret = FAIL; 675 676 if (!gui.in_use) 677 return FAIL; 678 679 font_name[0] = NUL; 680 if (*font_list == NUL) 681 font_list_empty = TRUE; 682 else 683 { 684 #ifdef FEAT_XFONTSET 685 /* When using a fontset, the whole list of fonts is one name. */ 686 if (fontset) 687 ret = gui_mch_init_font(font_list, TRUE); 688 else 689 #endif 690 while (*font_list != NUL) 691 { 692 /* Isolate one comma separated font name. */ 693 (void)copy_option_part(&font_list, font_name, FONTLEN, ","); 694 695 /* Careful!!! The Win32 version of gui_mch_init_font(), when 696 * called with "*" will change p_guifont to the selected font 697 * name, which frees the old value. This makes font_list 698 * invalid. Thus when OK is returned here, font_list must no 699 * longer be used! */ 700 if (gui_mch_init_font(font_name, FALSE) == OK) 701 { 702 #if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) 703 /* If it's a Unicode font, try setting 'guifontwide' to a 704 * similar double-width font. */ 705 if ((p_guifontwide == NULL || *p_guifontwide == NUL) 706 && strstr((char *)font_name, "10646") != NULL) 707 set_guifontwide(font_name); 708 #endif 709 ret = OK; 710 break; 711 } 712 } 713 } 714 715 if (ret != OK 716 && STRCMP(font_list, "*") != 0 717 && (font_list_empty || gui.norm_font == NOFONT)) 718 { 719 /* 720 * Couldn't load any font in 'font_list', keep the current font if 721 * there is one. If 'font_list' is empty, or if there is no current 722 * font, tell gui_mch_init_font() to try to find a font we can load. 723 */ 724 ret = gui_mch_init_font(NULL, FALSE); 725 } 726 727 if (ret == OK) 728 { 729 #ifndef HAVE_GTK2 730 /* Set normal font as current font */ 731 # ifdef FEAT_XFONTSET 732 if (gui.fontset != NOFONTSET) 733 gui_mch_set_fontset(gui.fontset); 734 else 735 # endif 736 gui_mch_set_font(gui.norm_font); 737 #endif 738 gui_set_shellsize(FALSE, 739 #ifdef MSWIN 740 TRUE 741 #else 742 FALSE 743 #endif 744 ); 745 } 746 747 return ret; 748 } 749 750 #if defined(FEAT_MBYTE) || defined(PROTO) 751 # ifndef HAVE_GTK2 752 /* 753 * Try setting 'guifontwide' to a font twice as wide as "name". 754 */ 755 static void 756 set_guifontwide(name) 757 char_u *name; 758 { 759 int i = 0; 760 char_u wide_name[FONTLEN + 10]; /* room for 2 * width and '*' */ 761 char_u *wp = NULL; 762 char_u *p; 763 GuiFont font; 764 765 wp = wide_name; 766 for (p = name; *p != NUL; ++p) 767 { 768 *wp++ = *p; 769 if (*p == '-') 770 { 771 ++i; 772 if (i == 6) /* font type: change "--" to "-*-" */ 773 { 774 if (p[1] == '-') 775 *wp++ = '*'; 776 } 777 else if (i == 12) /* found the width */ 778 { 779 ++p; 780 i = getdigits(&p); 781 if (i != 0) 782 { 783 /* Double the width specification. */ 784 sprintf((char *)wp, "%d%s", i * 2, p); 785 font = gui_mch_get_font(wide_name, FALSE); 786 if (font != NOFONT) 787 { 788 gui_mch_free_font(gui.wide_font); 789 gui.wide_font = font; 790 set_string_option_direct((char_u *)"gfw", -1, 791 wide_name, OPT_FREE, 0); 792 } 793 } 794 break; 795 } 796 } 797 } 798 } 799 # endif /* !HAVE_GTK2 */ 800 801 /* 802 * Get the font for 'guifontwide'. 803 * Return FAIL for an invalid font name. 804 */ 805 int 806 gui_get_wide_font() 807 { 808 GuiFont font = NOFONT; 809 char_u font_name[FONTLEN]; 810 char_u *p; 811 812 if (!gui.in_use) /* Can't allocate font yet, assume it's OK. */ 813 return OK; /* Will give an error message later. */ 814 815 if (p_guifontwide != NULL && *p_guifontwide != NUL) 816 { 817 for (p = p_guifontwide; *p != NUL; ) 818 { 819 /* Isolate one comma separated font name. */ 820 (void)copy_option_part(&p, font_name, FONTLEN, ","); 821 font = gui_mch_get_font(font_name, FALSE); 822 if (font != NOFONT) 823 break; 824 } 825 if (font == NOFONT) 826 return FAIL; 827 } 828 829 gui_mch_free_font(gui.wide_font); 830 #ifdef HAVE_GTK2 831 /* Avoid unnecessary overhead if 'guifontwide' is equal to 'guifont'. */ 832 if (font != NOFONT && gui.norm_font != NOFONT 833 && pango_font_description_equal(font, gui.norm_font)) 834 { 835 gui.wide_font = NOFONT; 836 gui_mch_free_font(font); 837 } 838 else 839 #endif 840 gui.wide_font = font; 841 return OK; 842 } 843 #endif 844 845 void 846 gui_set_cursor(row, col) 847 int row; 848 int col; 849 { 850 gui.row = row; 851 gui.col = col; 852 } 853 854 /* 855 * gui_check_pos - check if the cursor is on the screen. 856 */ 857 static void 858 gui_check_pos() 859 { 860 if (gui.row >= screen_Rows) 861 gui.row = screen_Rows - 1; 862 if (gui.col >= screen_Columns) 863 gui.col = screen_Columns - 1; 864 if (gui.cursor_row >= screen_Rows || gui.cursor_col >= screen_Columns) 865 gui.cursor_is_valid = FALSE; 866 } 867 868 /* 869 * Redraw the cursor if necessary or when forced. 870 * Careful: The contents of ScreenLines[] must match what is on the screen, 871 * otherwise this goes wrong. May need to call out_flush() first. 872 */ 873 void 874 gui_update_cursor(force, clear_selection) 875 int force; /* when TRUE, update even when not moved */ 876 int clear_selection;/* clear selection under cursor */ 877 { 878 int cur_width = 0; 879 int cur_height = 0; 880 int old_hl_mask; 881 int idx; 882 int id; 883 guicolor_T cfg, cbg, cc; /* cursor fore-/background color */ 884 int cattr; /* cursor attributes */ 885 int attr; 886 attrentry_T *aep = NULL; 887 888 /* Don't update the cursor when halfway busy scrolling. 889 * ScreenLines[] isn't valid then. */ 890 if (!can_update_cursor) 891 return; 892 893 gui_check_pos(); 894 if (!gui.cursor_is_valid || force 895 || gui.row != gui.cursor_row || gui.col != gui.cursor_col) 896 { 897 gui_undraw_cursor(); 898 if (gui.row < 0) 899 return; 900 #ifdef USE_IM_CONTROL 901 if (gui.row != gui.cursor_row || gui.col != gui.cursor_col) 902 im_set_position(gui.row, gui.col); 903 #endif 904 gui.cursor_row = gui.row; 905 gui.cursor_col = gui.col; 906 907 /* Only write to the screen after ScreenLines[] has been initialized */ 908 if (!screen_cleared || ScreenLines == NULL) 909 return; 910 911 /* Clear the selection if we are about to write over it */ 912 if (clear_selection) 913 clip_may_clear_selection(gui.row, gui.row); 914 /* Check that the cursor is inside the shell (resizing may have made 915 * it invalid) */ 916 if (gui.row >= screen_Rows || gui.col >= screen_Columns) 917 return; 918 919 gui.cursor_is_valid = TRUE; 920 921 /* 922 * How the cursor is drawn depends on the current mode. 923 */ 924 idx = get_shape_idx(FALSE); 925 if (State & LANGMAP) 926 id = shape_table[idx].id_lm; 927 else 928 id = shape_table[idx].id; 929 930 /* get the colors and attributes for the cursor. Default is inverted */ 931 cfg = INVALCOLOR; 932 cbg = INVALCOLOR; 933 cattr = HL_INVERSE; 934 gui_mch_set_blinking(shape_table[idx].blinkwait, 935 shape_table[idx].blinkon, 936 shape_table[idx].blinkoff); 937 if (id > 0) 938 { 939 cattr = syn_id2colors(id, &cfg, &cbg); 940 #if defined(USE_IM_CONTROL) || defined(FEAT_HANGULIN) 941 { 942 static int iid; 943 guicolor_T fg, bg; 944 945 if (im_get_status()) 946 { 947 iid = syn_name2id((char_u *)"CursorIM"); 948 if (iid > 0) 949 { 950 syn_id2colors(iid, &fg, &bg); 951 if (bg != INVALCOLOR) 952 cbg = bg; 953 if (fg != INVALCOLOR) 954 cfg = fg; 955 } 956 } 957 } 958 #endif 959 } 960 961 /* 962 * Get the attributes for the character under the cursor. 963 * When no cursor color was given, use the character color. 964 */ 965 attr = ScreenAttrs[LineOffset[gui.row] + gui.col]; 966 if (attr > HL_ALL) 967 aep = syn_gui_attr2entry(attr); 968 if (aep != NULL) 969 { 970 attr = aep->ae_attr; 971 if (cfg == INVALCOLOR) 972 cfg = ((attr & HL_INVERSE) ? aep->ae_u.gui.bg_color 973 : aep->ae_u.gui.fg_color); 974 if (cbg == INVALCOLOR) 975 cbg = ((attr & HL_INVERSE) ? aep->ae_u.gui.fg_color 976 : aep->ae_u.gui.bg_color); 977 } 978 if (cfg == INVALCOLOR) 979 cfg = (attr & HL_INVERSE) ? gui.back_pixel : gui.norm_pixel; 980 if (cbg == INVALCOLOR) 981 cbg = (attr & HL_INVERSE) ? gui.norm_pixel : gui.back_pixel; 982 983 #ifdef FEAT_XIM 984 if (aep != NULL) 985 { 986 xim_bg_color = ((attr & HL_INVERSE) ? aep->ae_u.gui.fg_color 987 : aep->ae_u.gui.bg_color); 988 xim_fg_color = ((attr & HL_INVERSE) ? aep->ae_u.gui.bg_color 989 : aep->ae_u.gui.fg_color); 990 if (xim_bg_color == INVALCOLOR) 991 xim_bg_color = (attr & HL_INVERSE) ? gui.norm_pixel 992 : gui.back_pixel; 993 if (xim_fg_color == INVALCOLOR) 994 xim_fg_color = (attr & HL_INVERSE) ? gui.back_pixel 995 : gui.norm_pixel; 996 } 997 else 998 { 999 xim_bg_color = (attr & HL_INVERSE) ? gui.norm_pixel 1000 : gui.back_pixel; 1001 xim_fg_color = (attr & HL_INVERSE) ? gui.back_pixel 1002 : gui.norm_pixel; 1003 } 1004 #endif 1005 1006 attr &= ~HL_INVERSE; 1007 if (cattr & HL_INVERSE) 1008 { 1009 cc = cbg; 1010 cbg = cfg; 1011 cfg = cc; 1012 } 1013 cattr &= ~HL_INVERSE; 1014 1015 /* 1016 * When we don't have window focus, draw a hollow cursor. 1017 */ 1018 if (!gui.in_focus) 1019 { 1020 gui_mch_draw_hollow_cursor(cbg); 1021 return; 1022 } 1023 1024 old_hl_mask = gui.highlight_mask; 1025 if (shape_table[idx].shape == SHAPE_BLOCK 1026 #ifdef FEAT_HANGULIN 1027 || composing_hangul 1028 #endif 1029 ) 1030 { 1031 /* 1032 * Draw the text character with the cursor colors. Use the 1033 * character attributes plus the cursor attributes. 1034 */ 1035 gui.highlight_mask = (cattr | attr); 1036 #ifdef FEAT_HANGULIN 1037 if (composing_hangul) 1038 (void)gui_outstr_nowrap(composing_hangul_buffer, 2, 1039 GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, cfg, cbg, 0); 1040 else 1041 #endif 1042 (void)gui_screenchar(LineOffset[gui.row] + gui.col, 1043 GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, cfg, cbg, 0); 1044 } 1045 else 1046 { 1047 #if defined(FEAT_MBYTE) && defined(FEAT_RIGHTLEFT) 1048 int col_off = FALSE; 1049 #endif 1050 /* 1051 * First draw the partial cursor, then overwrite with the text 1052 * character, using a transparent background. 1053 */ 1054 if (shape_table[idx].shape == SHAPE_VER) 1055 { 1056 cur_height = gui.char_height; 1057 cur_width = (gui.char_width * shape_table[idx].percentage 1058 + 99) / 100; 1059 } 1060 else 1061 { 1062 cur_height = (gui.char_height * shape_table[idx].percentage 1063 + 99) / 100; 1064 cur_width = gui.char_width; 1065 } 1066 #ifdef FEAT_MBYTE 1067 if (has_mbyte && (*mb_off2cells)(LineOffset[gui.row] + gui.col) > 1) 1068 { 1069 /* Double wide character. */ 1070 if (shape_table[idx].shape != SHAPE_VER) 1071 cur_width += gui.char_width; 1072 # ifdef FEAT_RIGHTLEFT 1073 if (CURSOR_BAR_RIGHT) 1074 { 1075 /* gui.col points to the left halve of the character but 1076 * the vertical line needs to be on the right halve. 1077 * A double-wide horizontal line is also drawn from the 1078 * right halve in gui_mch_draw_part_cursor(). */ 1079 col_off = TRUE; 1080 ++gui.col; 1081 } 1082 # endif 1083 } 1084 #endif 1085 gui_mch_draw_part_cursor(cur_width, cur_height, cbg); 1086 #if defined(FEAT_MBYTE) && defined(FEAT_RIGHTLEFT) 1087 if (col_off) 1088 --gui.col; 1089 #endif 1090 1091 #ifndef FEAT_GUI_MSWIN /* doesn't seem to work for MSWindows */ 1092 gui.highlight_mask = ScreenAttrs[LineOffset[gui.row] + gui.col]; 1093 (void)gui_screenchar(LineOffset[gui.row] + gui.col, 1094 GUI_MON_TRS_CURSOR | GUI_MON_NOCLEAR, 1095 (guicolor_T)0, (guicolor_T)0, 0); 1096 #endif 1097 } 1098 gui.highlight_mask = old_hl_mask; 1099 } 1100 } 1101 1102 #if defined(FEAT_MENU) || defined(PROTO) 1103 void 1104 gui_position_menu() 1105 { 1106 # if !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) 1107 if (gui.menu_is_active && gui.in_use) 1108 gui_mch_set_menu_pos(0, 0, gui.menu_width, gui.menu_height); 1109 # endif 1110 } 1111 #endif 1112 1113 /* 1114 * Position the various GUI components (text area, menu). The vertical 1115 * scrollbars are NOT handled here. See gui_update_scrollbars(). 1116 */ 1117 /*ARGSUSED*/ 1118 static void 1119 gui_position_components(total_width) 1120 int total_width; 1121 { 1122 int text_area_x; 1123 int text_area_y; 1124 int text_area_width; 1125 int text_area_height; 1126 1127 /* avoid that moving components around generates events */ 1128 ++hold_gui_events; 1129 1130 text_area_x = 0; 1131 if (gui.which_scrollbars[SBAR_LEFT]) 1132 text_area_x += gui.scrollbar_width; 1133 1134 text_area_y = 0; 1135 #if defined(FEAT_MENU) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON)) 1136 gui.menu_width = total_width; 1137 if (gui.menu_is_active) 1138 text_area_y += gui.menu_height; 1139 #endif 1140 #if defined(FEAT_TOOLBAR) && defined(FEAT_GUI_MSWIN) 1141 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1142 text_area_y = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; 1143 #endif 1144 1145 #if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) 1146 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1147 { 1148 # ifdef FEAT_GUI_ATHENA 1149 gui_mch_set_toolbar_pos(0, text_area_y, 1150 gui.menu_width, gui.toolbar_height); 1151 # endif 1152 text_area_y += gui.toolbar_height; 1153 } 1154 #endif 1155 1156 text_area_width = gui.num_cols * gui.char_width + gui.border_offset * 2; 1157 text_area_height = gui.num_rows * gui.char_height + gui.border_offset * 2; 1158 1159 gui_mch_set_text_area_pos(text_area_x, 1160 text_area_y, 1161 text_area_width, 1162 text_area_height 1163 #if defined(FEAT_XIM) && !defined(HAVE_GTK2) 1164 + xim_get_status_area_height() 1165 #endif 1166 ); 1167 #ifdef FEAT_MENU 1168 gui_position_menu(); 1169 #endif 1170 if (gui.which_scrollbars[SBAR_BOTTOM]) 1171 gui_mch_set_scrollbar_pos(&gui.bottom_sbar, 1172 text_area_x, 1173 text_area_y + text_area_height, 1174 text_area_width, 1175 gui.scrollbar_height); 1176 gui.left_sbar_x = 0; 1177 gui.right_sbar_x = text_area_x + text_area_width; 1178 1179 --hold_gui_events; 1180 } 1181 1182 /* 1183 * Get the width of the widgets and decorations to the side of the text area. 1184 */ 1185 int 1186 gui_get_base_width() 1187 { 1188 int base_width; 1189 1190 base_width = 2 * gui.border_offset; 1191 if (gui.which_scrollbars[SBAR_LEFT]) 1192 base_width += gui.scrollbar_width; 1193 if (gui.which_scrollbars[SBAR_RIGHT]) 1194 base_width += gui.scrollbar_width; 1195 return base_width; 1196 } 1197 1198 /* 1199 * Get the height of the widgets and decorations above and below the text area. 1200 */ 1201 int 1202 gui_get_base_height() 1203 { 1204 int base_height; 1205 1206 base_height = 2 * gui.border_offset; 1207 if (gui.which_scrollbars[SBAR_BOTTOM]) 1208 base_height += gui.scrollbar_height; 1209 #ifdef FEAT_GUI_GTK 1210 /* We can't take the sizes properly into account until anything is 1211 * realized. Therefore we recalculate all the values here just before 1212 * setting the size. (--mdcki) */ 1213 #else 1214 # ifdef FEAT_MENU 1215 if (gui.menu_is_active) 1216 base_height += gui.menu_height; 1217 # endif 1218 # ifdef FEAT_TOOLBAR 1219 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1220 # if defined(FEAT_GUI_MSWIN) && defined(FEAT_TOOLBAR) 1221 base_height += (TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT); 1222 # else 1223 base_height += gui.toolbar_height; 1224 # endif 1225 # endif 1226 # ifdef FEAT_FOOTER 1227 if (vim_strchr(p_go, GO_FOOTER) != NULL) 1228 base_height += gui.footer_height; 1229 # endif 1230 # if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) 1231 base_height += gui_mch_text_area_extra_height(); 1232 # endif 1233 #endif 1234 return base_height; 1235 } 1236 1237 /* 1238 * Should be called after the GUI shell has been resized. Its arguments are 1239 * the new width and height of the shell in pixels. 1240 */ 1241 void 1242 gui_resize_shell(pixel_width, pixel_height) 1243 int pixel_width; 1244 int pixel_height; 1245 { 1246 static int busy = FALSE; 1247 1248 if (!gui.shell_created) /* ignore when still initializing */ 1249 return; 1250 1251 /* 1252 * Can't resize the screen while it is being redrawn. Remember the new 1253 * size and handle it later. 1254 */ 1255 if (updating_screen || busy) 1256 { 1257 new_pixel_width = pixel_width; 1258 new_pixel_height = pixel_height; 1259 return; 1260 } 1261 1262 again: 1263 busy = TRUE; 1264 1265 /* Flush pending output before redrawing */ 1266 out_flush(); 1267 1268 gui.num_cols = (pixel_width - gui_get_base_width()) / gui.char_width; 1269 gui.num_rows = (pixel_height - gui_get_base_height() 1270 #if !defined(FEAT_GUI_PHOTON) && !defined(FEAT_GUI_MSWIN) 1271 + (gui.char_height / 2) 1272 #endif 1273 ) / gui.char_height; 1274 1275 gui_position_components(pixel_width); 1276 1277 gui_reset_scroll_region(); 1278 /* 1279 * At the "more" and ":confirm" prompt there is no redraw, put the cursor 1280 * at the last line here (why does it have to be one row too low?). 1281 */ 1282 if (State == ASKMORE || State == CONFIRM) 1283 gui.row = gui.num_rows; 1284 1285 /* Only comparing Rows and Columns may be sufficient, but let's stay on 1286 * the safe side. */ 1287 if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns 1288 || gui.num_rows != Rows || gui.num_cols != Columns) 1289 shell_resized(); 1290 1291 gui_update_scrollbars(TRUE); 1292 gui_update_cursor(FALSE, TRUE); 1293 #if defined(FEAT_XIM) && !defined(HAVE_GTK2) 1294 xim_set_status_area(); 1295 #endif 1296 1297 busy = FALSE; 1298 1299 /* 1300 * We could have been called again while redrawing the screen. 1301 * Need to do it all again with the latest size then. 1302 */ 1303 if (new_pixel_height) 1304 { 1305 pixel_width = new_pixel_width; 1306 pixel_height = new_pixel_height; 1307 new_pixel_width = 0; 1308 new_pixel_height = 0; 1309 goto again; 1310 } 1311 } 1312 1313 /* 1314 * Check if gui_resize_shell() must be called. 1315 */ 1316 void 1317 gui_may_resize_shell() 1318 { 1319 int h, w; 1320 1321 if (new_pixel_height) 1322 { 1323 /* careful: gui_resize_shell() may postpone the resize again if we 1324 * were called indirectly by it */ 1325 w = new_pixel_width; 1326 h = new_pixel_height; 1327 new_pixel_width = 0; 1328 new_pixel_height = 0; 1329 gui_resize_shell(w, h); 1330 } 1331 } 1332 1333 int 1334 gui_get_shellsize() 1335 { 1336 Rows = gui.num_rows; 1337 Columns = gui.num_cols; 1338 return OK; 1339 } 1340 1341 /* 1342 * Set the size of the Vim shell according to Rows and Columns. 1343 * If "fit_to_display" is TRUE then the size may be reduced to fit the window 1344 * on the screen. 1345 */ 1346 /*ARGSUSED*/ 1347 void 1348 gui_set_shellsize(mustset, fit_to_display) 1349 int mustset; /* set by the user */ 1350 int fit_to_display; 1351 { 1352 int base_width; 1353 int base_height; 1354 int width; 1355 int height; 1356 int min_width; 1357 int min_height; 1358 int screen_w; 1359 int screen_h; 1360 1361 if (!gui.shell_created) 1362 return; 1363 1364 #ifdef MSWIN 1365 /* If not setting to a user specified size and maximized, calculate the 1366 * number of characters that fit in the maximized window. */ 1367 if (!mustset && gui_mch_maximized()) 1368 { 1369 gui_mch_newfont(); 1370 return; 1371 } 1372 #endif 1373 1374 base_width = gui_get_base_width(); 1375 base_height = gui_get_base_height(); 1376 #ifdef USE_SUN_WORKSHOP 1377 if (!mustset && usingSunWorkShop 1378 && workshop_get_width_height(&width, &height)) 1379 { 1380 Columns = (width - base_width + gui.char_width - 1) / gui.char_width; 1381 Rows = (height - base_height + gui.char_height - 1) / gui.char_height; 1382 } 1383 else 1384 #endif 1385 { 1386 width = Columns * gui.char_width + base_width; 1387 height = Rows * gui.char_height + base_height; 1388 } 1389 1390 if (fit_to_display) 1391 { 1392 gui_mch_get_screen_dimensions(&screen_w, &screen_h); 1393 if (width > screen_w) 1394 { 1395 Columns = (screen_w - base_width) / gui.char_width; 1396 if (Columns < MIN_COLUMNS) 1397 Columns = MIN_COLUMNS; 1398 width = Columns * gui.char_width + base_width; 1399 } 1400 if (height > screen_h) 1401 { 1402 Rows = (screen_h - base_height) / gui.char_height; 1403 check_shellsize(); 1404 height = Rows * gui.char_height + base_height; 1405 } 1406 } 1407 gui.num_cols = Columns; 1408 gui.num_rows = Rows; 1409 1410 min_width = base_width + MIN_COLUMNS * gui.char_width; 1411 min_height = base_height + MIN_LINES * gui.char_height; 1412 # ifdef FEAT_WINDOWS 1413 min_height += tabline_height() * gui.char_height; 1414 # endif 1415 1416 gui_mch_set_shellsize(width, height, min_width, min_height, 1417 base_width, base_height); 1418 if (fit_to_display) 1419 { 1420 int x, y; 1421 1422 /* Some window managers put the Vim window left of/above the screen. */ 1423 gui_mch_update(); 1424 if (gui_mch_get_winpos(&x, &y) == OK && (x < 0 || y < 0)) 1425 gui_mch_set_winpos(x < 0 ? 0 : x, y < 0 ? 0 : y); 1426 } 1427 1428 gui_position_components(width); 1429 gui_update_scrollbars(TRUE); 1430 gui_reset_scroll_region(); 1431 } 1432 1433 /* 1434 * Called when Rows and/or Columns has changed. 1435 */ 1436 void 1437 gui_new_shellsize() 1438 { 1439 gui_reset_scroll_region(); 1440 } 1441 1442 /* 1443 * Make scroll region cover whole screen. 1444 */ 1445 void 1446 gui_reset_scroll_region() 1447 { 1448 gui.scroll_region_top = 0; 1449 gui.scroll_region_bot = gui.num_rows - 1; 1450 gui.scroll_region_left = 0; 1451 gui.scroll_region_right = gui.num_cols - 1; 1452 } 1453 1454 void 1455 gui_start_highlight(mask) 1456 int mask; 1457 { 1458 if (mask > HL_ALL) /* highlight code */ 1459 gui.highlight_mask = mask; 1460 else /* mask */ 1461 gui.highlight_mask |= mask; 1462 } 1463 1464 void 1465 gui_stop_highlight(mask) 1466 int mask; 1467 { 1468 if (mask > HL_ALL) /* highlight code */ 1469 gui.highlight_mask = HL_NORMAL; 1470 else /* mask */ 1471 gui.highlight_mask &= ~mask; 1472 } 1473 1474 /* 1475 * Clear a rectangular region of the screen from text pos (row1, col1) to 1476 * (row2, col2) inclusive. 1477 */ 1478 void 1479 gui_clear_block(row1, col1, row2, col2) 1480 int row1; 1481 int col1; 1482 int row2; 1483 int col2; 1484 { 1485 /* Clear the selection if we are about to write over it */ 1486 clip_may_clear_selection(row1, row2); 1487 1488 gui_mch_clear_block(row1, col1, row2, col2); 1489 1490 /* Invalidate cursor if it was in this block */ 1491 if ( gui.cursor_row >= row1 && gui.cursor_row <= row2 1492 && gui.cursor_col >= col1 && gui.cursor_col <= col2) 1493 gui.cursor_is_valid = FALSE; 1494 } 1495 1496 /* 1497 * Write code to update the cursor later. This avoids the need to flush the 1498 * output buffer before calling gui_update_cursor(). 1499 */ 1500 void 1501 gui_update_cursor_later() 1502 { 1503 OUT_STR(IF_EB("\033|s", ESC_STR "|s")); 1504 } 1505 1506 void 1507 gui_write(s, len) 1508 char_u *s; 1509 int len; 1510 { 1511 char_u *p; 1512 int arg1 = 0, arg2 = 0; 1513 /* this doesn't make sense, disabled until someone can explain why it 1514 * would be needed */ 1515 #if 0 && (defined(RISCOS) || defined(WIN16)) 1516 int force_cursor = TRUE; /* JK230798, stop Vim being smart or 1517 our redraw speed will suffer */ 1518 #else 1519 int force_cursor = FALSE; /* force cursor update */ 1520 #endif 1521 int force_scrollbar = FALSE; 1522 static win_T *old_curwin = NULL; 1523 1524 /* #define DEBUG_GUI_WRITE */ 1525 #ifdef DEBUG_GUI_WRITE 1526 { 1527 int i; 1528 char_u *str; 1529 1530 printf("gui_write(%d):\n ", len); 1531 for (i = 0; i < len; i++) 1532 if (s[i] == ESC) 1533 { 1534 if (i != 0) 1535 printf("\n "); 1536 printf("<ESC>"); 1537 } 1538 else 1539 { 1540 str = transchar_byte(s[i]); 1541 if (str[0] && str[1]) 1542 printf("<%s>", (char *)str); 1543 else 1544 printf("%s", (char *)str); 1545 } 1546 printf("\n"); 1547 } 1548 #endif 1549 while (len) 1550 { 1551 if (s[0] == ESC && s[1] == '|') 1552 { 1553 p = s + 2; 1554 if (VIM_ISDIGIT(*p)) 1555 { 1556 arg1 = getdigits(&p); 1557 if (p > s + len) 1558 break; 1559 if (*p == ';') 1560 { 1561 ++p; 1562 arg2 = getdigits(&p); 1563 if (p > s + len) 1564 break; 1565 } 1566 } 1567 switch (*p) 1568 { 1569 case 'C': /* Clear screen */ 1570 clip_scroll_selection(9999); 1571 gui_mch_clear_all(); 1572 gui.cursor_is_valid = FALSE; 1573 force_scrollbar = TRUE; 1574 break; 1575 case 'M': /* Move cursor */ 1576 gui_set_cursor(arg1, arg2); 1577 break; 1578 case 's': /* force cursor (shape) update */ 1579 force_cursor = TRUE; 1580 break; 1581 case 'R': /* Set scroll region */ 1582 if (arg1 < arg2) 1583 { 1584 gui.scroll_region_top = arg1; 1585 gui.scroll_region_bot = arg2; 1586 } 1587 else 1588 { 1589 gui.scroll_region_top = arg2; 1590 gui.scroll_region_bot = arg1; 1591 } 1592 break; 1593 #ifdef FEAT_VERTSPLIT 1594 case 'V': /* Set vertical scroll region */ 1595 if (arg1 < arg2) 1596 { 1597 gui.scroll_region_left = arg1; 1598 gui.scroll_region_right = arg2; 1599 } 1600 else 1601 { 1602 gui.scroll_region_left = arg2; 1603 gui.scroll_region_right = arg1; 1604 } 1605 break; 1606 #endif 1607 case 'd': /* Delete line */ 1608 gui_delete_lines(gui.row, 1); 1609 break; 1610 case 'D': /* Delete lines */ 1611 gui_delete_lines(gui.row, arg1); 1612 break; 1613 case 'i': /* Insert line */ 1614 gui_insert_lines(gui.row, 1); 1615 break; 1616 case 'I': /* Insert lines */ 1617 gui_insert_lines(gui.row, arg1); 1618 break; 1619 case '$': /* Clear to end-of-line */ 1620 gui_clear_block(gui.row, gui.col, gui.row, 1621 (int)Columns - 1); 1622 break; 1623 case 'h': /* Turn on highlighting */ 1624 gui_start_highlight(arg1); 1625 break; 1626 case 'H': /* Turn off highlighting */ 1627 gui_stop_highlight(arg1); 1628 break; 1629 case 'f': /* flash the window (visual bell) */ 1630 gui_mch_flash(arg1 == 0 ? 20 : arg1); 1631 break; 1632 default: 1633 p = s + 1; /* Skip the ESC */ 1634 break; 1635 } 1636 len -= (int)(++p - s); 1637 s = p; 1638 } 1639 else if ( 1640 #ifdef EBCDIC 1641 CtrlChar(s[0]) != 0 /* Ctrl character */ 1642 #else 1643 s[0] < 0x20 /* Ctrl character */ 1644 #endif 1645 #ifdef FEAT_SIGN_ICONS 1646 && s[0] != SIGN_BYTE 1647 # ifdef FEAT_NETBEANS_INTG 1648 && s[0] != MULTISIGN_BYTE 1649 # endif 1650 #endif 1651 ) 1652 { 1653 if (s[0] == '\n') /* NL */ 1654 { 1655 gui.col = 0; 1656 if (gui.row < gui.scroll_region_bot) 1657 gui.row++; 1658 else 1659 gui_delete_lines(gui.scroll_region_top, 1); 1660 } 1661 else if (s[0] == '\r') /* CR */ 1662 { 1663 gui.col = 0; 1664 } 1665 else if (s[0] == '\b') /* Backspace */ 1666 { 1667 if (gui.col) 1668 --gui.col; 1669 } 1670 else if (s[0] == Ctrl_L) /* cursor-right */ 1671 { 1672 ++gui.col; 1673 } 1674 else if (s[0] == Ctrl_G) /* Beep */ 1675 { 1676 gui_mch_beep(); 1677 } 1678 /* Other Ctrl character: shouldn't happen! */ 1679 1680 --len; /* Skip this char */ 1681 ++s; 1682 } 1683 else 1684 { 1685 p = s; 1686 while (len > 0 && ( 1687 #ifdef EBCDIC 1688 CtrlChar(*p) == 0 1689 #else 1690 *p >= 0x20 1691 #endif 1692 #ifdef FEAT_SIGN_ICONS 1693 || *p == SIGN_BYTE 1694 # ifdef FEAT_NETBEANS_INTG 1695 || *p == MULTISIGN_BYTE 1696 # endif 1697 #endif 1698 )) 1699 { 1700 len--; 1701 p++; 1702 } 1703 gui_outstr(s, (int)(p - s)); 1704 s = p; 1705 } 1706 } 1707 1708 /* Postponed update of the cursor (won't work if "can_update_cursor" isn't 1709 * set). */ 1710 if (force_cursor) 1711 gui_update_cursor(TRUE, TRUE); 1712 1713 /* When switching to another window the dragging must have stopped. 1714 * Required for GTK, dragged_sb isn't reset. */ 1715 if (old_curwin != curwin) 1716 gui.dragged_sb = SBAR_NONE; 1717 1718 /* Update the scrollbars after clearing the screen or when switched 1719 * to another window. 1720 * Update the horizontal scrollbar always, it's difficult to check all 1721 * situations where it might change. */ 1722 if (force_scrollbar || old_curwin != curwin) 1723 gui_update_scrollbars(force_scrollbar); 1724 else 1725 gui_update_horiz_scrollbar(FALSE); 1726 old_curwin = curwin; 1727 1728 /* 1729 * We need to make sure this is cleared since Athena doesn't tell us when 1730 * he is done dragging. Do the same for GTK. 1731 */ 1732 #if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_GTK) 1733 gui.dragged_sb = SBAR_NONE; 1734 #endif 1735 1736 gui_mch_flush(); /* In case vim decides to take a nap */ 1737 } 1738 1739 /* 1740 * When ScreenLines[] is invalid, updating the cursor should not be done, it 1741 * produces wrong results. Call gui_dont_update_cursor() before that code and 1742 * gui_can_update_cursor() afterwards. 1743 */ 1744 void 1745 gui_dont_update_cursor() 1746 { 1747 if (gui.in_use) 1748 { 1749 /* Undraw the cursor now, we probably can't do it after the change. */ 1750 gui_undraw_cursor(); 1751 can_update_cursor = FALSE; 1752 } 1753 } 1754 1755 void 1756 gui_can_update_cursor() 1757 { 1758 can_update_cursor = TRUE; 1759 /* No need to update the cursor right now, there is always more output 1760 * after scrolling. */ 1761 } 1762 1763 static void 1764 gui_outstr(s, len) 1765 char_u *s; 1766 int len; 1767 { 1768 int this_len; 1769 #ifdef FEAT_MBYTE 1770 int cells; 1771 #endif 1772 1773 if (len == 0) 1774 return; 1775 1776 if (len < 0) 1777 len = (int)STRLEN(s); 1778 1779 while (len > 0) 1780 { 1781 #ifdef FEAT_MBYTE 1782 if (has_mbyte) 1783 { 1784 /* Find out how many chars fit in the current line. */ 1785 cells = 0; 1786 for (this_len = 0; this_len < len; ) 1787 { 1788 cells += (*mb_ptr2cells)(s + this_len); 1789 if (gui.col + cells > Columns) 1790 break; 1791 this_len += (*mb_ptr2len)(s + this_len); 1792 } 1793 if (this_len > len) 1794 this_len = len; /* don't include following composing char */ 1795 } 1796 else 1797 #endif 1798 if (gui.col + len > Columns) 1799 this_len = Columns - gui.col; 1800 else 1801 this_len = len; 1802 1803 (void)gui_outstr_nowrap(s, this_len, 1804 0, (guicolor_T)0, (guicolor_T)0, 0); 1805 s += this_len; 1806 len -= this_len; 1807 #ifdef FEAT_MBYTE 1808 /* fill up for a double-width char that doesn't fit. */ 1809 if (len > 0 && gui.col < Columns) 1810 (void)gui_outstr_nowrap((char_u *)" ", 1, 1811 0, (guicolor_T)0, (guicolor_T)0, 0); 1812 #endif 1813 /* The cursor may wrap to the next line. */ 1814 if (gui.col >= Columns) 1815 { 1816 gui.col = 0; 1817 gui.row++; 1818 } 1819 } 1820 } 1821 1822 /* 1823 * Output one character (may be one or two display cells). 1824 * Caller must check for valid "off". 1825 * Returns FAIL or OK, just like gui_outstr_nowrap(). 1826 */ 1827 static int 1828 gui_screenchar(off, flags, fg, bg, back) 1829 int off; /* Offset from start of screen */ 1830 int flags; 1831 guicolor_T fg, bg; /* colors for cursor */ 1832 int back; /* backup this many chars when using bold trick */ 1833 { 1834 #ifdef FEAT_MBYTE 1835 char_u buf[MB_MAXBYTES + 1]; 1836 1837 /* Don't draw right halve of a double-width UTF-8 char. "cannot happen" */ 1838 if (enc_utf8 && ScreenLines[off] == 0) 1839 return OK; 1840 1841 if (enc_utf8 && ScreenLinesUC[off] != 0) 1842 /* Draw UTF-8 multi-byte character. */ 1843 return gui_outstr_nowrap(buf, utfc_char2bytes(off, buf), 1844 flags, fg, bg, back); 1845 1846 if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 1847 { 1848 buf[0] = ScreenLines[off]; 1849 buf[1] = ScreenLines2[off]; 1850 return gui_outstr_nowrap(buf, 2, flags, fg, bg, back); 1851 } 1852 1853 /* Draw non-multi-byte character or DBCS character. */ 1854 return gui_outstr_nowrap(ScreenLines + off, 1855 enc_dbcs ? (*mb_ptr2len)(ScreenLines + off) : 1, 1856 flags, fg, bg, back); 1857 #else 1858 return gui_outstr_nowrap(ScreenLines + off, 1, flags, fg, bg, back); 1859 #endif 1860 } 1861 1862 #ifdef HAVE_GTK2 1863 /* 1864 * Output the string at the given screen position. This is used in place 1865 * of gui_screenchar() where possible because Pango needs as much context 1866 * as possible to work nicely. It's a lot faster as well. 1867 */ 1868 static int 1869 gui_screenstr(off, len, flags, fg, bg, back) 1870 int off; /* Offset from start of screen */ 1871 int len; /* string length in screen cells */ 1872 int flags; 1873 guicolor_T fg, bg; /* colors for cursor */ 1874 int back; /* backup this many chars when using bold trick */ 1875 { 1876 char_u *buf; 1877 int outlen = 0; 1878 int i; 1879 int retval; 1880 1881 if (len <= 0) /* "cannot happen"? */ 1882 return OK; 1883 1884 if (enc_utf8) 1885 { 1886 buf = alloc((unsigned)(len * MB_MAXBYTES + 1)); 1887 if (buf == NULL) 1888 return OK; /* not much we could do here... */ 1889 1890 for (i = off; i < off + len; ++i) 1891 { 1892 if (ScreenLines[i] == 0) 1893 continue; /* skip second half of double-width char */ 1894 1895 if (ScreenLinesUC[i] == 0) 1896 buf[outlen++] = ScreenLines[i]; 1897 else 1898 outlen += utfc_char2bytes(i, buf + outlen); 1899 } 1900 1901 buf[outlen] = NUL; /* only to aid debugging */ 1902 retval = gui_outstr_nowrap(buf, outlen, flags, fg, bg, back); 1903 vim_free(buf); 1904 1905 return retval; 1906 } 1907 else if (enc_dbcs == DBCS_JPNU) 1908 { 1909 buf = alloc((unsigned)(len * 2 + 1)); 1910 if (buf == NULL) 1911 return OK; /* not much we could do here... */ 1912 1913 for (i = off; i < off + len; ++i) 1914 { 1915 buf[outlen++] = ScreenLines[i]; 1916 1917 /* handle double-byte single-width char */ 1918 if (ScreenLines[i] == 0x8e) 1919 buf[outlen++] = ScreenLines2[i]; 1920 else if (MB_BYTE2LEN(ScreenLines[i]) == 2) 1921 buf[outlen++] = ScreenLines[++i]; 1922 } 1923 1924 buf[outlen] = NUL; /* only to aid debugging */ 1925 retval = gui_outstr_nowrap(buf, outlen, flags, fg, bg, back); 1926 vim_free(buf); 1927 1928 return retval; 1929 } 1930 else 1931 { 1932 return gui_outstr_nowrap(&ScreenLines[off], len, 1933 flags, fg, bg, back); 1934 } 1935 } 1936 #endif /* HAVE_GTK2 */ 1937 1938 /* 1939 * Output the given string at the current cursor position. If the string is 1940 * too long to fit on the line, then it is truncated. 1941 * "flags": 1942 * GUI_MON_IS_CURSOR should only be used when this function is being called to 1943 * actually draw (an inverted) cursor. 1944 * GUI_MON_TRS_CURSOR is used to draw the cursor text with a transparant 1945 * background. 1946 * GUI_MON_NOCLEAR is used to avoid clearing the selection when drawing over 1947 * it. 1948 * Returns OK, unless "back" is non-zero and using the bold trick, then return 1949 * FAIL (the caller should start drawing "back" chars back). 1950 */ 1951 int 1952 gui_outstr_nowrap(s, len, flags, fg, bg, back) 1953 char_u *s; 1954 int len; 1955 int flags; 1956 guicolor_T fg, bg; /* colors for cursor */ 1957 int back; /* backup this many chars when using bold trick */ 1958 { 1959 long_u highlight_mask; 1960 long_u hl_mask_todo; 1961 guicolor_T fg_color; 1962 guicolor_T bg_color; 1963 guicolor_T sp_color; 1964 #if !defined(MSWIN16_FASTTEXT) && !defined(HAVE_GTK2) 1965 GuiFont font = NOFONT; 1966 # ifdef FEAT_XFONTSET 1967 GuiFontset fontset = NOFONTSET; 1968 # endif 1969 #endif 1970 attrentry_T *aep = NULL; 1971 int draw_flags; 1972 int col = gui.col; 1973 #ifdef FEAT_SIGN_ICONS 1974 int draw_sign = FALSE; 1975 # ifdef FEAT_NETBEANS_INTG 1976 int multi_sign = FALSE; 1977 # endif 1978 #endif 1979 1980 if (len < 0) 1981 len = (int)STRLEN(s); 1982 if (len == 0) 1983 return OK; 1984 1985 #ifdef FEAT_SIGN_ICONS 1986 if (*s == SIGN_BYTE 1987 # ifdef FEAT_NETBEANS_INTG 1988 || *s == MULTISIGN_BYTE 1989 # endif 1990 ) 1991 { 1992 # ifdef FEAT_NETBEANS_INTG 1993 if (*s == MULTISIGN_BYTE) 1994 multi_sign = TRUE; 1995 # endif 1996 /* draw spaces instead */ 1997 s = (char_u *)" "; 1998 if (len == 1 && col > 0) 1999 --col; 2000 len = 2; 2001 draw_sign = TRUE; 2002 highlight_mask = 0; 2003 } 2004 else 2005 #endif 2006 if (gui.highlight_mask > HL_ALL) 2007 { 2008 aep = syn_gui_attr2entry(gui.highlight_mask); 2009 if (aep == NULL) /* highlighting not set */ 2010 highlight_mask = 0; 2011 else 2012 highlight_mask = aep->ae_attr; 2013 } 2014 else 2015 highlight_mask = gui.highlight_mask; 2016 hl_mask_todo = highlight_mask; 2017 2018 #if !defined(MSWIN16_FASTTEXT) && !defined(HAVE_GTK2) 2019 /* Set the font */ 2020 if (aep != NULL && aep->ae_u.gui.font != NOFONT) 2021 font = aep->ae_u.gui.font; 2022 # ifdef FEAT_XFONTSET 2023 else if (aep != NULL && aep->ae_u.gui.fontset != NOFONTSET) 2024 fontset = aep->ae_u.gui.fontset; 2025 # endif 2026 else 2027 { 2028 # ifdef FEAT_XFONTSET 2029 if (gui.fontset != NOFONTSET) 2030 fontset = gui.fontset; 2031 else 2032 # endif 2033 if (hl_mask_todo & (HL_BOLD | HL_STANDOUT)) 2034 { 2035 if ((hl_mask_todo & HL_ITALIC) && gui.boldital_font != NOFONT) 2036 { 2037 font = gui.boldital_font; 2038 hl_mask_todo &= ~(HL_BOLD | HL_STANDOUT | HL_ITALIC); 2039 } 2040 else if (gui.bold_font != NOFONT) 2041 { 2042 font = gui.bold_font; 2043 hl_mask_todo &= ~(HL_BOLD | HL_STANDOUT); 2044 } 2045 else 2046 font = gui.norm_font; 2047 } 2048 else if ((hl_mask_todo & HL_ITALIC) && gui.ital_font != NOFONT) 2049 { 2050 font = gui.ital_font; 2051 hl_mask_todo &= ~HL_ITALIC; 2052 } 2053 else 2054 font = gui.norm_font; 2055 } 2056 # ifdef FEAT_XFONTSET 2057 if (fontset != NOFONTSET) 2058 gui_mch_set_fontset(fontset); 2059 else 2060 # endif 2061 gui_mch_set_font(font); 2062 #endif 2063 2064 draw_flags = 0; 2065 2066 /* Set the color */ 2067 bg_color = gui.back_pixel; 2068 if ((flags & GUI_MON_IS_CURSOR) && gui.in_focus) 2069 { 2070 draw_flags |= DRAW_CURSOR; 2071 fg_color = fg; 2072 bg_color = bg; 2073 sp_color = fg; 2074 } 2075 else if (aep != NULL) 2076 { 2077 fg_color = aep->ae_u.gui.fg_color; 2078 if (fg_color == INVALCOLOR) 2079 fg_color = gui.norm_pixel; 2080 bg_color = aep->ae_u.gui.bg_color; 2081 if (bg_color == INVALCOLOR) 2082 bg_color = gui.back_pixel; 2083 sp_color = aep->ae_u.gui.sp_color; 2084 if (sp_color == INVALCOLOR) 2085 sp_color = fg_color; 2086 } 2087 else 2088 { 2089 fg_color = gui.norm_pixel; 2090 sp_color = fg_color; 2091 } 2092 2093 if (highlight_mask & (HL_INVERSE | HL_STANDOUT)) 2094 { 2095 #if defined(AMIGA) || defined(RISCOS) 2096 gui_mch_set_colors(bg_color, fg_color); 2097 #else 2098 gui_mch_set_fg_color(bg_color); 2099 gui_mch_set_bg_color(fg_color); 2100 #endif 2101 } 2102 else 2103 { 2104 #if defined(AMIGA) || defined(RISCOS) 2105 gui_mch_set_colors(fg_color, bg_color); 2106 #else 2107 gui_mch_set_fg_color(fg_color); 2108 gui_mch_set_bg_color(bg_color); 2109 #endif 2110 } 2111 gui_mch_set_sp_color(sp_color); 2112 2113 /* Clear the selection if we are about to write over it */ 2114 if (!(flags & GUI_MON_NOCLEAR)) 2115 clip_may_clear_selection(gui.row, gui.row); 2116 2117 2118 #ifndef MSWIN16_FASTTEXT 2119 /* If there's no bold font, then fake it */ 2120 if (hl_mask_todo & (HL_BOLD | HL_STANDOUT)) 2121 draw_flags |= DRAW_BOLD; 2122 #endif 2123 2124 /* 2125 * When drawing bold or italic characters the spill-over from the left 2126 * neighbor may be destroyed. Let the caller backup to start redrawing 2127 * just after a blank. 2128 */ 2129 if (back != 0 && ((draw_flags & DRAW_BOLD) || (highlight_mask & HL_ITALIC))) 2130 return FAIL; 2131 2132 #if defined(RISCOS) || defined(HAVE_GTK2) 2133 /* If there's no italic font, then fake it. 2134 * For GTK2, we don't need a different font for italic style. */ 2135 if (hl_mask_todo & HL_ITALIC) 2136 draw_flags |= DRAW_ITALIC; 2137 2138 /* Do we underline the text? */ 2139 if (hl_mask_todo & HL_UNDERLINE) 2140 draw_flags |= DRAW_UNDERL; 2141 #else 2142 /* Do we underline the text? */ 2143 if ((hl_mask_todo & HL_UNDERLINE) 2144 # ifndef MSWIN16_FASTTEXT 2145 || (hl_mask_todo & HL_ITALIC) 2146 # endif 2147 ) 2148 draw_flags |= DRAW_UNDERL; 2149 #endif 2150 /* Do we undercurl the text? */ 2151 if (hl_mask_todo & HL_UNDERCURL) 2152 draw_flags |= DRAW_UNDERC; 2153 2154 /* Do we draw transparantly? */ 2155 if (flags & GUI_MON_TRS_CURSOR) 2156 draw_flags |= DRAW_TRANSP; 2157 2158 /* 2159 * Draw the text. 2160 */ 2161 #ifdef HAVE_GTK2 2162 /* The value returned is the length in display cells */ 2163 len = gui_gtk2_draw_string(gui.row, col, s, len, draw_flags); 2164 #else 2165 # ifdef FEAT_MBYTE 2166 if (enc_utf8) 2167 { 2168 int start; /* index of bytes to be drawn */ 2169 int cells; /* cellwidth of bytes to be drawn */ 2170 int thislen; /* length of bytes to be drawin */ 2171 int cn; /* cellwidth of current char */ 2172 int i; /* index of current char */ 2173 int c; /* current char value */ 2174 int cl; /* byte length of current char */ 2175 int comping; /* current char is composing */ 2176 int scol = col; /* screen column */ 2177 int dowide; /* use 'guifontwide' */ 2178 2179 /* Break the string at a composing character, it has to be drawn on 2180 * top of the previous character. */ 2181 start = 0; 2182 cells = 0; 2183 for (i = 0; i < len; i += cl) 2184 { 2185 c = utf_ptr2char(s + i); 2186 cn = utf_char2cells(c); 2187 if (cn > 1 2188 # ifdef FEAT_XFONTSET 2189 && fontset == NOFONTSET 2190 # endif 2191 && gui.wide_font != NOFONT) 2192 dowide = TRUE; 2193 else 2194 dowide = FALSE; 2195 comping = utf_iscomposing(c); 2196 if (!comping) /* count cells from non-composing chars */ 2197 cells += cn; 2198 cl = utf_ptr2len(s + i); 2199 if (cl == 0) /* hit end of string */ 2200 len = i + cl; /* len must be wrong "cannot happen" */ 2201 2202 /* print the string so far if it's the last character or there is 2203 * a composing character. */ 2204 if (i + cl >= len || (comping && i > start) || dowide 2205 # if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) 2206 || (cn > 1 2207 # ifdef FEAT_XFONTSET 2208 /* No fontset: At least draw char after wide char at 2209 * right position. */ 2210 && fontset == NOFONTSET 2211 # endif 2212 ) 2213 # endif 2214 ) 2215 { 2216 if (comping || dowide) 2217 thislen = i - start; 2218 else 2219 thislen = i - start + cl; 2220 if (thislen > 0) 2221 { 2222 gui_mch_draw_string(gui.row, scol, s + start, thislen, 2223 draw_flags); 2224 start += thislen; 2225 } 2226 scol += cells; 2227 cells = 0; 2228 if (dowide) 2229 { 2230 gui_mch_set_font(gui.wide_font); 2231 gui_mch_draw_string(gui.row, scol - cn, 2232 s + start, cl, draw_flags); 2233 gui_mch_set_font(font); 2234 start += cl; 2235 } 2236 2237 # if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) 2238 /* No fontset: draw a space to fill the gap after a wide char 2239 * */ 2240 if (cn > 1 && (draw_flags & DRAW_TRANSP) == 0 2241 # ifdef FEAT_XFONTSET 2242 && fontset == NOFONTSET 2243 # endif 2244 && !dowide) 2245 gui_mch_draw_string(gui.row, scol - 1, (char_u *)" ", 2246 1, draw_flags); 2247 # endif 2248 } 2249 /* Draw a composing char on top of the previous char. */ 2250 if (comping) 2251 { 2252 # if (defined(__APPLE_CC__) || defined(__MRC__)) && TARGET_API_MAC_CARBON 2253 /* Carbon ATSUI autodraws composing char over previous char */ 2254 gui_mch_draw_string(gui.row, scol, s + i, cl, 2255 draw_flags | DRAW_TRANSP); 2256 # else 2257 gui_mch_draw_string(gui.row, scol - cn, s + i, cl, 2258 draw_flags | DRAW_TRANSP); 2259 # endif 2260 start = i + cl; 2261 } 2262 } 2263 /* The stuff below assumes "len" is the length in screen columns. */ 2264 len = scol - col; 2265 } 2266 else 2267 # endif 2268 { 2269 gui_mch_draw_string(gui.row, col, s, len, draw_flags); 2270 # ifdef FEAT_MBYTE 2271 if (enc_dbcs == DBCS_JPNU) 2272 { 2273 int clen = 0; 2274 int i; 2275 2276 /* Get the length in display cells, this can be different from the 2277 * number of bytes for "euc-jp". */ 2278 for (i = 0; i < len; i += (*mb_ptr2len)(s + i)) 2279 clen += (*mb_ptr2cells)(s + i); 2280 len = clen; 2281 } 2282 # endif 2283 } 2284 #endif /* !HAVE_GTK2 */ 2285 2286 if (!(flags & (GUI_MON_IS_CURSOR | GUI_MON_TRS_CURSOR))) 2287 gui.col = col + len; 2288 2289 /* May need to invert it when it's part of the selection. */ 2290 if (flags & GUI_MON_NOCLEAR) 2291 clip_may_redraw_selection(gui.row, col, len); 2292 2293 if (!(flags & (GUI_MON_IS_CURSOR | GUI_MON_TRS_CURSOR))) 2294 { 2295 /* Invalidate the old physical cursor position if we wrote over it */ 2296 if (gui.cursor_row == gui.row 2297 && gui.cursor_col >= col 2298 && gui.cursor_col < col + len) 2299 gui.cursor_is_valid = FALSE; 2300 } 2301 2302 #ifdef FEAT_SIGN_ICONS 2303 if (draw_sign) 2304 /* Draw the sign on top of the spaces. */ 2305 gui_mch_drawsign(gui.row, col, gui.highlight_mask); 2306 # ifdef FEAT_NETBEANS_INTG 2307 if (multi_sign) 2308 netbeans_draw_multisign_indicator(gui.row); 2309 # endif 2310 #endif 2311 2312 return OK; 2313 } 2314 2315 /* 2316 * Un-draw the cursor. Actually this just redraws the character at the given 2317 * position. The character just before it too, for when it was in bold. 2318 */ 2319 void 2320 gui_undraw_cursor() 2321 { 2322 if (gui.cursor_is_valid) 2323 { 2324 #ifdef FEAT_HANGULIN 2325 if (composing_hangul 2326 && gui.col == gui.cursor_col && gui.row == gui.cursor_row) 2327 (void)gui_outstr_nowrap(composing_hangul_buffer, 2, 2328 GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, 2329 gui.norm_pixel, gui.back_pixel, 0); 2330 else 2331 { 2332 #endif 2333 if (gui_redraw_block(gui.cursor_row, gui.cursor_col, 2334 gui.cursor_row, gui.cursor_col, GUI_MON_NOCLEAR) 2335 && gui.cursor_col > 0) 2336 (void)gui_redraw_block(gui.cursor_row, gui.cursor_col - 1, 2337 gui.cursor_row, gui.cursor_col - 1, GUI_MON_NOCLEAR); 2338 #ifdef FEAT_HANGULIN 2339 if (composing_hangul) 2340 (void)gui_redraw_block(gui.cursor_row, gui.cursor_col + 1, 2341 gui.cursor_row, gui.cursor_col + 1, GUI_MON_NOCLEAR); 2342 } 2343 #endif 2344 /* Cursor_is_valid is reset when the cursor is undrawn, also reset it 2345 * here in case it wasn't needed to undraw it. */ 2346 gui.cursor_is_valid = FALSE; 2347 } 2348 } 2349 2350 void 2351 gui_redraw(x, y, w, h) 2352 int x; 2353 int y; 2354 int w; 2355 int h; 2356 { 2357 int row1, col1, row2, col2; 2358 2359 row1 = Y_2_ROW(y); 2360 col1 = X_2_COL(x); 2361 row2 = Y_2_ROW(y + h - 1); 2362 col2 = X_2_COL(x + w - 1); 2363 2364 (void)gui_redraw_block(row1, col1, row2, col2, GUI_MON_NOCLEAR); 2365 2366 /* 2367 * We may need to redraw the cursor, but don't take it upon us to change 2368 * its location after a scroll. 2369 * (maybe be more strict even and test col too?) 2370 * These things may be outside the update/clipping region and reality may 2371 * not reflect Vims internal ideas if these operations are clipped away. 2372 */ 2373 if (gui.row == gui.cursor_row) 2374 gui_update_cursor(TRUE, TRUE); 2375 } 2376 2377 /* 2378 * Draw a rectangular block of characters, from row1 to row2 (inclusive) and 2379 * from col1 to col2 (inclusive). 2380 * Return TRUE when the character before the first drawn character has 2381 * different attributes (may have to be redrawn too). 2382 */ 2383 int 2384 gui_redraw_block(row1, col1, row2, col2, flags) 2385 int row1; 2386 int col1; 2387 int row2; 2388 int col2; 2389 int flags; /* flags for gui_outstr_nowrap() */ 2390 { 2391 int old_row, old_col; 2392 long_u old_hl_mask; 2393 int off; 2394 sattr_T first_attr; 2395 int idx, len; 2396 int back, nback; 2397 int retval = FALSE; 2398 #ifdef FEAT_MBYTE 2399 int orig_col1, orig_col2; 2400 #endif 2401 2402 /* Don't try to update when ScreenLines is not valid */ 2403 if (!screen_cleared || ScreenLines == NULL) 2404 return retval; 2405 2406 /* Don't try to draw outside the shell! */ 2407 /* Check everything, strange values may be caused by a big border width */ 2408 col1 = check_col(col1); 2409 col2 = check_col(col2); 2410 row1 = check_row(row1); 2411 row2 = check_row(row2); 2412 2413 /* Remember where our cursor was */ 2414 old_row = gui.row; 2415 old_col = gui.col; 2416 old_hl_mask = gui.highlight_mask; 2417 #ifdef FEAT_MBYTE 2418 orig_col1 = col1; 2419 orig_col2 = col2; 2420 #endif 2421 2422 for (gui.row = row1; gui.row <= row2; gui.row++) 2423 { 2424 #ifdef FEAT_MBYTE 2425 /* When only half of a double-wide character is in the block, include 2426 * the other half. */ 2427 col1 = orig_col1; 2428 col2 = orig_col2; 2429 off = LineOffset[gui.row]; 2430 if (enc_dbcs != 0) 2431 { 2432 if (col1 > 0) 2433 col1 -= dbcs_screen_head_off(ScreenLines + off, 2434 ScreenLines + off + col1); 2435 col2 += dbcs_screen_tail_off(ScreenLines + off, 2436 ScreenLines + off + col2); 2437 } 2438 else if (enc_utf8) 2439 { 2440 if (ScreenLines[off + col1] == 0) 2441 --col1; 2442 # ifdef HAVE_GTK2 2443 if (col2 + 1 < Columns && ScreenLines[off + col2 + 1] == 0) 2444 ++col2; 2445 # endif 2446 } 2447 #endif 2448 gui.col = col1; 2449 off = LineOffset[gui.row] + gui.col; 2450 len = col2 - col1 + 1; 2451 2452 /* Find how many chars back this highlighting starts, or where a space 2453 * is. Needed for when the bold trick is used */ 2454 for (back = 0; back < col1; ++back) 2455 if (ScreenAttrs[off - 1 - back] != ScreenAttrs[off] 2456 || ScreenLines[off - 1 - back] == ' ') 2457 break; 2458 retval = (col1 > 0 && ScreenAttrs[off - 1] != 0 && back == 0 2459 && ScreenLines[off - 1] != ' '); 2460 2461 /* Break it up in strings of characters with the same attributes. */ 2462 /* Print UTF-8 characters individually. */ 2463 while (len > 0) 2464 { 2465 first_attr = ScreenAttrs[off]; 2466 gui.highlight_mask = first_attr; 2467 #if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) 2468 if (enc_utf8 && ScreenLinesUC[off] != 0) 2469 { 2470 /* output multi-byte character separately */ 2471 nback = gui_screenchar(off, flags, 2472 (guicolor_T)0, (guicolor_T)0, back); 2473 if (gui.col < Columns && ScreenLines[off + 1] == 0) 2474 idx = 2; 2475 else 2476 idx = 1; 2477 } 2478 else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 2479 { 2480 /* output double-byte, single-width character separately */ 2481 nback = gui_screenchar(off, flags, 2482 (guicolor_T)0, (guicolor_T)0, back); 2483 idx = 1; 2484 } 2485 else 2486 #endif 2487 { 2488 #ifdef HAVE_GTK2 2489 for (idx = 0; idx < len; ++idx) 2490 { 2491 if (enc_utf8 && ScreenLines[off + idx] == 0) 2492 continue; /* skip second half of double-width char */ 2493 if (ScreenAttrs[off + idx] != first_attr) 2494 break; 2495 } 2496 /* gui_screenstr() takes care of multibyte chars */ 2497 nback = gui_screenstr(off, idx, flags, 2498 (guicolor_T)0, (guicolor_T)0, back); 2499 #else 2500 for (idx = 0; idx < len && ScreenAttrs[off + idx] == first_attr; 2501 idx++) 2502 { 2503 # ifdef FEAT_MBYTE 2504 /* Stop at a multi-byte Unicode character. */ 2505 if (enc_utf8 && ScreenLinesUC[off + idx] != 0) 2506 break; 2507 if (enc_dbcs == DBCS_JPNU) 2508 { 2509 /* Stop at a double-byte single-width char. */ 2510 if (ScreenLines[off + idx] == 0x8e) 2511 break; 2512 if (len > 1 && (*mb_ptr2len)(ScreenLines 2513 + off + idx) == 2) 2514 ++idx; /* skip second byte of double-byte char */ 2515 } 2516 # endif 2517 } 2518 nback = gui_outstr_nowrap(ScreenLines + off, idx, flags, 2519 (guicolor_T)0, (guicolor_T)0, back); 2520 #endif 2521 } 2522 if (nback == FAIL) 2523 { 2524 /* Must back up to start drawing where a bold or italic word 2525 * starts. */ 2526 off -= back; 2527 len += back; 2528 gui.col -= back; 2529 } 2530 else 2531 { 2532 off += idx; 2533 len -= idx; 2534 } 2535 back = 0; 2536 } 2537 } 2538 2539 /* Put the cursor back where it was */ 2540 gui.row = old_row; 2541 gui.col = old_col; 2542 gui.highlight_mask = old_hl_mask; 2543 2544 return retval; 2545 } 2546 2547 static void 2548 gui_delete_lines(row, count) 2549 int row; 2550 int count; 2551 { 2552 if (count <= 0) 2553 return; 2554 2555 if (row + count > gui.scroll_region_bot) 2556 /* Scrolled out of region, just blank the lines out */ 2557 gui_clear_block(row, gui.scroll_region_left, 2558 gui.scroll_region_bot, gui.scroll_region_right); 2559 else 2560 { 2561 gui_mch_delete_lines(row, count); 2562 2563 /* If the cursor was in the deleted lines it's now gone. If the 2564 * cursor was in the scrolled lines adjust its position. */ 2565 if (gui.cursor_row >= row 2566 && gui.cursor_col >= gui.scroll_region_left 2567 && gui.cursor_col <= gui.scroll_region_right) 2568 { 2569 if (gui.cursor_row < row + count) 2570 gui.cursor_is_valid = FALSE; 2571 else if (gui.cursor_row <= gui.scroll_region_bot) 2572 gui.cursor_row -= count; 2573 } 2574 } 2575 } 2576 2577 static void 2578 gui_insert_lines(row, count) 2579 int row; 2580 int count; 2581 { 2582 if (count <= 0) 2583 return; 2584 2585 if (row + count > gui.scroll_region_bot) 2586 /* Scrolled out of region, just blank the lines out */ 2587 gui_clear_block(row, gui.scroll_region_left, 2588 gui.scroll_region_bot, gui.scroll_region_right); 2589 else 2590 { 2591 gui_mch_insert_lines(row, count); 2592 2593 if (gui.cursor_row >= gui.row 2594 && gui.cursor_col >= gui.scroll_region_left 2595 && gui.cursor_col <= gui.scroll_region_right) 2596 { 2597 if (gui.cursor_row <= gui.scroll_region_bot - count) 2598 gui.cursor_row += count; 2599 else if (gui.cursor_row <= gui.scroll_region_bot) 2600 gui.cursor_is_valid = FALSE; 2601 } 2602 } 2603 } 2604 2605 /* 2606 * The main GUI input routine. Waits for a character from the keyboard. 2607 * wtime == -1 Wait forever. 2608 * wtime == 0 Don't wait. 2609 * wtime > 0 Wait wtime milliseconds for a character. 2610 * Returns OK if a character was found to be available within the given time, 2611 * or FAIL otherwise. 2612 */ 2613 int 2614 gui_wait_for_chars(wtime) 2615 long wtime; 2616 { 2617 int retval; 2618 2619 /* 2620 * If we're going to wait a bit, update the menus and mouse shape for the 2621 * current State. 2622 */ 2623 if (wtime != 0) 2624 { 2625 #ifdef FEAT_MENU 2626 gui_update_menus(0); 2627 #endif 2628 } 2629 2630 gui_mch_update(); 2631 if (input_available()) /* Got char, return immediately */ 2632 return OK; 2633 if (wtime == 0) /* Don't wait for char */ 2634 return FAIL; 2635 2636 /* Before waiting, flush any output to the screen. */ 2637 gui_mch_flush(); 2638 2639 if (wtime > 0) 2640 { 2641 /* Blink when waiting for a character. Probably only does something 2642 * for showmatch() */ 2643 gui_mch_start_blink(); 2644 retval = gui_mch_wait_for_chars(wtime); 2645 gui_mch_stop_blink(); 2646 return retval; 2647 } 2648 2649 /* 2650 * While we are waiting indefenitely for a character, blink the cursor. 2651 */ 2652 gui_mch_start_blink(); 2653 2654 retval = FAIL; 2655 /* 2656 * We may want to trigger the CursorHold event. First wait for 2657 * 'updatetime' and if nothing is typed within that time put the 2658 * K_CURSORHOLD key in the input buffer. 2659 */ 2660 if (gui_mch_wait_for_chars(p_ut) == OK) 2661 retval = OK; 2662 #ifdef FEAT_AUTOCMD 2663 else if (trigger_cursorhold()) 2664 { 2665 char_u buf[3]; 2666 2667 /* Put K_CURSORHOLD in the input buffer. */ 2668 buf[0] = CSI; 2669 buf[1] = KS_EXTRA; 2670 buf[2] = (int)KE_CURSORHOLD; 2671 add_to_input_buf(buf, 3); 2672 2673 retval = OK; 2674 } 2675 #endif 2676 2677 if (retval == FAIL) 2678 { 2679 /* Blocking wait. */ 2680 before_blocking(); 2681 retval = gui_mch_wait_for_chars(-1L); 2682 } 2683 2684 gui_mch_stop_blink(); 2685 return retval; 2686 } 2687 2688 /* 2689 * Fill buffer with mouse coordinates encoded for check_termcode(). 2690 */ 2691 static void 2692 fill_mouse_coord(p, col, row) 2693 char_u *p; 2694 int col; 2695 int row; 2696 { 2697 p[0] = (char_u)(col / 128 + ' ' + 1); 2698 p[1] = (char_u)(col % 128 + ' ' + 1); 2699 p[2] = (char_u)(row / 128 + ' ' + 1); 2700 p[3] = (char_u)(row % 128 + ' ' + 1); 2701 } 2702 2703 /* 2704 * Generic mouse support function. Add a mouse event to the input buffer with 2705 * the given properties. 2706 * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, 2707 * MOUSE_X1, MOUSE_X2 2708 * MOUSE_DRAG, or MOUSE_RELEASE. 2709 * MOUSE_4 and MOUSE_5 are used for a scroll wheel. 2710 * x, y --- Coordinates of mouse in pixels. 2711 * repeated_click --- TRUE if this click comes only a short time after a 2712 * previous click. 2713 * modifiers --- Bit field which may be any of the following modifiers 2714 * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT. 2715 * This function will ignore drag events where the mouse has not moved to a new 2716 * character. 2717 */ 2718 void 2719 gui_send_mouse_event(button, x, y, repeated_click, modifiers) 2720 int button; 2721 int x; 2722 int y; 2723 int repeated_click; 2724 int_u modifiers; 2725 { 2726 static int prev_row = 0, prev_col = 0; 2727 static int prev_button = -1; 2728 static int num_clicks = 1; 2729 char_u string[10]; 2730 enum key_extra button_char; 2731 int row, col; 2732 #ifdef FEAT_CLIPBOARD 2733 int checkfor; 2734 int did_clip = FALSE; 2735 #endif 2736 2737 /* 2738 * Scrolling may happen at any time, also while a selection is present. 2739 */ 2740 switch (button) 2741 { 2742 case MOUSE_X1: 2743 button_char = KE_X1MOUSE; 2744 goto button_set; 2745 case MOUSE_X2: 2746 button_char = KE_X2MOUSE; 2747 goto button_set; 2748 case MOUSE_4: 2749 button_char = KE_MOUSEDOWN; 2750 goto button_set; 2751 case MOUSE_5: 2752 button_char = KE_MOUSEUP; 2753 button_set: 2754 { 2755 /* Don't put events in the input queue now. */ 2756 if (hold_gui_events) 2757 return; 2758 2759 string[3] = CSI; 2760 string[4] = KS_EXTRA; 2761 string[5] = (int)button_char; 2762 2763 /* Pass the pointer coordinates of the scroll event so that we 2764 * know which window to scroll. */ 2765 row = gui_xy2colrow(x, y, &col); 2766 string[6] = (char_u)(col / 128 + ' ' + 1); 2767 string[7] = (char_u)(col % 128 + ' ' + 1); 2768 string[8] = (char_u)(row / 128 + ' ' + 1); 2769 string[9] = (char_u)(row % 128 + ' ' + 1); 2770 2771 if (modifiers == 0) 2772 add_to_input_buf(string + 3, 7); 2773 else 2774 { 2775 string[0] = CSI; 2776 string[1] = KS_MODIFIER; 2777 string[2] = 0; 2778 if (modifiers & MOUSE_SHIFT) 2779 string[2] |= MOD_MASK_SHIFT; 2780 if (modifiers & MOUSE_CTRL) 2781 string[2] |= MOD_MASK_CTRL; 2782 if (modifiers & MOUSE_ALT) 2783 string[2] |= MOD_MASK_ALT; 2784 add_to_input_buf(string, 10); 2785 } 2786 return; 2787 } 2788 } 2789 2790 #ifdef FEAT_CLIPBOARD 2791 /* If a clipboard selection is in progress, handle it */ 2792 if (clip_star.state == SELECT_IN_PROGRESS) 2793 { 2794 clip_process_selection(button, X_2_COL(x), Y_2_ROW(y), repeated_click); 2795 return; 2796 } 2797 2798 /* Determine which mouse settings to look for based on the current mode */ 2799 switch (get_real_state()) 2800 { 2801 case NORMAL_BUSY: 2802 case OP_PENDING: 2803 case NORMAL: checkfor = MOUSE_NORMAL; break; 2804 case VISUAL: checkfor = MOUSE_VISUAL; break; 2805 case SELECTMODE: checkfor = MOUSE_VISUAL; break; 2806 case REPLACE: 2807 case REPLACE+LANGMAP: 2808 #ifdef FEAT_VREPLACE 2809 case VREPLACE: 2810 case VREPLACE+LANGMAP: 2811 #endif 2812 case INSERT: 2813 case INSERT+LANGMAP: checkfor = MOUSE_INSERT; break; 2814 case ASKMORE: 2815 case HITRETURN: /* At the more- and hit-enter prompt pass the 2816 mouse event for a click on or below the 2817 message line. */ 2818 if (Y_2_ROW(y) >= msg_row) 2819 checkfor = MOUSE_NORMAL; 2820 else 2821 checkfor = MOUSE_RETURN; 2822 break; 2823 2824 /* 2825 * On the command line, use the clipboard selection on all lines 2826 * but the command line. But not when pasting. 2827 */ 2828 case CMDLINE: 2829 case CMDLINE+LANGMAP: 2830 if (Y_2_ROW(y) < cmdline_row && button != MOUSE_MIDDLE) 2831 checkfor = MOUSE_NONE; 2832 else 2833 checkfor = MOUSE_COMMAND; 2834 break; 2835 2836 default: 2837 checkfor = MOUSE_NONE; 2838 break; 2839 }; 2840 2841 /* 2842 * Allow clipboard selection of text on the command line in "normal" 2843 * modes. Don't do this when dragging the status line, or extending a 2844 * Visual selection. 2845 */ 2846 if ((State == NORMAL || State == NORMAL_BUSY || (State & INSERT)) 2847 && Y_2_ROW(y) >= topframe->fr_height 2848 && button != MOUSE_DRAG 2849 # ifdef FEAT_MOUSESHAPE 2850 && !drag_status_line 2851 # ifdef FEAT_VERTSPLIT 2852 && !drag_sep_line 2853 # endif 2854 # endif 2855 ) 2856 checkfor = MOUSE_NONE; 2857 2858 /* 2859 * Use modeless selection when holding CTRL and SHIFT pressed. 2860 */ 2861 if ((modifiers & MOUSE_CTRL) && (modifiers & MOUSE_SHIFT)) 2862 checkfor = MOUSE_NONEF; 2863 2864 /* 2865 * In Ex mode, always use modeless selection. 2866 */ 2867 if (exmode_active) 2868 checkfor = MOUSE_NONE; 2869 2870 /* 2871 * If the mouse settings say to not use the mouse, use the modeless 2872 * selection. But if Visual is active, assume that only the Visual area 2873 * will be selected. 2874 * Exception: On the command line, both the selection is used and a mouse 2875 * key is send. 2876 */ 2877 if (!mouse_has(checkfor) || checkfor == MOUSE_COMMAND) 2878 { 2879 #ifdef FEAT_VISUAL 2880 /* Don't do modeless selection in Visual mode. */ 2881 if (checkfor != MOUSE_NONEF && VIsual_active && (State & NORMAL)) 2882 return; 2883 #endif 2884 2885 /* 2886 * When 'mousemodel' is "popup", shift-left is translated to right. 2887 * But not when also using Ctrl. 2888 */ 2889 if (mouse_model_popup() && button == MOUSE_LEFT 2890 && (modifiers & MOUSE_SHIFT) && !(modifiers & MOUSE_CTRL)) 2891 { 2892 button = MOUSE_RIGHT; 2893 modifiers &= ~ MOUSE_SHIFT; 2894 } 2895 2896 /* If the selection is done, allow the right button to extend it. 2897 * If the selection is cleared, allow the right button to start it 2898 * from the cursor position. */ 2899 if (button == MOUSE_RIGHT) 2900 { 2901 if (clip_star.state == SELECT_CLEARED) 2902 { 2903 if (State & CMDLINE) 2904 { 2905 col = msg_col; 2906 row = msg_row; 2907 } 2908 else 2909 { 2910 col = curwin->w_wcol; 2911 row = curwin->w_wrow + W_WINROW(curwin); 2912 } 2913 clip_start_selection(col, row, FALSE); 2914 } 2915 clip_process_selection(button, X_2_COL(x), Y_2_ROW(y), 2916 repeated_click); 2917 did_clip = TRUE; 2918 } 2919 /* Allow the left button to start the selection */ 2920 else if (button == 2921 # ifdef RISCOS 2922 /* Only start a drag on a drag event. Otherwise 2923 * we don't get a release event. */ 2924 MOUSE_DRAG 2925 # else 2926 MOUSE_LEFT 2927 # endif 2928 ) 2929 { 2930 clip_start_selection(X_2_COL(x), Y_2_ROW(y), repeated_click); 2931 did_clip = TRUE; 2932 } 2933 # ifdef RISCOS 2934 else if (button == MOUSE_LEFT) 2935 { 2936 clip_clear_selection(); 2937 did_clip = TRUE; 2938 } 2939 # endif 2940 2941 /* Always allow pasting */ 2942 if (button != MOUSE_MIDDLE) 2943 { 2944 if (!mouse_has(checkfor) || button == MOUSE_RELEASE) 2945 return; 2946 if (checkfor != MOUSE_COMMAND) 2947 button = MOUSE_LEFT; 2948 } 2949 repeated_click = FALSE; 2950 } 2951 2952 if (clip_star.state != SELECT_CLEARED && !did_clip) 2953 clip_clear_selection(); 2954 #endif 2955 2956 /* Don't put events in the input queue now. */ 2957 if (hold_gui_events) 2958 return; 2959 2960 row = gui_xy2colrow(x, y, &col); 2961 2962 /* 2963 * If we are dragging and the mouse hasn't moved far enough to be on a 2964 * different character, then don't send an event to vim. 2965 */ 2966 if (button == MOUSE_DRAG) 2967 { 2968 if (row == prev_row && col == prev_col) 2969 return; 2970 /* Dragging above the window, set "row" to -1 to cause a scroll. */ 2971 if (y < 0) 2972 row = -1; 2973 } 2974 2975 /* 2976 * If topline has changed (window scrolled) since the last click, reset 2977 * repeated_click, because we don't want starting Visual mode when 2978 * clicking on a different character in the text. 2979 */ 2980 if (curwin->w_topline != gui_prev_topline 2981 #ifdef FEAT_DIFF 2982 || curwin->w_topfill != gui_prev_topfill 2983 #endif 2984 ) 2985 repeated_click = FALSE; 2986 2987 string[0] = CSI; /* this sequence is recognized by check_termcode() */ 2988 string[1] = KS_MOUSE; 2989 string[2] = KE_FILLER; 2990 if (button != MOUSE_DRAG && button != MOUSE_RELEASE) 2991 { 2992 if (repeated_click) 2993 { 2994 /* 2995 * Handle multiple clicks. They only count if the mouse is still 2996 * pointing at the same character. 2997 */ 2998 if (button != prev_button || row != prev_row || col != prev_col) 2999 num_clicks = 1; 3000 else if (++num_clicks > 4) 3001 num_clicks = 1; 3002 } 3003 else 3004 num_clicks = 1; 3005 prev_button = button; 3006 gui_prev_topline = curwin->w_topline; 3007 #ifdef FEAT_DIFF 3008 gui_prev_topfill = curwin->w_topfill; 3009 #endif 3010 3011 string[3] = (char_u)(button | 0x20); 3012 SET_NUM_MOUSE_CLICKS(string[3], num_clicks); 3013 } 3014 else 3015 string[3] = (char_u)button; 3016 3017 string[3] |= modifiers; 3018 fill_mouse_coord(string + 4, col, row); 3019 add_to_input_buf(string, 8); 3020 3021 if (row < 0) 3022 prev_row = 0; 3023 else 3024 prev_row = row; 3025 prev_col = col; 3026 3027 /* 3028 * We need to make sure this is cleared since Athena doesn't tell us when 3029 * he is done dragging. Neither does GTK+ 2 -- at least for now. 3030 */ 3031 #if defined(FEAT_GUI_ATHENA) || defined(HAVE_GTK2) 3032 gui.dragged_sb = SBAR_NONE; 3033 #endif 3034 } 3035 3036 /* 3037 * Convert x and y coordinate to column and row in text window. 3038 * Corrects for multi-byte character. 3039 * returns column in "*colp" and row as return value; 3040 */ 3041 int 3042 gui_xy2colrow(x, y, colp) 3043 int x; 3044 int y; 3045 int *colp; 3046 { 3047 int col = check_col(X_2_COL(x)); 3048 int row = check_row(Y_2_ROW(y)); 3049 3050 #ifdef FEAT_MBYTE 3051 *colp = mb_fix_col(col, row); 3052 #else 3053 *colp = col; 3054 #endif 3055 return row; 3056 } 3057 3058 #if defined(FEAT_MENU) || defined(PROTO) 3059 /* 3060 * Callback function for when a menu entry has been selected. 3061 */ 3062 void 3063 gui_menu_cb(menu) 3064 vimmenu_T *menu; 3065 { 3066 char_u bytes[sizeof(long_u)]; 3067 3068 /* Don't put events in the input queue now. */ 3069 if (hold_gui_events) 3070 return; 3071 3072 bytes[0] = CSI; 3073 bytes[1] = KS_MENU; 3074 bytes[2] = KE_FILLER; 3075 add_to_input_buf(bytes, 3); 3076 add_long_to_buf((long_u)menu, bytes); 3077 add_to_input_buf_csi(bytes, sizeof(long_u)); 3078 } 3079 #endif 3080 3081 #ifndef FEAT_WINDOWS 3082 static int prev_which_scrollbars[3]; 3083 #endif 3084 3085 /* 3086 * Set which components are present. 3087 * If "oldval" is not NULL, "oldval" is the previous value, the new value is 3088 * in p_go. 3089 */ 3090 /*ARGSUSED*/ 3091 void 3092 gui_init_which_components(oldval) 3093 char_u *oldval; 3094 { 3095 #ifdef FEAT_MENU 3096 static int prev_menu_is_active = -1; 3097 #endif 3098 #ifdef FEAT_TOOLBAR 3099 static int prev_toolbar = -1; 3100 int using_toolbar = FALSE; 3101 #endif 3102 #ifdef FEAT_GUI_TABLINE 3103 int using_tabline; 3104 #endif 3105 #ifdef FEAT_FOOTER 3106 static int prev_footer = -1; 3107 int using_footer = FALSE; 3108 #endif 3109 #if defined(FEAT_MENU) && !defined(WIN16) 3110 static int prev_tearoff = -1; 3111 int using_tearoff = FALSE; 3112 #endif 3113 3114 char_u *p; 3115 int i; 3116 #ifdef FEAT_MENU 3117 int grey_old, grey_new; 3118 char_u *temp; 3119 #endif 3120 win_T *wp; 3121 int need_set_size; 3122 int fix_size; 3123 3124 #ifdef FEAT_MENU 3125 if (oldval != NULL && gui.in_use) 3126 { 3127 /* 3128 * Check if the menu's go from grey to non-grey or vise versa. 3129 */ 3130 grey_old = (vim_strchr(oldval, GO_GREY) != NULL); 3131 grey_new = (vim_strchr(p_go, GO_GREY) != NULL); 3132 if (grey_old != grey_new) 3133 { 3134 temp = p_go; 3135 p_go = oldval; 3136 gui_update_menus(MENU_ALL_MODES); 3137 p_go = temp; 3138 } 3139 } 3140 gui.menu_is_active = FALSE; 3141 #endif 3142 3143 for (i = 0; i < 3; i++) 3144 gui.which_scrollbars[i] = FALSE; 3145 for (p = p_go; *p; p++) 3146 switch (*p) 3147 { 3148 case GO_LEFT: 3149 gui.which_scrollbars[SBAR_LEFT] = TRUE; 3150 break; 3151 case GO_RIGHT: 3152 gui.which_scrollbars[SBAR_RIGHT] = TRUE; 3153 break; 3154 #ifdef FEAT_VERTSPLIT 3155 case GO_VLEFT: 3156 if (win_hasvertsplit()) 3157 gui.which_scrollbars[SBAR_LEFT] = TRUE; 3158 break; 3159 case GO_VRIGHT: 3160 if (win_hasvertsplit()) 3161 gui.which_scrollbars[SBAR_RIGHT] = TRUE; 3162 break; 3163 #endif 3164 case GO_BOT: 3165 gui.which_scrollbars[SBAR_BOTTOM] = TRUE; 3166 break; 3167 #ifdef FEAT_MENU 3168 case GO_MENUS: 3169 gui.menu_is_active = TRUE; 3170 break; 3171 #endif 3172 case GO_GREY: 3173 /* make menu's have grey items, ignored here */ 3174 break; 3175 #ifdef FEAT_TOOLBAR 3176 case GO_TOOLBAR: 3177 using_toolbar = TRUE; 3178 break; 3179 #endif 3180 #ifdef FEAT_FOOTER 3181 case GO_FOOTER: 3182 using_footer = TRUE; 3183 break; 3184 #endif 3185 case GO_TEAROFF: 3186 #if defined(FEAT_MENU) && !defined(WIN16) 3187 using_tearoff = TRUE; 3188 #endif 3189 break; 3190 default: 3191 /* Ignore options that are not supported */ 3192 break; 3193 } 3194 3195 if (gui.in_use) 3196 { 3197 need_set_size = FALSE; 3198 fix_size = FALSE; 3199 3200 #ifdef FEAT_GUI_TABLINE 3201 /* Update the GUI tab line, it may appear or disappear. This may 3202 * cause the non-GUI tab line to disappear or appear. */ 3203 using_tabline = gui_has_tabline(); 3204 if (!gui_mch_showing_tabline() != !using_tabline) 3205 { 3206 /* We don't want a resize event change "Rows" here, save and 3207 * restore it. Resizing is handled below. */ 3208 i = Rows; 3209 gui_update_tabline(); 3210 Rows = i; 3211 need_set_size = TRUE; 3212 if (using_tabline) 3213 fix_size = TRUE; 3214 if (!gui_use_tabline()) 3215 redraw_tabline = TRUE; /* may draw non-GUI tab line */ 3216 } 3217 #endif 3218 3219 for (i = 0; i < 3; i++) 3220 { 3221 if (gui.which_scrollbars[i] != 3222 #ifdef FEAT_WINDOWS 3223 curtab->tp_prev_which_scrollbars[i] 3224 #else 3225 prev_which_scrollbars[i] 3226 #endif 3227 ) 3228 { 3229 if (i == SBAR_BOTTOM) 3230 gui_mch_enable_scrollbar(&gui.bottom_sbar, 3231 gui.which_scrollbars[i]); 3232 else 3233 { 3234 FOR_ALL_WINDOWS(wp) 3235 { 3236 gui_do_scrollbar(wp, i, gui.which_scrollbars[i]); 3237 } 3238 } 3239 need_set_size = TRUE; 3240 if (gui.which_scrollbars[i]) 3241 fix_size = TRUE; 3242 } 3243 #ifdef FEAT_WINDOWS 3244 curtab->tp_prev_which_scrollbars[i] 3245 #else 3246 prev_which_scrollbars[i] 3247 #endif 3248 = gui.which_scrollbars[i]; 3249 } 3250 3251 #ifdef FEAT_MENU 3252 if (gui.menu_is_active != prev_menu_is_active) 3253 { 3254 /* We don't want a resize event change "Rows" here, save and 3255 * restore it. Resizing is handled below. */ 3256 i = Rows; 3257 gui_mch_enable_menu(gui.menu_is_active); 3258 Rows = i; 3259 prev_menu_is_active = gui.menu_is_active; 3260 need_set_size = TRUE; 3261 if (gui.menu_is_active) 3262 fix_size = TRUE; 3263 } 3264 #endif 3265 3266 #ifdef FEAT_TOOLBAR 3267 if (using_toolbar != prev_toolbar) 3268 { 3269 gui_mch_show_toolbar(using_toolbar); 3270 prev_toolbar = using_toolbar; 3271 need_set_size = TRUE; 3272 if (using_toolbar) 3273 fix_size = TRUE; 3274 } 3275 #endif 3276 #ifdef FEAT_FOOTER 3277 if (using_footer != prev_footer) 3278 { 3279 gui_mch_enable_footer(using_footer); 3280 prev_footer = using_footer; 3281 need_set_size = TRUE; 3282 if (using_footer) 3283 fix_size = TRUE; 3284 } 3285 #endif 3286 #if defined(FEAT_MENU) && !defined(WIN16) && !(defined(WIN3264) && !defined(FEAT_TEAROFF)) 3287 if (using_tearoff != prev_tearoff) 3288 { 3289 gui_mch_toggle_tearoffs(using_tearoff); 3290 prev_tearoff = using_tearoff; 3291 } 3292 #endif 3293 if (need_set_size) 3294 { 3295 #ifdef FEAT_GUI_GTK 3296 long c = Columns; 3297 #endif 3298 /* Adjust the size of the window to make the text area keep the 3299 * same size and to avoid that part of our window is off-screen 3300 * and a scrollbar can't be used, for example. */ 3301 gui_set_shellsize(FALSE, fix_size); 3302 3303 #ifdef FEAT_GUI_GTK 3304 /* GTK has the annoying habit of sending us resize events when 3305 * changing the window size ourselves. This mostly happens when 3306 * waiting for a character to arrive, quite unpredictably, and may 3307 * change Columns and Rows when we don't want it. Wait for a 3308 * character here to avoid this effect. 3309 * If you remove this, please test this command for resizing 3310 * effects (with optional left scrollbar): ":vsp|q|vsp|q|vsp|q". 3311 * Don't do this while starting up though. 3312 * And don't change Rows, it may have be reduced intentionally 3313 * when adding menu/toolbar/tabline. */ 3314 if (!gui.starting) 3315 (void)char_avail(); 3316 Columns = c; 3317 #endif 3318 } 3319 #ifdef FEAT_WINDOWS 3320 /* When the console tabline appears or disappears the window positions 3321 * change. */ 3322 if (firstwin->w_winrow != tabline_height()) 3323 shell_new_rows(); /* recompute window positions and heights */ 3324 #endif 3325 } 3326 } 3327 3328 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 3329 /* 3330 * Return TRUE if the GUI is taking care of the tabline. 3331 * It may still be hidden if 'showtabline' is zero. 3332 */ 3333 int 3334 gui_use_tabline() 3335 { 3336 return gui.in_use && vim_strchr(p_go, GO_TABLINE) != NULL; 3337 } 3338 3339 /* 3340 * Return TRUE if the GUI is showing the tabline. 3341 * This uses 'showtabline'. 3342 */ 3343 static int 3344 gui_has_tabline() 3345 { 3346 if (!gui_use_tabline() 3347 || p_stal == 0 3348 || (p_stal == 1 && first_tabpage->tp_next == NULL)) 3349 return FALSE; 3350 return TRUE; 3351 } 3352 3353 /* 3354 * Update the tabline. 3355 * This may display/undisplay the tabline and update the labels. 3356 */ 3357 void 3358 gui_update_tabline() 3359 { 3360 int showit = gui_has_tabline(); 3361 int shown = gui_mch_showing_tabline(); 3362 3363 if (!gui.starting && starting == 0) 3364 { 3365 if (!showit != !shown) 3366 gui_mch_show_tabline(showit); 3367 if (showit != 0) 3368 gui_mch_update_tabline(); 3369 3370 /* When the tabs change from hidden to shown or from shown to 3371 * hidden the size of the text area should remain the same. */ 3372 if (!showit != !shown) 3373 gui_set_shellsize(FALSE, showit); 3374 } 3375 } 3376 3377 /* 3378 * Get the label for tab page "tp" into NameBuff[]. 3379 */ 3380 void 3381 get_tabline_label(tp) 3382 tabpage_T *tp; 3383 { 3384 int modified = FALSE; 3385 char_u buf[40]; 3386 int wincount; 3387 win_T *wp; 3388 3389 /* Use 'guitablabel' if it's set. */ 3390 if (*p_gtl != NUL) 3391 { 3392 int use_sandbox = FALSE; 3393 int save_called_emsg = called_emsg; 3394 char_u res[MAXPATHL]; 3395 tabpage_T *save_curtab; 3396 3397 called_emsg = FALSE; 3398 3399 printer_page_num = tabpage_index(tp); 3400 # ifdef FEAT_EVAL 3401 set_vim_var_nr(VV_LNUM, printer_page_num); 3402 use_sandbox = was_set_insecurely((char_u *)"guitablabel", 0); 3403 # endif 3404 /* It's almost as going to the tabpage, but without autocommands. */ 3405 curtab->tp_firstwin = firstwin; 3406 curtab->tp_lastwin = lastwin; 3407 curtab->tp_curwin = curwin; 3408 save_curtab = curtab; 3409 curtab = tp; 3410 topframe = curtab->tp_topframe; 3411 firstwin = curtab->tp_firstwin; 3412 lastwin = curtab->tp_lastwin; 3413 curwin = curtab->tp_curwin; 3414 curbuf = curwin->w_buffer; 3415 3416 /* Can't use NameBuff directly, build_stl_str_hl() uses it. */ 3417 build_stl_str_hl(curwin, res, MAXPATHL, p_gtl, use_sandbox, 3418 0, (int)Columns, NULL, NULL); 3419 STRCPY(NameBuff, res); 3420 3421 /* Back to the original curtab. */ 3422 curtab = save_curtab; 3423 topframe = curtab->tp_topframe; 3424 firstwin = curtab->tp_firstwin; 3425 lastwin = curtab->tp_lastwin; 3426 curwin = curtab->tp_curwin; 3427 curbuf = curwin->w_buffer; 3428 3429 if (called_emsg) 3430 set_string_option_direct((char_u *)"guitablabel", -1, 3431 (char_u *)"", OPT_FREE, SID_ERROR); 3432 called_emsg |= save_called_emsg; 3433 } 3434 else 3435 { 3436 /* Get the buffer name into NameBuff[] */ 3437 get_trans_bufname(tp == curtab ? curbuf : tp->tp_curwin->w_buffer); 3438 3439 wp = (tp == curtab) ? firstwin : tp->tp_firstwin; 3440 for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount) 3441 if (bufIsChanged(wp->w_buffer)) 3442 modified = TRUE; 3443 if (modified || wincount > 1) 3444 { 3445 if (wincount > 1) 3446 vim_snprintf((char *)buf, sizeof(buf), "%d", wincount); 3447 else 3448 buf[0] = NUL; 3449 if (modified) 3450 STRCAT(buf, "+"); 3451 STRCAT(buf, " "); 3452 mch_memmove(NameBuff + STRLEN(buf), NameBuff, STRLEN(NameBuff) + 1); 3453 mch_memmove(NameBuff, buf, STRLEN(buf)); 3454 } 3455 } 3456 } 3457 3458 /* 3459 * Send the event for clicking to select tab page "nr". 3460 * Returns TRUE if it was done, FALSE when skipped because we are already at 3461 * that tab page. 3462 */ 3463 int 3464 send_tabline_event(nr) 3465 int nr; 3466 { 3467 char_u string[3]; 3468 3469 if (nr == tabpage_index(curtab)) 3470 return FALSE; 3471 string[0] = CSI; 3472 string[1] = KS_TABLINE; 3473 string[2] = KE_FILLER; 3474 add_to_input_buf(string, 3); 3475 string[0] = nr; 3476 add_to_input_buf_csi(string, 1); 3477 return TRUE; 3478 } 3479 3480 #endif 3481 3482 /* 3483 * Scrollbar stuff: 3484 */ 3485 3486 #if defined(FEAT_WINDOWS) || defined(PROTO) 3487 /* 3488 * Remove all scrollbars. Used before switching to another tab page. 3489 */ 3490 void 3491 gui_remove_scrollbars() 3492 { 3493 int i; 3494 win_T *wp; 3495 3496 for (i = 0; i < 3; i++) 3497 { 3498 if (i == SBAR_BOTTOM) 3499 gui_mch_enable_scrollbar(&gui.bottom_sbar, FALSE); 3500 else 3501 { 3502 FOR_ALL_WINDOWS(wp) 3503 { 3504 gui_do_scrollbar(wp, i, FALSE); 3505 } 3506 } 3507 curtab->tp_prev_which_scrollbars[i] = -1; 3508 } 3509 } 3510 #endif 3511 3512 void 3513 gui_create_scrollbar(sb, type, wp) 3514 scrollbar_T *sb; 3515 int type; 3516 win_T *wp; 3517 { 3518 static int sbar_ident = 0; 3519 3520 sb->ident = sbar_ident++; /* No check for too big, but would it happen? */ 3521 sb->wp = wp; 3522 sb->type = type; 3523 sb->value = 0; 3524 #ifdef FEAT_GUI_ATHENA 3525 sb->pixval = 0; 3526 #endif 3527 sb->size = 1; 3528 sb->max = 1; 3529 sb->top = 0; 3530 sb->height = 0; 3531 #ifdef FEAT_VERTSPLIT 3532 sb->width = 0; 3533 #endif 3534 sb->status_height = 0; 3535 gui_mch_create_scrollbar(sb, (wp == NULL) ? SBAR_HORIZ : SBAR_VERT); 3536 } 3537 3538 /* 3539 * Find the scrollbar with the given index. 3540 */ 3541 scrollbar_T * 3542 gui_find_scrollbar(ident) 3543 long ident; 3544 { 3545 win_T *wp; 3546 3547 if (gui.bottom_sbar.ident == ident) 3548 return &gui.bottom_sbar; 3549 FOR_ALL_WINDOWS(wp) 3550 { 3551 if (wp->w_scrollbars[SBAR_LEFT].ident == ident) 3552 return &wp->w_scrollbars[SBAR_LEFT]; 3553 if (wp->w_scrollbars[SBAR_RIGHT].ident == ident) 3554 return &wp->w_scrollbars[SBAR_RIGHT]; 3555 } 3556 return NULL; 3557 } 3558 3559 /* 3560 * For most systems: Put a code in the input buffer for a dragged scrollbar. 3561 * 3562 * For Win32, Macintosh and GTK+ 2: 3563 * Scrollbars seem to grab focus and vim doesn't read the input queue until 3564 * you stop dragging the scrollbar. We get here each time the scrollbar is 3565 * dragged another pixel, but as far as the rest of vim goes, it thinks 3566 * we're just hanging in the call to DispatchMessage() in 3567 * process_message(). The DispatchMessage() call that hangs was passed a 3568 * mouse button click event in the scrollbar window. -- webb. 3569 * 3570 * Solution: Do the scrolling right here. But only when allowed. 3571 * Ignore the scrollbars while executing an external command or when there 3572 * are still characters to be processed. 3573 */ 3574 void 3575 gui_drag_scrollbar(sb, value, still_dragging) 3576 scrollbar_T *sb; 3577 long value; 3578 int still_dragging; 3579 { 3580 #ifdef FEAT_WINDOWS 3581 win_T *wp; 3582 #endif 3583 int sb_num; 3584 #ifdef USE_ON_FLY_SCROLL 3585 colnr_T old_leftcol = curwin->w_leftcol; 3586 # ifdef FEAT_SCROLLBIND 3587 linenr_T old_topline = curwin->w_topline; 3588 # endif 3589 # ifdef FEAT_DIFF 3590 int old_topfill = curwin->w_topfill; 3591 # endif 3592 #else 3593 char_u bytes[sizeof(long_u)]; 3594 int byte_count; 3595 #endif 3596 3597 if (sb == NULL) 3598 return; 3599 3600 /* Don't put events in the input queue now. */ 3601 if (hold_gui_events) 3602 return; 3603 3604 #ifdef FEAT_CMDWIN 3605 if (cmdwin_type != 0 && sb->wp != curwin) 3606 return; 3607 #endif 3608 3609 if (still_dragging) 3610 { 3611 if (sb->wp == NULL) 3612 gui.dragged_sb = SBAR_BOTTOM; 3613 else if (sb == &sb->wp->w_scrollbars[SBAR_LEFT]) 3614 gui.dragged_sb = SBAR_LEFT; 3615 else 3616 gui.dragged_sb = SBAR_RIGHT; 3617 gui.dragged_wp = sb->wp; 3618 } 3619 else 3620 { 3621 gui.dragged_sb = SBAR_NONE; 3622 #ifdef HAVE_GTK2 3623 /* Keep the "dragged_wp" value until after the scrolling, for when the 3624 * moust button is released. GTK2 doesn't send the button-up event. */ 3625 gui.dragged_wp = NULL; 3626 #endif 3627 } 3628 3629 /* Vertical sbar info is kept in the first sbar (the left one) */ 3630 if (sb->wp != NULL) 3631 sb = &sb->wp->w_scrollbars[0]; 3632 3633 /* 3634 * Check validity of value 3635 */ 3636 if (value < 0) 3637 value = 0; 3638 #ifdef SCROLL_PAST_END 3639 else if (value > sb->max) 3640 value = sb->max; 3641 #else 3642 if (value > sb->max - sb->size + 1) 3643 value = sb->max - sb->size + 1; 3644 #endif 3645 3646 sb->value = value; 3647 3648 #ifdef USE_ON_FLY_SCROLL 3649 /* When not allowed to do the scrolling right now, return. */ 3650 if (dont_scroll || input_available()) 3651 return; 3652 #endif 3653 3654 #ifdef FEAT_RIGHTLEFT 3655 if (sb->wp == NULL && curwin->w_p_rl) 3656 { 3657 value = sb->max + 1 - sb->size - value; 3658 if (value < 0) 3659 value = 0; 3660 } 3661 #endif 3662 3663 if (sb->wp != NULL) /* vertical scrollbar */ 3664 { 3665 sb_num = 0; 3666 #ifdef FEAT_WINDOWS 3667 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = wp->w_next) 3668 sb_num++; 3669 if (wp == NULL) 3670 return; 3671 #else 3672 if (sb->wp != curwin) 3673 return; 3674 #endif 3675 3676 #ifdef USE_ON_FLY_SCROLL 3677 current_scrollbar = sb_num; 3678 scrollbar_value = value; 3679 if (State & NORMAL) 3680 { 3681 gui_do_scroll(); 3682 setcursor(); 3683 } 3684 else if (State & INSERT) 3685 { 3686 ins_scroll(); 3687 setcursor(); 3688 } 3689 else if (State & CMDLINE) 3690 { 3691 if (msg_scrolled == 0) 3692 { 3693 gui_do_scroll(); 3694 redrawcmdline(); 3695 } 3696 } 3697 # ifdef FEAT_FOLDING 3698 /* Value may have been changed for closed fold. */ 3699 sb->value = sb->wp->w_topline - 1; 3700 # endif 3701 3702 /* When dragging one scrollbar and there is another one at the other 3703 * side move the thumb of that one too. */ 3704 if (gui.which_scrollbars[SBAR_RIGHT] && gui.which_scrollbars[SBAR_LEFT]) 3705 gui_mch_set_scrollbar_thumb( 3706 &sb->wp->w_scrollbars[ 3707 sb == &sb->wp->w_scrollbars[SBAR_RIGHT] 3708 ? SBAR_LEFT : SBAR_RIGHT], 3709 sb->value, sb->size, sb->max); 3710 3711 #else 3712 bytes[0] = CSI; 3713 bytes[1] = KS_VER_SCROLLBAR; 3714 bytes[2] = KE_FILLER; 3715 bytes[3] = (char_u)sb_num; 3716 byte_count = 4; 3717 #endif 3718 } 3719 else 3720 { 3721 #ifdef USE_ON_FLY_SCROLL 3722 scrollbar_value = value; 3723 3724 if (State & NORMAL) 3725 gui_do_horiz_scroll(); 3726 else if (State & INSERT) 3727 ins_horscroll(); 3728 else if (State & CMDLINE) 3729 { 3730 if (msg_scrolled == 0) 3731 { 3732 gui_do_horiz_scroll(); 3733 redrawcmdline(); 3734 } 3735 } 3736 if (old_leftcol != curwin->w_leftcol) 3737 { 3738 updateWindow(curwin); /* update window, status and cmdline */ 3739 setcursor(); 3740 } 3741 #else 3742 bytes[0] = CSI; 3743 bytes[1] = KS_HOR_SCROLLBAR; 3744 bytes[2] = KE_FILLER; 3745 byte_count = 3; 3746 #endif 3747 } 3748 3749 #ifdef USE_ON_FLY_SCROLL 3750 # ifdef FEAT_SCROLLBIND 3751 /* 3752 * synchronize other windows, as necessary according to 'scrollbind' 3753 */ 3754 if (curwin->w_p_scb 3755 && ((sb->wp == NULL && curwin->w_leftcol != old_leftcol) 3756 || (sb->wp == curwin && (curwin->w_topline != old_topline 3757 # ifdef FEAT_DIFF 3758 || curwin->w_topfill != old_topfill 3759 # endif 3760 )))) 3761 { 3762 do_check_scrollbind(TRUE); 3763 /* need to update the window right here */ 3764 for (wp = firstwin; wp != NULL; wp = wp->w_next) 3765 if (wp->w_redr_type > 0) 3766 updateWindow(wp); 3767 setcursor(); 3768 } 3769 # endif 3770 out_flush(); 3771 gui_update_cursor(FALSE, TRUE); 3772 #else 3773 add_to_input_buf(bytes, byte_count); 3774 add_long_to_buf((long_u)value, bytes); 3775 add_to_input_buf_csi(bytes, sizeof(long_u)); 3776 #endif 3777 } 3778 3779 /* 3780 * Scrollbar stuff: 3781 */ 3782 3783 void 3784 gui_update_scrollbars(force) 3785 int force; /* Force all scrollbars to get updated */ 3786 { 3787 win_T *wp; 3788 scrollbar_T *sb; 3789 long val, size, max; /* need 32 bits here */ 3790 int which_sb; 3791 int h, y; 3792 #ifdef FEAT_VERTSPLIT 3793 static win_T *prev_curwin = NULL; 3794 #endif 3795 3796 /* Update the horizontal scrollbar */ 3797 gui_update_horiz_scrollbar(force); 3798 3799 #ifndef WIN3264 3800 /* Return straight away if there is neither a left nor right scrollbar. 3801 * On MS-Windows this is required anyway for scrollwheel messages. */ 3802 if (!gui.which_scrollbars[SBAR_LEFT] && !gui.which_scrollbars[SBAR_RIGHT]) 3803 return; 3804 #endif 3805 3806 /* 3807 * Don't want to update a scrollbar while we're dragging it. But if we 3808 * have both a left and right scrollbar, and we drag one of them, we still 3809 * need to update the other one. 3810 */ 3811 if (!force && (gui.dragged_sb == SBAR_LEFT || gui.dragged_sb == SBAR_RIGHT) 3812 && gui.which_scrollbars[SBAR_LEFT] 3813 && gui.which_scrollbars[SBAR_RIGHT]) 3814 { 3815 /* 3816 * If we have two scrollbars and one of them is being dragged, just 3817 * copy the scrollbar position from the dragged one to the other one. 3818 */ 3819 which_sb = SBAR_LEFT + SBAR_RIGHT - gui.dragged_sb; 3820 if (gui.dragged_wp != NULL) 3821 gui_mch_set_scrollbar_thumb( 3822 &gui.dragged_wp->w_scrollbars[which_sb], 3823 gui.dragged_wp->w_scrollbars[0].value, 3824 gui.dragged_wp->w_scrollbars[0].size, 3825 gui.dragged_wp->w_scrollbars[0].max); 3826 } 3827 3828 /* avoid that moving components around generates events */ 3829 ++hold_gui_events; 3830 3831 for (wp = firstwin; wp != NULL; wp = W_NEXT(wp)) 3832 { 3833 if (wp->w_buffer == NULL) /* just in case */ 3834 continue; 3835 /* Skip a scrollbar that is being dragged. */ 3836 if (!force && (gui.dragged_sb == SBAR_LEFT 3837 || gui.dragged_sb == SBAR_RIGHT) 3838 && gui.dragged_wp == wp) 3839 continue; 3840 3841 #ifdef SCROLL_PAST_END 3842 max = wp->w_buffer->b_ml.ml_line_count - 1; 3843 #else 3844 max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 2; 3845 #endif 3846 if (max < 0) /* empty buffer */ 3847 max = 0; 3848 val = wp->w_topline - 1; 3849 size = wp->w_height; 3850 #ifdef SCROLL_PAST_END 3851 if (val > max) /* just in case */ 3852 val = max; 3853 #else 3854 if (size > max + 1) /* just in case */ 3855 size = max + 1; 3856 if (val > max - size + 1) 3857 val = max - size + 1; 3858 #endif 3859 if (val < 0) /* minimal value is 0 */ 3860 val = 0; 3861 3862 /* 3863 * Scrollbar at index 0 (the left one) contains all the information. 3864 * It would be the same info for left and right so we just store it for 3865 * one of them. 3866 */ 3867 sb = &wp->w_scrollbars[0]; 3868 3869 /* 3870 * Note: no check for valid w_botline. If it's not valid the 3871 * scrollbars will be updated later anyway. 3872 */ 3873 if (size < 1 || wp->w_botline - 2 > max) 3874 { 3875 /* 3876 * This can happen during changing files. Just don't update the 3877 * scrollbar for now. 3878 */ 3879 sb->height = 0; /* Force update next time */ 3880 if (gui.which_scrollbars[SBAR_LEFT]) 3881 gui_do_scrollbar(wp, SBAR_LEFT, FALSE); 3882 if (gui.which_scrollbars[SBAR_RIGHT]) 3883 gui_do_scrollbar(wp, SBAR_RIGHT, FALSE); 3884 continue; 3885 } 3886 if (force || sb->height != wp->w_height 3887 #ifdef FEAT_WINDOWS 3888 || sb->top != wp->w_winrow 3889 || sb->status_height != wp->w_status_height 3890 # ifdef FEAT_VERTSPLIT 3891 || sb->width != wp->w_width 3892 || prev_curwin != curwin 3893 # endif 3894 #endif 3895 ) 3896 { 3897 /* Height, width or position of scrollbar has changed. For 3898 * vertical split: curwin changed. */ 3899 sb->height = wp->w_height; 3900 #ifdef FEAT_WINDOWS 3901 sb->top = wp->w_winrow; 3902 sb->status_height = wp->w_status_height; 3903 # ifdef FEAT_VERTSPLIT 3904 sb->width = wp->w_width; 3905 # endif 3906 #endif 3907 3908 /* Calculate height and position in pixels */ 3909 h = (sb->height + sb->status_height) * gui.char_height; 3910 y = sb->top * gui.char_height + gui.border_offset; 3911 #if defined(FEAT_MENU) && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) && !defined(FEAT_GUI_PHOTON) 3912 if (gui.menu_is_active) 3913 y += gui.menu_height; 3914 #endif 3915 3916 #if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_ATHENA)) 3917 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 3918 # ifdef FEAT_GUI_ATHENA 3919 y += gui.toolbar_height; 3920 # else 3921 # ifdef FEAT_GUI_MSWIN 3922 y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; 3923 # endif 3924 # endif 3925 #endif 3926 3927 #ifdef FEAT_WINDOWS 3928 if (wp->w_winrow == 0) 3929 #endif 3930 { 3931 /* Height of top scrollbar includes width of top border */ 3932 h += gui.border_offset; 3933 y -= gui.border_offset; 3934 } 3935 if (gui.which_scrollbars[SBAR_LEFT]) 3936 { 3937 gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_LEFT], 3938 gui.left_sbar_x, y, 3939 gui.scrollbar_width, h); 3940 gui_do_scrollbar(wp, SBAR_LEFT, TRUE); 3941 } 3942 if (gui.which_scrollbars[SBAR_RIGHT]) 3943 { 3944 gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_RIGHT], 3945 gui.right_sbar_x, y, 3946 gui.scrollbar_width, h); 3947 gui_do_scrollbar(wp, SBAR_RIGHT, TRUE); 3948 } 3949 } 3950 3951 /* Reduce the number of calls to gui_mch_set_scrollbar_thumb() by 3952 * checking if the thumb moved at least a pixel. Only do this for 3953 * Athena, most other GUIs require the update anyway to make the 3954 * arrows work. */ 3955 #ifdef FEAT_GUI_ATHENA 3956 if (max == 0) 3957 y = 0; 3958 else 3959 y = (val * (sb->height + 2) * gui.char_height + max / 2) / max; 3960 if (force || sb->pixval != y || sb->size != size || sb->max != max) 3961 #else 3962 if (force || sb->value != val || sb->size != size || sb->max != max) 3963 #endif 3964 { 3965 /* Thumb of scrollbar has moved */ 3966 sb->value = val; 3967 #ifdef FEAT_GUI_ATHENA 3968 sb->pixval = y; 3969 #endif 3970 sb->size = size; 3971 sb->max = max; 3972 if (gui.which_scrollbars[SBAR_LEFT] 3973 && (gui.dragged_sb != SBAR_LEFT || gui.dragged_wp != wp)) 3974 gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_LEFT], 3975 val, size, max); 3976 if (gui.which_scrollbars[SBAR_RIGHT] 3977 && (gui.dragged_sb != SBAR_RIGHT || gui.dragged_wp != wp)) 3978 gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_RIGHT], 3979 val, size, max); 3980 } 3981 } 3982 #ifdef FEAT_VERTSPLIT 3983 prev_curwin = curwin; 3984 #endif 3985 --hold_gui_events; 3986 } 3987 3988 /* 3989 * Enable or disable a scrollbar. 3990 * Check for scrollbars for vertically split windows which are not enabled 3991 * sometimes. 3992 */ 3993 static void 3994 gui_do_scrollbar(wp, which, enable) 3995 win_T *wp; 3996 int which; /* SBAR_LEFT or SBAR_RIGHT */ 3997 int enable; /* TRUE to enable scrollbar */ 3998 { 3999 #ifdef FEAT_VERTSPLIT 4000 int midcol = curwin->w_wincol + curwin->w_width / 2; 4001 int has_midcol = (wp->w_wincol <= midcol 4002 && wp->w_wincol + wp->w_width >= midcol); 4003 4004 /* Only enable scrollbars that contain the middle column of the current 4005 * window. */ 4006 if (gui.which_scrollbars[SBAR_RIGHT] != gui.which_scrollbars[SBAR_LEFT]) 4007 { 4008 /* Scrollbars only on one side. Don't enable scrollbars that don't 4009 * contain the middle column of the current window. */ 4010 if (!has_midcol) 4011 enable = FALSE; 4012 } 4013 else 4014 { 4015 /* Scrollbars on both sides. Don't enable scrollbars that neither 4016 * contain the middle column of the current window nor are on the far 4017 * side. */ 4018 if (midcol > Columns / 2) 4019 { 4020 if (which == SBAR_LEFT ? wp->w_wincol != 0 : !has_midcol) 4021 enable = FALSE; 4022 } 4023 else 4024 { 4025 if (which == SBAR_RIGHT ? wp->w_wincol + wp->w_width != Columns 4026 : !has_midcol) 4027 enable = FALSE; 4028 } 4029 } 4030 #endif 4031 gui_mch_enable_scrollbar(&wp->w_scrollbars[which], enable); 4032 } 4033 4034 /* 4035 * Scroll a window according to the values set in the globals current_scrollbar 4036 * and scrollbar_value. Return TRUE if the cursor in the current window moved 4037 * or FALSE otherwise. 4038 */ 4039 int 4040 gui_do_scroll() 4041 { 4042 win_T *wp, *save_wp; 4043 int i; 4044 long nlines; 4045 pos_T old_cursor; 4046 linenr_T old_topline; 4047 #ifdef FEAT_DIFF 4048 int old_topfill; 4049 #endif 4050 4051 for (wp = firstwin, i = 0; i < current_scrollbar; wp = W_NEXT(wp), i++) 4052 if (wp == NULL) 4053 break; 4054 if (wp == NULL) 4055 /* Couldn't find window */ 4056 return FALSE; 4057 4058 /* 4059 * Compute number of lines to scroll. If zero, nothing to do. 4060 */ 4061 nlines = (long)scrollbar_value + 1 - (long)wp->w_topline; 4062 if (nlines == 0) 4063 return FALSE; 4064 4065 save_wp = curwin; 4066 old_topline = wp->w_topline; 4067 #ifdef FEAT_DIFF 4068 old_topfill = wp->w_topfill; 4069 #endif 4070 old_cursor = wp->w_cursor; 4071 curwin = wp; 4072 curbuf = wp->w_buffer; 4073 if (nlines < 0) 4074 scrolldown(-nlines, gui.dragged_wp == NULL); 4075 else 4076 scrollup(nlines, gui.dragged_wp == NULL); 4077 /* Reset dragged_wp after using it. "dragged_sb" will have been reset for 4078 * the mouse-up event already, but we still want it to behave like when 4079 * dragging. But not the next click in an arrow. */ 4080 if (gui.dragged_sb == SBAR_NONE) 4081 gui.dragged_wp = NULL; 4082 4083 if (old_topline != wp->w_topline 4084 #ifdef FEAT_DIFF 4085 || old_topfill != wp->w_topfill 4086 #endif 4087 ) 4088 { 4089 if (p_so != 0) 4090 { 4091 cursor_correct(); /* fix window for 'so' */ 4092 update_topline(); /* avoid up/down jump */ 4093 } 4094 if (old_cursor.lnum != wp->w_cursor.lnum) 4095 coladvance(wp->w_curswant); 4096 #ifdef FEAT_SCROLLBIND 4097 wp->w_scbind_pos = wp->w_topline; 4098 #endif 4099 } 4100 4101 /* Make sure wp->w_leftcol and wp->w_skipcol are correct. */ 4102 validate_cursor(); 4103 4104 curwin = save_wp; 4105 curbuf = save_wp->w_buffer; 4106 4107 /* 4108 * Don't call updateWindow() when nothing has changed (it will overwrite 4109 * the status line!). 4110 */ 4111 if (old_topline != wp->w_topline 4112 || wp->w_redr_type != 0 4113 #ifdef FEAT_DIFF 4114 || old_topfill != wp->w_topfill 4115 #endif 4116 ) 4117 { 4118 redraw_win_later(wp, VALID); 4119 updateWindow(wp); /* update window, status line, and cmdline */ 4120 } 4121 4122 return (wp == curwin && !equalpos(curwin->w_cursor, old_cursor)); 4123 } 4124 4125 4126 /* 4127 * Horizontal scrollbar stuff: 4128 */ 4129 4130 /* 4131 * Return length of line "lnum" for horizontal scrolling. 4132 */ 4133 static colnr_T 4134 scroll_line_len(lnum) 4135 linenr_T lnum; 4136 { 4137 char_u *p; 4138 colnr_T col; 4139 int w; 4140 4141 p = ml_get(lnum); 4142 col = 0; 4143 if (*p != NUL) 4144 for (;;) 4145 { 4146 w = chartabsize(p, col); 4147 mb_ptr_adv(p); 4148 if (*p == NUL) /* don't count the last character */ 4149 break; 4150 col += w; 4151 } 4152 return col; 4153 } 4154 4155 /* Remember which line is currently the longest, so that we don't have to 4156 * search for it when scrolling horizontally. */ 4157 static linenr_T longest_lnum = 0; 4158 4159 static void 4160 gui_update_horiz_scrollbar(force) 4161 int force; 4162 { 4163 long value, size, max; /* need 32 bit ints here */ 4164 4165 if (!gui.which_scrollbars[SBAR_BOTTOM]) 4166 return; 4167 4168 if (!force && gui.dragged_sb == SBAR_BOTTOM) 4169 return; 4170 4171 if (!force && curwin->w_p_wrap && gui.prev_wrap) 4172 return; 4173 4174 /* 4175 * It is possible for the cursor to be invalid if we're in the middle of 4176 * something (like changing files). If so, don't do anything for now. 4177 */ 4178 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) 4179 { 4180 gui.bottom_sbar.value = -1; 4181 return; 4182 } 4183 4184 size = W_WIDTH(curwin); 4185 if (curwin->w_p_wrap) 4186 { 4187 value = 0; 4188 #ifdef SCROLL_PAST_END 4189 max = 0; 4190 #else 4191 max = W_WIDTH(curwin) - 1; 4192 #endif 4193 } 4194 else 4195 { 4196 value = curwin->w_leftcol; 4197 4198 /* Calculate maximum for horizontal scrollbar. Check for reasonable 4199 * line numbers, topline and botline can be invalid when displaying is 4200 * postponed. */ 4201 if (vim_strchr(p_go, GO_HORSCROLL) == NULL 4202 && curwin->w_topline <= curwin->w_cursor.lnum 4203 && curwin->w_botline > curwin->w_cursor.lnum 4204 && curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) 4205 { 4206 linenr_T lnum; 4207 colnr_T n; 4208 4209 /* Use maximum of all visible lines. Remember the lnum of the 4210 * longest line, clostest to the cursor line. Used when scrolling 4211 * below. */ 4212 max = 0; 4213 for (lnum = curwin->w_topline; lnum < curwin->w_botline; ++lnum) 4214 { 4215 n = scroll_line_len(lnum); 4216 if (n > (colnr_T)max) 4217 { 4218 max = n; 4219 longest_lnum = lnum; 4220 } 4221 else if (n == (colnr_T)max 4222 && abs((int)(lnum - curwin->w_cursor.lnum)) 4223 < abs((int)(longest_lnum - curwin->w_cursor.lnum))) 4224 longest_lnum = lnum; 4225 } 4226 } 4227 else 4228 /* Use cursor line only. */ 4229 max = scroll_line_len(curwin->w_cursor.lnum); 4230 #ifdef FEAT_VIRTUALEDIT 4231 if (virtual_active()) 4232 { 4233 /* May move the cursor even further to the right. */ 4234 if (curwin->w_virtcol >= (colnr_T)max) 4235 max = curwin->w_virtcol; 4236 } 4237 #endif 4238 4239 #ifndef SCROLL_PAST_END 4240 max += W_WIDTH(curwin) - 1; 4241 #endif 4242 /* The line number isn't scrolled, thus there is less space when 4243 * 'number' is set (also for 'foldcolumn'). */ 4244 size -= curwin_col_off(); 4245 #ifndef SCROLL_PAST_END 4246 max -= curwin_col_off(); 4247 #endif 4248 } 4249 4250 #ifndef SCROLL_PAST_END 4251 if (value > max - size + 1) 4252 value = max - size + 1; /* limit the value to allowable range */ 4253 #endif 4254 4255 #ifdef FEAT_RIGHTLEFT 4256 if (curwin->w_p_rl) 4257 { 4258 value = max + 1 - size - value; 4259 if (value < 0) 4260 { 4261 size += value; 4262 value = 0; 4263 } 4264 } 4265 #endif 4266 if (!force && value == gui.bottom_sbar.value && size == gui.bottom_sbar.size 4267 && max == gui.bottom_sbar.max) 4268 return; 4269 4270 gui.bottom_sbar.value = value; 4271 gui.bottom_sbar.size = size; 4272 gui.bottom_sbar.max = max; 4273 gui.prev_wrap = curwin->w_p_wrap; 4274 4275 gui_mch_set_scrollbar_thumb(&gui.bottom_sbar, value, size, max); 4276 } 4277 4278 /* 4279 * Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise. 4280 */ 4281 int 4282 gui_do_horiz_scroll() 4283 { 4284 /* no wrapping, no scrolling */ 4285 if (curwin->w_p_wrap) 4286 return FALSE; 4287 4288 if (curwin->w_leftcol == scrollbar_value) 4289 return FALSE; 4290 4291 curwin->w_leftcol = scrollbar_value; 4292 4293 /* When the line of the cursor is too short, move the cursor to the 4294 * longest visible line. Do a sanity check on "longest_lnum", just in 4295 * case. */ 4296 if (vim_strchr(p_go, GO_HORSCROLL) == NULL 4297 && longest_lnum >= curwin->w_topline 4298 && longest_lnum < curwin->w_botline 4299 && !virtual_active()) 4300 { 4301 if (scrollbar_value > scroll_line_len(curwin->w_cursor.lnum)) 4302 { 4303 curwin->w_cursor.lnum = longest_lnum; 4304 curwin->w_cursor.col = 0; 4305 } 4306 } 4307 4308 return leftcol_changed(); 4309 } 4310 4311 /* 4312 * Check that none of the colors are the same as the background color 4313 */ 4314 void 4315 gui_check_colors() 4316 { 4317 if (gui.norm_pixel == gui.back_pixel || gui.norm_pixel == INVALCOLOR) 4318 { 4319 gui_set_bg_color((char_u *)"White"); 4320 if (gui.norm_pixel == gui.back_pixel || gui.norm_pixel == INVALCOLOR) 4321 gui_set_fg_color((char_u *)"Black"); 4322 } 4323 } 4324 4325 static void 4326 gui_set_fg_color(name) 4327 char_u *name; 4328 { 4329 gui.norm_pixel = gui_get_color(name); 4330 hl_set_fg_color_name(vim_strsave(name)); 4331 } 4332 4333 static void 4334 gui_set_bg_color(name) 4335 char_u *name; 4336 { 4337 gui.back_pixel = gui_get_color(name); 4338 hl_set_bg_color_name(vim_strsave(name)); 4339 } 4340 4341 /* 4342 * Allocate a color by name. 4343 * Returns INVALCOLOR and gives an error message when failed. 4344 */ 4345 guicolor_T 4346 gui_get_color(name) 4347 char_u *name; 4348 { 4349 guicolor_T t; 4350 4351 if (*name == NUL) 4352 return INVALCOLOR; 4353 t = gui_mch_get_color(name); 4354 4355 if (t == INVALCOLOR 4356 #if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) 4357 && gui.in_use 4358 #endif 4359 ) 4360 EMSG2(_("E254: Cannot allocate color %s"), name); 4361 return t; 4362 } 4363 4364 /* 4365 * Return the grey value of a color (range 0-255). 4366 */ 4367 int 4368 gui_get_lightness(pixel) 4369 guicolor_T pixel; 4370 { 4371 long_u rgb = gui_mch_get_rgb(pixel); 4372 4373 return ( (((rgb >> 16) & 0xff) * 299) 4374 + (((rgb >> 8) & 0xff) * 587) 4375 + ((rgb & 0xff) * 114)) / 1000; 4376 } 4377 4378 #if defined(FEAT_GUI_X11) || defined(PROTO) 4379 void 4380 gui_new_scrollbar_colors() 4381 { 4382 win_T *wp; 4383 4384 /* Nothing to do if GUI hasn't started yet. */ 4385 if (!gui.in_use) 4386 return; 4387 4388 FOR_ALL_WINDOWS(wp) 4389 { 4390 gui_mch_set_scrollbar_colors(&(wp->w_scrollbars[SBAR_LEFT])); 4391 gui_mch_set_scrollbar_colors(&(wp->w_scrollbars[SBAR_RIGHT])); 4392 } 4393 gui_mch_set_scrollbar_colors(&gui.bottom_sbar); 4394 } 4395 #endif 4396 4397 /* 4398 * Call this when focus has changed. 4399 */ 4400 void 4401 gui_focus_change(in_focus) 4402 int in_focus; 4403 { 4404 /* 4405 * Skip this code to avoid drawing the cursor when debugging and switching 4406 * between the debugger window and gvim. 4407 */ 4408 #if 1 4409 gui.in_focus = in_focus; 4410 out_flush(); /* make sure output has been written */ 4411 gui_update_cursor(TRUE, FALSE); 4412 4413 # ifdef FEAT_XIM 4414 xim_set_focus(in_focus); 4415 # endif 4416 4417 ui_focus_change(in_focus); 4418 #endif 4419 } 4420 4421 /* 4422 * Called when the mouse moved (but not when dragging). 4423 */ 4424 void 4425 gui_mouse_moved(x, y) 4426 int x; 4427 int y; 4428 { 4429 win_T *wp; 4430 char_u st[6]; 4431 4432 /* Ignore this while still starting up. */ 4433 if (!gui.in_use || gui.starting) 4434 return; 4435 4436 #ifdef FEAT_MOUSESHAPE 4437 /* Get window pointer, and update mouse shape as well. */ 4438 wp = xy2win(x, y); 4439 #endif 4440 4441 /* Only handle this when 'mousefocus' set and ... */ 4442 if (p_mousef 4443 && !hold_gui_events /* not holding events */ 4444 && (State & (NORMAL|INSERT))/* Normal/Visual/Insert mode */ 4445 && State != HITRETURN /* but not hit-return prompt */ 4446 && msg_scrolled == 0 /* no scrolled message */ 4447 && !need_mouse_correct /* not moving the pointer */ 4448 && gui.in_focus) /* gvim in focus */ 4449 { 4450 /* Don't move the mouse when it's left or right of the Vim window */ 4451 if (x < 0 || x > Columns * gui.char_width) 4452 return; 4453 #ifndef FEAT_MOUSESHAPE 4454 wp = xy2win(x, y); 4455 #endif 4456 if (wp == curwin || wp == NULL) 4457 return; /* still in the same old window, or none at all */ 4458 4459 /* 4460 * format a mouse click on status line input 4461 * ala gui_send_mouse_event(0, x, y, 0, 0); 4462 * Trick: Use a column number -1, so that get_pseudo_mouse_code() will 4463 * generate a K_LEFTMOUSE_NM key code. 4464 */ 4465 if (finish_op) 4466 { 4467 /* abort the current operator first */ 4468 st[0] = ESC; 4469 add_to_input_buf(st, 1); 4470 } 4471 st[0] = CSI; 4472 st[1] = KS_MOUSE; 4473 st[2] = KE_FILLER; 4474 st[3] = (char_u)MOUSE_LEFT; 4475 fill_mouse_coord(st + 4, 4476 #ifdef FEAT_VERTSPLIT 4477 wp->w_wincol == 0 ? -1 : wp->w_wincol + MOUSE_COLOFF, 4478 #else 4479 -1, 4480 #endif 4481 wp->w_height + W_WINROW(wp)); 4482 4483 add_to_input_buf(st, 8); 4484 st[3] = (char_u)MOUSE_RELEASE; 4485 add_to_input_buf(st, 8); 4486 #ifdef FEAT_GUI_GTK 4487 /* Need to wake up the main loop */ 4488 if (gtk_main_level() > 0) 4489 gtk_main_quit(); 4490 #endif 4491 } 4492 } 4493 4494 /* 4495 * Called when mouse should be moved to window with focus. 4496 */ 4497 void 4498 gui_mouse_correct() 4499 { 4500 int x, y; 4501 win_T *wp = NULL; 4502 4503 need_mouse_correct = FALSE; 4504 4505 if (!(gui.in_use && p_mousef)) 4506 return; 4507 4508 gui_mch_getmouse(&x, &y); 4509 /* Don't move the mouse when it's left or right of the Vim window */ 4510 if (x < 0 || x > Columns * gui.char_width) 4511 return; 4512 if (y >= 0) 4513 wp = xy2win(x, y); 4514 if (wp != curwin && wp != NULL) /* If in other than current window */ 4515 { 4516 validate_cline_row(); 4517 gui_mch_setmouse((int)W_ENDCOL(curwin) * gui.char_width - 3, 4518 (W_WINROW(curwin) + curwin->w_wrow) * gui.char_height 4519 + (gui.char_height) / 2); 4520 } 4521 } 4522 4523 /* 4524 * Find window where the mouse pointer "y" coordinate is in. 4525 */ 4526 /*ARGSUSED*/ 4527 static win_T * 4528 xy2win(x, y) 4529 int x; 4530 int y; 4531 { 4532 #ifdef FEAT_WINDOWS 4533 int row; 4534 int col; 4535 win_T *wp; 4536 4537 row = Y_2_ROW(y); 4538 col = X_2_COL(x); 4539 if (row < 0 || col < 0) /* before first window */ 4540 return NULL; 4541 wp = mouse_find_win(&row, &col); 4542 # ifdef FEAT_MOUSESHAPE 4543 if (State == HITRETURN || State == ASKMORE) 4544 { 4545 if (Y_2_ROW(y) >= msg_row) 4546 update_mouseshape(SHAPE_IDX_MOREL); 4547 else 4548 update_mouseshape(SHAPE_IDX_MORE); 4549 } 4550 else if (row > wp->w_height) /* below status line */ 4551 update_mouseshape(SHAPE_IDX_CLINE); 4552 # ifdef FEAT_VERTSPLIT 4553 else if (!(State & CMDLINE) && W_VSEP_WIDTH(wp) > 0 && col == wp->w_width 4554 && (row != wp->w_height || !stl_connected(wp))) 4555 update_mouseshape(SHAPE_IDX_VSEP); 4556 # endif 4557 else if (!(State & CMDLINE) && W_STATUS_HEIGHT(wp) > 0 4558 && row == wp->w_height) 4559 update_mouseshape(SHAPE_IDX_STATUS); 4560 else 4561 update_mouseshape(-2); 4562 # endif 4563 return wp; 4564 #else 4565 return firstwin; 4566 #endif 4567 } 4568 4569 /* 4570 * ":gui" and ":gvim": Change from the terminal version to the GUI version. 4571 * File names may be given to redefine the args list. 4572 */ 4573 void 4574 ex_gui(eap) 4575 exarg_T *eap; 4576 { 4577 char_u *arg = eap->arg; 4578 4579 /* 4580 * Check for "-f" argument: foreground, don't fork. 4581 * Also don't fork when started with "gvim -f". 4582 * Do fork when using "gui -b". 4583 */ 4584 if (arg[0] == '-' 4585 && (arg[1] == 'f' || arg[1] == 'b') 4586 && (arg[2] == NUL || vim_iswhite(arg[2]))) 4587 { 4588 gui.dofork = (arg[1] == 'b'); 4589 eap->arg = skipwhite(eap->arg + 2); 4590 } 4591 if (!gui.in_use) 4592 { 4593 /* Clear the command. Needed for when forking+exiting, to avoid part 4594 * of the argument ending up after the shell prompt. */ 4595 msg_clr_eos_force(); 4596 gui_start(); 4597 } 4598 if (!ends_excmd(*eap->arg)) 4599 ex_next(eap); 4600 } 4601 4602 #if ((defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_W32) \ 4603 || defined(FEAT_GUI_PHOTON)) && defined(FEAT_TOOLBAR)) || defined(PROTO) 4604 /* 4605 * This is shared between Athena, Motif and GTK. 4606 */ 4607 static void gfp_setname __ARGS((char_u *fname, void *cookie)); 4608 4609 /* 4610 * Callback function for do_in_runtimepath(). 4611 */ 4612 static void 4613 gfp_setname(fname, cookie) 4614 char_u *fname; 4615 void *cookie; 4616 { 4617 char_u *gfp_buffer = cookie; 4618 4619 if (STRLEN(fname) >= MAXPATHL) 4620 *gfp_buffer = NUL; 4621 else 4622 STRCPY(gfp_buffer, fname); 4623 } 4624 4625 /* 4626 * Find the path of bitmap "name" with extension "ext" in 'runtimepath'. 4627 * Return FAIL for failure and OK if buffer[MAXPATHL] contains the result. 4628 */ 4629 int 4630 gui_find_bitmap(name, buffer, ext) 4631 char_u *name; 4632 char_u *buffer; 4633 char *ext; 4634 { 4635 if (STRLEN(name) > MAXPATHL - 14) 4636 return FAIL; 4637 vim_snprintf((char *)buffer, MAXPATHL, "bitmaps/%s.%s", name, ext); 4638 if (do_in_runtimepath(buffer, FALSE, gfp_setname, buffer) == FAIL 4639 || *buffer == NUL) 4640 return FAIL; 4641 return OK; 4642 } 4643 4644 # if !defined(HAVE_GTK2) || defined(PROTO) 4645 /* 4646 * Given the name of the "icon=" argument, try finding the bitmap file for the 4647 * icon. If it is an absolute path name, use it as it is. Otherwise append 4648 * "ext" and search for it in 'runtimepath'. 4649 * The result is put in "buffer[MAXPATHL]". If something fails "buffer" 4650 * contains "name". 4651 */ 4652 void 4653 gui_find_iconfile(name, buffer, ext) 4654 char_u *name; 4655 char_u *buffer; 4656 char *ext; 4657 { 4658 char_u buf[MAXPATHL + 1]; 4659 4660 expand_env(name, buffer, MAXPATHL); 4661 if (!mch_isFullName(buffer) && gui_find_bitmap(buffer, buf, ext) == OK) 4662 STRCPY(buffer, buf); 4663 } 4664 # endif 4665 #endif 4666 4667 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11) || defined(PROTO) 4668 void 4669 display_errors() 4670 { 4671 char_u *p; 4672 4673 if (isatty(2)) 4674 fflush(stderr); 4675 else if (error_ga.ga_data != NULL) 4676 { 4677 /* avoid putting up a message box with blanks only */ 4678 for (p = (char_u *)error_ga.ga_data; *p != NUL; ++p) 4679 if (!isspace(*p)) 4680 { 4681 /* Truncate a very long message, it will go off-screen. */ 4682 if (STRLEN(p) > 2000) 4683 STRCPY(p + 2000 - 14, "...(truncated)"); 4684 (void)do_dialog(VIM_ERROR, (char_u *)_("Error"), 4685 p, (char_u *)_("&Ok"), 1, NULL); 4686 break; 4687 } 4688 ga_clear(&error_ga); 4689 } 4690 } 4691 #endif 4692 4693 #if defined(NO_CONSOLE_INPUT) || defined(PROTO) 4694 /* 4695 * Return TRUE if still starting up and there is no place to enter text. 4696 * For GTK and X11 we check if stderr is not a tty, which means we were 4697 * (probably) started from the desktop. Also check stdin, "vim >& file" does 4698 * allow typing on stdin. 4699 */ 4700 int 4701 no_console_input() 4702 { 4703 return ((!gui.in_use || gui.starting) 4704 # ifndef NO_CONSOLE 4705 && !isatty(0) && !isatty(2) 4706 # endif 4707 ); 4708 } 4709 #endif 4710 4711 #if defined(FIND_REPLACE_DIALOG) || defined(FEAT_SUN_WORKSHOP) \ 4712 || defined(PROTO) 4713 /* 4714 * Update the current window and the screen. 4715 */ 4716 void 4717 gui_update_screen() 4718 { 4719 update_topline(); 4720 validate_cursor(); 4721 update_screen(0); /* may need to update the screen */ 4722 setcursor(); 4723 out_flush(); /* make sure output has been written */ 4724 gui_update_cursor(TRUE, FALSE); 4725 gui_mch_flush(); 4726 } 4727 #endif 4728 4729 #if defined(FIND_REPLACE_DIALOG) || defined(PROTO) 4730 static void concat_esc __ARGS((garray_T *gap, char_u *text, int what)); 4731 4732 /* 4733 * Get the text to use in a find/replace dialog. Uses the last search pattern 4734 * if the argument is empty. 4735 * Returns an allocated string. 4736 */ 4737 char_u * 4738 get_find_dialog_text(arg, wwordp, mcasep) 4739 char_u *arg; 4740 int *wwordp; /* return: TRUE if \< \> found */ 4741 int *mcasep; /* return: TRUE if \C found */ 4742 { 4743 char_u *text; 4744 4745 if (*arg == NUL) 4746 text = last_search_pat(); 4747 else 4748 text = arg; 4749 if (text != NULL) 4750 { 4751 text = vim_strsave(text); 4752 if (text != NULL) 4753 { 4754 int len = STRLEN(text); 4755 int i; 4756 4757 /* Remove "\V" */ 4758 if (len >= 2 && STRNCMP(text, "\\V", 2) == 0) 4759 { 4760 mch_memmove(text, text + 2, (size_t)(len - 1)); 4761 len -= 2; 4762 } 4763 4764 /* Recognize "\c" and "\C" and remove. */ 4765 if (len >= 2 && *text == '\\' && (text[1] == 'c' || text[1] == 'C')) 4766 { 4767 *mcasep = (text[1] == 'C'); 4768 mch_memmove(text, text + 2, (size_t)(len - 1)); 4769 len -= 2; 4770 } 4771 4772 /* Recognize "\<text\>" and remove. */ 4773 if (len >= 4 4774 && STRNCMP(text, "\\<", 2) == 0 4775 && STRNCMP(text + len - 2, "\\>", 2) == 0) 4776 { 4777 *wwordp = TRUE; 4778 mch_memmove(text, text + 2, (size_t)(len - 4)); 4779 text[len - 4] = NUL; 4780 } 4781 4782 /* Recognize "\/" or "\?" and remove. */ 4783 for (i = 0; i + 1 < len; ++i) 4784 if (text[i] == '\\' && (text[i + 1] == '/' 4785 || text[i + 1] == '?')) 4786 { 4787 mch_memmove(text + i, text + i + 1, (size_t)(len - i)); 4788 --len; 4789 } 4790 } 4791 } 4792 return text; 4793 } 4794 4795 /* 4796 * Concatenate "text" to grow array "gap", escaping "what" with a backslash. 4797 */ 4798 static void 4799 concat_esc(gap, text, what) 4800 garray_T *gap; 4801 char_u *text; 4802 int what; 4803 { 4804 while (*text != NUL) 4805 { 4806 #ifdef FEAT_MBYTE 4807 int l = (*mb_ptr2len)(text); 4808 if (l > 1) 4809 { 4810 while (--l >= 0) 4811 ga_append(gap, *text++); 4812 continue; 4813 } 4814 #endif 4815 if (*text == what) 4816 ga_append(gap, '\\'); 4817 ga_append(gap, *text); 4818 ++text; 4819 } 4820 } 4821 4822 /* 4823 * Handle the press of a button in the find-replace dialog. 4824 * Return TRUE when something was added to the input buffer. 4825 */ 4826 int 4827 gui_do_findrepl(flags, find_text, repl_text, down) 4828 int flags; /* one of FRD_REPLACE, FRD_FINDNEXT, etc. */ 4829 char_u *find_text; 4830 char_u *repl_text; 4831 int down; /* Search downwards. */ 4832 { 4833 garray_T ga; 4834 int i; 4835 int type = (flags & FRD_TYPE_MASK); 4836 char_u *p; 4837 regmatch_T regmatch; 4838 int save_did_emsg = did_emsg; 4839 4840 ga_init2(&ga, 1, 100); 4841 if (type == FRD_REPLACEALL) 4842 ga_concat(&ga, (char_u *)"%s/"); 4843 4844 ga_concat(&ga, (char_u *)"\\V"); 4845 if (flags & FRD_MATCH_CASE) 4846 ga_concat(&ga, (char_u *)"\\C"); 4847 else 4848 ga_concat(&ga, (char_u *)"\\c"); 4849 if (flags & FRD_WHOLE_WORD) 4850 ga_concat(&ga, (char_u *)"\\<"); 4851 if (type == FRD_REPLACEALL || down) 4852 concat_esc(&ga, find_text, '/'); /* escape slashes */ 4853 else 4854 concat_esc(&ga, find_text, '?'); /* escape '?' */ 4855 if (flags & FRD_WHOLE_WORD) 4856 ga_concat(&ga, (char_u *)"\\>"); 4857 4858 if (type == FRD_REPLACEALL) 4859 { 4860 ga_concat(&ga, (char_u *)"/"); 4861 /* escape / and \ */ 4862 p = vim_strsave_escaped(repl_text, (char_u *)"/\\"); 4863 if (p != NULL) 4864 ga_concat(&ga, p); 4865 vim_free(p); 4866 ga_concat(&ga, (char_u *)"/g"); 4867 } 4868 ga_append(&ga, NUL); 4869 4870 if (type == FRD_REPLACE) 4871 { 4872 /* Do the replacement when the text at the cursor matches. Thus no 4873 * replacement is done if the cursor was moved! */ 4874 regmatch.regprog = vim_regcomp(ga.ga_data, RE_MAGIC + RE_STRING); 4875 regmatch.rm_ic = 0; 4876 if (regmatch.regprog != NULL) 4877 { 4878 p = ml_get_cursor(); 4879 if (vim_regexec_nl(®match, p, (colnr_T)0) 4880 && regmatch.startp[0] == p) 4881 { 4882 /* Clear the command line to remove any old "No match" 4883 * error. */ 4884 msg_end_prompt(); 4885 4886 if (u_save_cursor() == OK) 4887 { 4888 /* A button was pressed thus undo should be synced. */ 4889 if (no_u_sync == 0) 4890 u_sync(); 4891 4892 del_bytes((long)(regmatch.endp[0] - regmatch.startp[0]), 4893 FALSE, FALSE); 4894 ins_str(repl_text); 4895 } 4896 } 4897 else 4898 MSG(_("No match at cursor, finding next")); 4899 vim_free(regmatch.regprog); 4900 } 4901 } 4902 4903 if (type == FRD_REPLACEALL) 4904 { 4905 /* A button was pressed, thus undo should be synced. */ 4906 if (no_u_sync == 0) 4907 u_sync(); 4908 do_cmdline_cmd(ga.ga_data); 4909 } 4910 else 4911 { 4912 /* Search for the next match. */ 4913 i = msg_scroll; 4914 do_search(NULL, down ? '/' : '?', ga.ga_data, 1L, 4915 SEARCH_MSG + SEARCH_MARK); 4916 msg_scroll = i; /* don't let an error message set msg_scroll */ 4917 } 4918 4919 /* Don't want to pass did_emsg to other code, it may cause disabling 4920 * syntax HL if we were busy redrawing. */ 4921 did_emsg = save_did_emsg; 4922 4923 if (State & (NORMAL | INSERT)) 4924 { 4925 gui_update_screen(); /* update the screen */ 4926 msg_didout = 0; /* overwrite any message */ 4927 need_wait_return = FALSE; /* don't wait for return */ 4928 } 4929 4930 vim_free(ga.ga_data); 4931 return (ga.ga_len > 0); 4932 } 4933 4934 #endif 4935 4936 #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ 4937 || defined(FEAT_GUI_MSWIN) \ 4938 || defined(FEAT_GUI_MAC) \ 4939 || defined(PROTO) 4940 4941 #ifdef FEAT_WINDOWS 4942 static void gui_wingoto_xy __ARGS((int x, int y)); 4943 4944 /* 4945 * Jump to the window at specified point (x, y). 4946 */ 4947 static void 4948 gui_wingoto_xy(x, y) 4949 int x; 4950 int y; 4951 { 4952 int row = Y_2_ROW(y); 4953 int col = X_2_COL(x); 4954 win_T *wp; 4955 4956 if (row >= 0 && col >= 0) 4957 { 4958 wp = mouse_find_win(&row, &col); 4959 if (wp != NULL && wp != curwin) 4960 win_goto(wp); 4961 } 4962 } 4963 #endif 4964 4965 /* 4966 * Process file drop. Mouse cursor position, key modifiers, name of files 4967 * and count of files are given. Argument "fnames[count]" has full pathnames 4968 * of dropped files, they will be freed in this function, and caller can't use 4969 * fnames after call this function. 4970 */ 4971 /*ARGSUSED*/ 4972 void 4973 gui_handle_drop(x, y, modifiers, fnames, count) 4974 int x; 4975 int y; 4976 int_u modifiers; 4977 char_u **fnames; 4978 int count; 4979 { 4980 int i; 4981 char_u *p; 4982 4983 /* 4984 * When the cursor is at the command line, add the file names to the 4985 * command line, don't edit the files. 4986 */ 4987 if (State & CMDLINE) 4988 { 4989 shorten_filenames(fnames, count); 4990 for (i = 0; i < count; ++i) 4991 { 4992 if (fnames[i] != NULL) 4993 { 4994 if (i > 0) 4995 add_to_input_buf((char_u*)" ", 1); 4996 4997 /* We don't know what command is used thus we can't be sure 4998 * about which characters need to be escaped. Only escape the 4999 * most common ones. */ 5000 # ifdef BACKSLASH_IN_FILENAME 5001 p = vim_strsave_escaped(fnames[i], (char_u *)" \t\"|"); 5002 # else 5003 p = vim_strsave_escaped(fnames[i], (char_u *)"\\ \t\"|"); 5004 # endif 5005 if (p != NULL) 5006 add_to_input_buf(p, (int)STRLEN(p)); 5007 vim_free(p); 5008 vim_free(fnames[i]); 5009 } 5010 } 5011 vim_free(fnames); 5012 } 5013 else 5014 { 5015 /* Go to the window under mouse cursor, then shorten given "fnames" by 5016 * current window, because a window can have local current dir. */ 5017 # ifdef FEAT_WINDOWS 5018 gui_wingoto_xy(x, y); 5019 # endif 5020 shorten_filenames(fnames, count); 5021 5022 /* If Shift held down, remember the first item. */ 5023 if ((modifiers & MOUSE_SHIFT) != 0) 5024 p = vim_strsave(fnames[0]); 5025 else 5026 p = NULL; 5027 5028 /* Handle the drop, :edit or :split to get to the file. This also 5029 * frees fnames[]. Skip this if there is only one item it's a 5030 * directory and Shift is held down. */ 5031 if (count == 1 && (modifiers & MOUSE_SHIFT) != 0 5032 && mch_isdir(fnames[0])) 5033 { 5034 vim_free(fnames[0]); 5035 vim_free(fnames); 5036 } 5037 else 5038 handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0); 5039 5040 /* If Shift held down, change to first file's directory. If the first 5041 * item is a directory, change to that directory (and let the explorer 5042 * plugin show the contents). */ 5043 if (p != NULL) 5044 { 5045 if (mch_isdir(p)) 5046 { 5047 if (mch_chdir((char *)p) == 0) 5048 shorten_fnames(TRUE); 5049 } 5050 else if (vim_chdirfile(p) == OK) 5051 shorten_fnames(TRUE); 5052 vim_free(p); 5053 } 5054 5055 /* Update the screen display */ 5056 update_screen(NOT_VALID); 5057 # ifdef FEAT_MENU 5058 gui_update_menus(0); 5059 # endif 5060 setcursor(); 5061 out_flush(); 5062 gui_update_cursor(FALSE, FALSE); 5063 gui_mch_flush(); 5064 } 5065 } 5066 #endif 5067