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