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