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