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