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