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