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