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) || defined(FEAT_GUI_KDE) 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) || defined(FEAT_GUI_KDE) 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_KDE) || 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_KDE) && !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 #ifndef FEAT_GUI_KDE 1216 # ifdef FEAT_TOOLBAR 1217 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1218 # if defined(FEAT_GUI_MSWIN) && defined(FEAT_TOOLBAR) 1219 base_height += (TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT); 1220 # else 1221 base_height += gui.toolbar_height; 1222 # endif 1223 # endif 1224 #endif 1225 # ifdef FEAT_FOOTER 1226 if (vim_strchr(p_go, GO_FOOTER) != NULL) 1227 base_height += gui.footer_height; 1228 # endif 1229 # if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) 1230 base_height += gui_mch_text_area_extra_height(); 1231 # endif 1232 #endif 1233 return base_height; 1234 } 1235 1236 /* 1237 * Should be called after the GUI shell has been resized. Its arguments are 1238 * the new width and height of the shell in pixels. 1239 */ 1240 void 1241 gui_resize_shell(pixel_width, pixel_height) 1242 int pixel_width; 1243 int pixel_height; 1244 { 1245 static int busy = FALSE; 1246 1247 if (!gui.shell_created) /* ignore when still initializing */ 1248 return; 1249 1250 /* 1251 * Can't resize the screen while it is being redrawn. Remember the new 1252 * size and handle it later. 1253 */ 1254 if (updating_screen || busy) 1255 { 1256 new_pixel_width = pixel_width; 1257 new_pixel_height = pixel_height; 1258 return; 1259 } 1260 1261 again: 1262 busy = TRUE; 1263 1264 /* Flush pending output before redrawing */ 1265 out_flush(); 1266 1267 gui.num_cols = (pixel_width - gui_get_base_width()) / gui.char_width; 1268 gui.num_rows = (pixel_height - gui_get_base_height() 1269 #if !defined(FEAT_GUI_PHOTON) && !defined(FEAT_GUI_MSWIN) 1270 + (gui.char_height / 2) 1271 #endif 1272 ) / gui.char_height; 1273 1274 gui_position_components(pixel_width); 1275 1276 gui_reset_scroll_region(); 1277 /* 1278 * At the "more" and ":confirm" prompt there is no redraw, put the cursor 1279 * at the last line here (why does it have to be one row too low?). 1280 */ 1281 if (State == ASKMORE || State == CONFIRM) 1282 gui.row = gui.num_rows; 1283 1284 /* Only comparing Rows and Columns may be sufficient, but let's stay on 1285 * the safe side. */ 1286 if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns 1287 || gui.num_rows != Rows || gui.num_cols != Columns) 1288 shell_resized(); 1289 1290 gui_update_scrollbars(TRUE); 1291 gui_update_cursor(FALSE, TRUE); 1292 #if defined(FEAT_XIM) && !defined(HAVE_GTK2) 1293 xim_set_status_area(); 1294 #endif 1295 1296 busy = FALSE; 1297 /* 1298 * We could have been called again while redrawing the screen. 1299 * Need to do it all again with the latest size then. 1300 */ 1301 if (new_pixel_height) 1302 { 1303 pixel_width = new_pixel_width; 1304 pixel_height = new_pixel_height; 1305 new_pixel_width = 0; 1306 new_pixel_height = 0; 1307 goto again; 1308 } 1309 } 1310 1311 /* 1312 * Check if gui_resize_shell() must be called. 1313 */ 1314 void 1315 gui_may_resize_shell() 1316 { 1317 int h, w; 1318 1319 if (new_pixel_height) 1320 { 1321 /* careful: gui_resize_shell() may postpone the resize again if we 1322 * were called indirectly by it */ 1323 w = new_pixel_width; 1324 h = new_pixel_height; 1325 new_pixel_width = 0; 1326 new_pixel_height = 0; 1327 gui_resize_shell(w, h); 1328 } 1329 } 1330 1331 int 1332 gui_get_shellsize() 1333 { 1334 Rows = gui.num_rows; 1335 Columns = gui.num_cols; 1336 return OK; 1337 } 1338 1339 /* 1340 * Set the size of the Vim shell according to Rows and Columns. 1341 * If "fit_to_display" is TRUE then the size may be reduced to fit the window 1342 * on the screen. 1343 */ 1344 /*ARGSUSED*/ 1345 void 1346 gui_set_shellsize(mustset, fit_to_display) 1347 int mustset; /* set by the user */ 1348 int fit_to_display; 1349 { 1350 int base_width; 1351 int base_height; 1352 int width; 1353 int height; 1354 int min_width; 1355 int min_height; 1356 int screen_w; 1357 int screen_h; 1358 1359 if (!gui.shell_created) 1360 return; 1361 1362 #ifdef MSWIN 1363 /* If not setting to a user specified size and maximized, calculate the 1364 * number of characters that fit in the maximized window. */ 1365 if (!mustset && gui_mch_maximized()) 1366 { 1367 gui_mch_newfont(); 1368 return; 1369 } 1370 #endif 1371 1372 base_width = gui_get_base_width(); 1373 base_height = gui_get_base_height(); 1374 #ifdef USE_SUN_WORKSHOP 1375 if (!mustset && usingSunWorkShop 1376 && workshop_get_width_height(&width, &height)) 1377 { 1378 Columns = (width - base_width + gui.char_width - 1) / gui.char_width; 1379 Rows = (height - base_height + gui.char_height - 1) / gui.char_height; 1380 } 1381 else 1382 #endif 1383 { 1384 width = Columns * gui.char_width + base_width; 1385 height = Rows * gui.char_height + base_height; 1386 } 1387 1388 if (fit_to_display) 1389 { 1390 gui_mch_get_screen_dimensions(&screen_w, &screen_h); 1391 if (width > screen_w) 1392 { 1393 Columns = (screen_w - base_width) / gui.char_width; 1394 if (Columns < MIN_COLUMNS) 1395 Columns = MIN_COLUMNS; 1396 width = Columns * gui.char_width + base_width; 1397 } 1398 if (height > screen_h) 1399 { 1400 Rows = (screen_h - base_height) / gui.char_height; 1401 check_shellsize(); 1402 height = Rows * gui.char_height + base_height; 1403 } 1404 } 1405 gui.num_cols = Columns; 1406 gui.num_rows = Rows; 1407 1408 min_width = base_width + MIN_COLUMNS * gui.char_width; 1409 min_height = base_height + MIN_LINES * gui.char_height; 1410 1411 gui_mch_set_shellsize(width, height, min_width, min_height, 1412 base_width, base_height); 1413 if (fit_to_display) 1414 { 1415 int x, y; 1416 1417 /* Some window managers put the Vim window left of/above the screen. */ 1418 gui_mch_update(); 1419 if (gui_mch_get_winpos(&x, &y) == OK && (x < 0 || y < 0)) 1420 gui_mch_set_winpos(x < 0 ? 0 : x, y < 0 ? 0 : y); 1421 } 1422 1423 gui_position_components(width); 1424 gui_update_scrollbars(TRUE); 1425 gui_reset_scroll_region(); 1426 } 1427 1428 /* 1429 * Called when Rows and/or Columns has changed. 1430 */ 1431 void 1432 gui_new_shellsize() 1433 { 1434 gui_reset_scroll_region(); 1435 } 1436 1437 /* 1438 * Make scroll region cover whole screen. 1439 */ 1440 void 1441 gui_reset_scroll_region() 1442 { 1443 gui.scroll_region_top = 0; 1444 gui.scroll_region_bot = gui.num_rows - 1; 1445 gui.scroll_region_left = 0; 1446 gui.scroll_region_right = gui.num_cols - 1; 1447 } 1448 1449 void 1450 gui_start_highlight(mask) 1451 int mask; 1452 { 1453 if (mask > HL_ALL) /* highlight code */ 1454 gui.highlight_mask = mask; 1455 else /* mask */ 1456 gui.highlight_mask |= mask; 1457 } 1458 1459 void 1460 gui_stop_highlight(mask) 1461 int mask; 1462 { 1463 if (mask > HL_ALL) /* highlight code */ 1464 gui.highlight_mask = HL_NORMAL; 1465 else /* mask */ 1466 gui.highlight_mask &= ~mask; 1467 } 1468 1469 /* 1470 * Clear a rectangular region of the screen from text pos (row1, col1) to 1471 * (row2, col2) inclusive. 1472 */ 1473 void 1474 gui_clear_block(row1, col1, row2, col2) 1475 int row1; 1476 int col1; 1477 int row2; 1478 int col2; 1479 { 1480 /* Clear the selection if we are about to write over it */ 1481 clip_may_clear_selection(row1, row2); 1482 1483 gui_mch_clear_block(row1, col1, row2, col2); 1484 1485 /* Invalidate cursor if it was in this block */ 1486 if ( gui.cursor_row >= row1 && gui.cursor_row <= row2 1487 && gui.cursor_col >= col1 && gui.cursor_col <= col2) 1488 gui.cursor_is_valid = FALSE; 1489 } 1490 1491 /* 1492 * Write code to update the cursor later. This avoids the need to flush the 1493 * output buffer before calling gui_update_cursor(). 1494 */ 1495 void 1496 gui_update_cursor_later() 1497 { 1498 OUT_STR(IF_EB("\033|s", ESC_STR "|s")); 1499 } 1500 1501 void 1502 gui_write(s, len) 1503 char_u *s; 1504 int len; 1505 { 1506 char_u *p; 1507 int arg1 = 0, arg2 = 0; 1508 /* this doesn't make sense, disabled until someone can explain why it 1509 * would be needed */ 1510 #if 0 && (defined(RISCOS) || defined(WIN16)) 1511 int force_cursor = TRUE; /* JK230798, stop Vim being smart or 1512 our redraw speed will suffer */ 1513 #else 1514 int force_cursor = FALSE; /* force cursor update */ 1515 #endif 1516 int force_scrollbar = FALSE; 1517 static win_T *old_curwin = NULL; 1518 1519 /* #define DEBUG_GUI_WRITE */ 1520 #ifdef DEBUG_GUI_WRITE 1521 { 1522 int i; 1523 char_u *str; 1524 1525 printf("gui_write(%d):\n ", len); 1526 for (i = 0; i < len; i++) 1527 if (s[i] == ESC) 1528 { 1529 if (i != 0) 1530 printf("\n "); 1531 printf("<ESC>"); 1532 } 1533 else 1534 { 1535 str = transchar_byte(s[i]); 1536 if (str[0] && str[1]) 1537 printf("<%s>", (char *)str); 1538 else 1539 printf("%s", (char *)str); 1540 } 1541 printf("\n"); 1542 } 1543 #endif 1544 while (len) 1545 { 1546 if (s[0] == ESC && s[1] == '|') 1547 { 1548 p = s + 2; 1549 if (VIM_ISDIGIT(*p)) 1550 { 1551 arg1 = getdigits(&p); 1552 if (p > s + len) 1553 break; 1554 if (*p == ';') 1555 { 1556 ++p; 1557 arg2 = getdigits(&p); 1558 if (p > s + len) 1559 break; 1560 } 1561 } 1562 switch (*p) 1563 { 1564 case 'C': /* Clear screen */ 1565 clip_scroll_selection(9999); 1566 gui_mch_clear_all(); 1567 gui.cursor_is_valid = FALSE; 1568 force_scrollbar = TRUE; 1569 break; 1570 case 'M': /* Move cursor */ 1571 gui_set_cursor(arg1, arg2); 1572 break; 1573 case 's': /* force cursor (shape) update */ 1574 force_cursor = TRUE; 1575 break; 1576 case 'R': /* Set scroll region */ 1577 if (arg1 < arg2) 1578 { 1579 gui.scroll_region_top = arg1; 1580 gui.scroll_region_bot = arg2; 1581 } 1582 else 1583 { 1584 gui.scroll_region_top = arg2; 1585 gui.scroll_region_bot = arg1; 1586 } 1587 break; 1588 #ifdef FEAT_VERTSPLIT 1589 case 'V': /* Set vertical scroll region */ 1590 if (arg1 < arg2) 1591 { 1592 gui.scroll_region_left = arg1; 1593 gui.scroll_region_right = arg2; 1594 } 1595 else 1596 { 1597 gui.scroll_region_left = arg2; 1598 gui.scroll_region_right = arg1; 1599 } 1600 break; 1601 #endif 1602 case 'd': /* Delete line */ 1603 gui_delete_lines(gui.row, 1); 1604 break; 1605 case 'D': /* Delete lines */ 1606 gui_delete_lines(gui.row, arg1); 1607 break; 1608 case 'i': /* Insert line */ 1609 gui_insert_lines(gui.row, 1); 1610 break; 1611 case 'I': /* Insert lines */ 1612 gui_insert_lines(gui.row, arg1); 1613 break; 1614 case '$': /* Clear to end-of-line */ 1615 gui_clear_block(gui.row, gui.col, gui.row, 1616 (int)Columns - 1); 1617 break; 1618 case 'h': /* Turn on highlighting */ 1619 gui_start_highlight(arg1); 1620 break; 1621 case 'H': /* Turn off highlighting */ 1622 gui_stop_highlight(arg1); 1623 break; 1624 case 'f': /* flash the window (visual bell) */ 1625 gui_mch_flash(arg1 == 0 ? 20 : arg1); 1626 break; 1627 default: 1628 p = s + 1; /* Skip the ESC */ 1629 break; 1630 } 1631 len -= (int)(++p - s); 1632 s = p; 1633 } 1634 else if ( 1635 #ifdef EBCDIC 1636 CtrlChar(s[0]) != 0 /* Ctrl character */ 1637 #else 1638 s[0] < 0x20 /* Ctrl character */ 1639 #endif 1640 #ifdef FEAT_SIGN_ICONS 1641 && s[0] != SIGN_BYTE 1642 # ifdef FEAT_NETBEANS_INTG 1643 && s[0] != MULTISIGN_BYTE 1644 # endif 1645 #endif 1646 ) 1647 { 1648 if (s[0] == '\n') /* NL */ 1649 { 1650 gui.col = 0; 1651 if (gui.row < gui.scroll_region_bot) 1652 gui.row++; 1653 else 1654 gui_delete_lines(gui.scroll_region_top, 1); 1655 } 1656 else if (s[0] == '\r') /* CR */ 1657 { 1658 gui.col = 0; 1659 } 1660 else if (s[0] == '\b') /* Backspace */ 1661 { 1662 if (gui.col) 1663 --gui.col; 1664 } 1665 else if (s[0] == Ctrl_L) /* cursor-right */ 1666 { 1667 ++gui.col; 1668 } 1669 else if (s[0] == Ctrl_G) /* Beep */ 1670 { 1671 gui_mch_beep(); 1672 } 1673 /* Other Ctrl character: shouldn't happen! */ 1674 1675 --len; /* Skip this char */ 1676 ++s; 1677 } 1678 else 1679 { 1680 p = s; 1681 while (len > 0 && ( 1682 #ifdef EBCDIC 1683 CtrlChar(*p) == 0 1684 #else 1685 *p >= 0x20 1686 #endif 1687 #ifdef FEAT_SIGN_ICONS 1688 || *p == SIGN_BYTE 1689 # ifdef FEAT_NETBEANS_INTG 1690 || *p == MULTISIGN_BYTE 1691 # endif 1692 #endif 1693 )) 1694 { 1695 len--; 1696 p++; 1697 } 1698 gui_outstr(s, (int)(p - s)); 1699 s = p; 1700 } 1701 } 1702 1703 /* Postponed update of the cursor (won't work if "can_update_cursor" isn't 1704 * set). */ 1705 if (force_cursor) 1706 gui_update_cursor(TRUE, TRUE); 1707 1708 /* When switching to another window the dragging must have stopped. 1709 * Required for GTK, dragged_sb isn't reset. */ 1710 if (old_curwin != curwin) 1711 gui.dragged_sb = SBAR_NONE; 1712 1713 /* Update the scrollbars after clearing the screen or when switched 1714 * to another window. 1715 * Update the horizontal scrollbar always, it's difficult to check all 1716 * situations where it might change. */ 1717 if (force_scrollbar || old_curwin != curwin) 1718 gui_update_scrollbars(force_scrollbar); 1719 else 1720 gui_update_horiz_scrollbar(FALSE); 1721 old_curwin = curwin; 1722 1723 /* 1724 * We need to make sure this is cleared since Athena doesn't tell us when 1725 * he is done dragging. Do the same for GTK. 1726 */ 1727 #if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_KDE) 1728 gui.dragged_sb = SBAR_NONE; 1729 #endif 1730 1731 gui_mch_flush(); /* In case vim decides to take a nap */ 1732 } 1733 1734 /* 1735 * When ScreenLines[] is invalid, updating the cursor should not be done, it 1736 * produces wrong results. Call gui_dont_update_cursor() before that code and 1737 * gui_can_update_cursor() afterwards. 1738 */ 1739 void 1740 gui_dont_update_cursor() 1741 { 1742 if (gui.in_use) 1743 { 1744 /* Undraw the cursor now, we probably can't do it after the change. */ 1745 gui_undraw_cursor(); 1746 can_update_cursor = FALSE; 1747 } 1748 } 1749 1750 void 1751 gui_can_update_cursor() 1752 { 1753 can_update_cursor = TRUE; 1754 /* No need to update the cursor right now, there is always more output 1755 * after scrolling. */ 1756 } 1757 1758 static void 1759 gui_outstr(s, len) 1760 char_u *s; 1761 int len; 1762 { 1763 int this_len; 1764 #ifdef FEAT_MBYTE 1765 int cells; 1766 #endif 1767 1768 if (len == 0) 1769 return; 1770 1771 if (len < 0) 1772 len = (int)STRLEN(s); 1773 1774 while (len > 0) 1775 { 1776 #ifdef FEAT_MBYTE 1777 if (has_mbyte) 1778 { 1779 /* Find out how many chars fit in the current line. */ 1780 cells = 0; 1781 for (this_len = 0; this_len < len; ) 1782 { 1783 cells += (*mb_ptr2cells)(s + this_len); 1784 if (gui.col + cells > Columns) 1785 break; 1786 this_len += (*mb_ptr2len)(s + this_len); 1787 } 1788 if (this_len > len) 1789 this_len = len; /* don't include following composing char */ 1790 } 1791 else 1792 #endif 1793 if (gui.col + len > Columns) 1794 this_len = Columns - gui.col; 1795 else 1796 this_len = len; 1797 1798 (void)gui_outstr_nowrap(s, this_len, 1799 0, (guicolor_T)0, (guicolor_T)0, 0); 1800 s += this_len; 1801 len -= this_len; 1802 #ifdef FEAT_MBYTE 1803 /* fill up for a double-width char that doesn't fit. */ 1804 if (len > 0 && gui.col < Columns) 1805 (void)gui_outstr_nowrap((char_u *)" ", 1, 1806 0, (guicolor_T)0, (guicolor_T)0, 0); 1807 #endif 1808 /* The cursor may wrap to the next line. */ 1809 if (gui.col >= Columns) 1810 { 1811 gui.col = 0; 1812 gui.row++; 1813 } 1814 } 1815 } 1816 1817 /* 1818 * Output one character (may be one or two display cells). 1819 * Caller must check for valid "off". 1820 * Returns FAIL or OK, just like gui_outstr_nowrap(). 1821 */ 1822 static int 1823 gui_screenchar(off, flags, fg, bg, back) 1824 int off; /* Offset from start of screen */ 1825 int flags; 1826 guicolor_T fg, bg; /* colors for cursor */ 1827 int back; /* backup this many chars when using bold trick */ 1828 { 1829 #ifdef FEAT_MBYTE 1830 char_u buf[MB_MAXBYTES + 1]; 1831 1832 /* Don't draw right halve of a double-width UTF-8 char. "cannot happen" */ 1833 if (enc_utf8 && ScreenLines[off] == 0) 1834 return OK; 1835 1836 if (enc_utf8 && ScreenLinesUC[off] != 0) 1837 /* Draw UTF-8 multi-byte character. */ 1838 return gui_outstr_nowrap(buf, utfc_char2bytes(off, buf), 1839 flags, fg, bg, back); 1840 1841 if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 1842 { 1843 buf[0] = ScreenLines[off]; 1844 buf[1] = ScreenLines2[off]; 1845 return gui_outstr_nowrap(buf, 2, flags, fg, bg, back); 1846 } 1847 1848 /* Draw non-multi-byte character or DBCS character. */ 1849 return gui_outstr_nowrap(ScreenLines + off, 1850 enc_dbcs ? (*mb_ptr2len)(ScreenLines + off) : 1, 1851 flags, fg, bg, back); 1852 #else 1853 return gui_outstr_nowrap(ScreenLines + off, 1, flags, fg, bg, back); 1854 #endif 1855 } 1856 1857 #ifdef HAVE_GTK2 1858 /* 1859 * Output the string at the given screen position. This is used in place 1860 * of gui_screenchar() where possible because Pango needs as much context 1861 * as possible to work nicely. It's a lot faster as well. 1862 */ 1863 static int 1864 gui_screenstr(off, len, flags, fg, bg, back) 1865 int off; /* Offset from start of screen */ 1866 int len; /* string length in screen cells */ 1867 int flags; 1868 guicolor_T fg, bg; /* colors for cursor */ 1869 int back; /* backup this many chars when using bold trick */ 1870 { 1871 char_u *buf; 1872 int outlen = 0; 1873 int i; 1874 int retval; 1875 1876 if (len <= 0) /* "cannot happen"? */ 1877 return OK; 1878 1879 if (enc_utf8) 1880 { 1881 buf = alloc((unsigned)(len * MB_MAXBYTES + 1)); 1882 if (buf == NULL) 1883 return OK; /* not much we could do here... */ 1884 1885 for (i = off; i < off + len; ++i) 1886 { 1887 if (ScreenLines[i] == 0) 1888 continue; /* skip second half of double-width char */ 1889 1890 if (ScreenLinesUC[i] == 0) 1891 buf[outlen++] = ScreenLines[i]; 1892 else 1893 outlen += utfc_char2bytes(i, buf + outlen); 1894 } 1895 1896 buf[outlen] = NUL; /* only to aid debugging */ 1897 retval = gui_outstr_nowrap(buf, outlen, flags, fg, bg, back); 1898 vim_free(buf); 1899 1900 return retval; 1901 } 1902 else if (enc_dbcs == DBCS_JPNU) 1903 { 1904 buf = alloc((unsigned)(len * 2 + 1)); 1905 if (buf == NULL) 1906 return OK; /* not much we could do here... */ 1907 1908 for (i = off; i < off + len; ++i) 1909 { 1910 buf[outlen++] = ScreenLines[i]; 1911 1912 /* handle double-byte single-width char */ 1913 if (ScreenLines[i] == 0x8e) 1914 buf[outlen++] = ScreenLines2[i]; 1915 else if (MB_BYTE2LEN(ScreenLines[i]) == 2) 1916 buf[outlen++] = ScreenLines[++i]; 1917 } 1918 1919 buf[outlen] = NUL; /* only to aid debugging */ 1920 retval = gui_outstr_nowrap(buf, outlen, flags, fg, bg, back); 1921 vim_free(buf); 1922 1923 return retval; 1924 } 1925 else 1926 { 1927 return gui_outstr_nowrap(&ScreenLines[off], len, 1928 flags, fg, bg, back); 1929 } 1930 } 1931 #endif /* HAVE_GTK2 */ 1932 1933 /* 1934 * Output the given string at the current cursor position. If the string is 1935 * too long to fit on the line, then it is truncated. 1936 * "flags": 1937 * GUI_MON_IS_CURSOR should only be used when this function is being called to 1938 * actually draw (an inverted) cursor. 1939 * GUI_MON_TRS_CURSOR is used to draw the cursor text with a transparant 1940 * background. 1941 * GUI_MON_NOCLEAR is used to avoid clearing the selection when drawing over 1942 * it. 1943 * Returns OK, unless "back" is non-zero and using the bold trick, then return 1944 * FAIL (the caller should start drawing "back" chars back). 1945 */ 1946 int 1947 gui_outstr_nowrap(s, len, flags, fg, bg, back) 1948 char_u *s; 1949 int len; 1950 int flags; 1951 guicolor_T fg, bg; /* colors for cursor */ 1952 int back; /* backup this many chars when using bold trick */ 1953 { 1954 long_u highlight_mask; 1955 long_u hl_mask_todo; 1956 guicolor_T fg_color; 1957 guicolor_T bg_color; 1958 guicolor_T sp_color; 1959 #if !defined(MSWIN16_FASTTEXT) && !defined(HAVE_GTK2) && !defined(FEAT_GUI_KDE) 1960 GuiFont font = NOFONT; 1961 # ifdef FEAT_XFONTSET 1962 GuiFontset fontset = NOFONTSET; 1963 # endif 1964 #endif 1965 attrentry_T *aep = NULL; 1966 int draw_flags; 1967 int col = gui.col; 1968 #ifdef FEAT_SIGN_ICONS 1969 int draw_sign = FALSE; 1970 # ifdef FEAT_NETBEANS_INTG 1971 int multi_sign = FALSE; 1972 # endif 1973 #endif 1974 1975 if (len < 0) 1976 len = (int)STRLEN(s); 1977 if (len == 0) 1978 return OK; 1979 1980 #ifdef FEAT_SIGN_ICONS 1981 if (*s == SIGN_BYTE 1982 # ifdef FEAT_NETBEANS_INTG 1983 || *s == MULTISIGN_BYTE 1984 # endif 1985 ) 1986 { 1987 # ifdef FEAT_NETBEANS_INTG 1988 if (*s == MULTISIGN_BYTE) 1989 multi_sign = TRUE; 1990 # endif 1991 /* draw spaces instead */ 1992 s = (char_u *)" "; 1993 if (len == 1 && col > 0) 1994 --col; 1995 len = 2; 1996 draw_sign = TRUE; 1997 highlight_mask = 0; 1998 } 1999 else 2000 #endif 2001 if (gui.highlight_mask > HL_ALL) 2002 { 2003 aep = syn_gui_attr2entry(gui.highlight_mask); 2004 if (aep == NULL) /* highlighting not set */ 2005 highlight_mask = 0; 2006 else 2007 highlight_mask = aep->ae_attr; 2008 } 2009 else 2010 highlight_mask = gui.highlight_mask; 2011 hl_mask_todo = highlight_mask; 2012 2013 #if !defined(MSWIN16_FASTTEXT) && !defined(HAVE_GTK2) && !defined(FEAT_GUI_KDE) 2014 /* Set the font */ 2015 if (aep != NULL && aep->ae_u.gui.font != NOFONT) 2016 font = aep->ae_u.gui.font; 2017 # ifdef FEAT_XFONTSET 2018 else if (aep != NULL && aep->ae_u.gui.fontset != NOFONTSET) 2019 fontset = aep->ae_u.gui.fontset; 2020 # endif 2021 else 2022 { 2023 # ifdef FEAT_XFONTSET 2024 if (gui.fontset != NOFONTSET) 2025 fontset = gui.fontset; 2026 else 2027 # endif 2028 if (hl_mask_todo & (HL_BOLD | HL_STANDOUT)) 2029 { 2030 if ((hl_mask_todo & HL_ITALIC) && gui.boldital_font != NOFONT) 2031 { 2032 font = gui.boldital_font; 2033 hl_mask_todo &= ~(HL_BOLD | HL_STANDOUT | HL_ITALIC); 2034 } 2035 else if (gui.bold_font != NOFONT) 2036 { 2037 font = gui.bold_font; 2038 hl_mask_todo &= ~(HL_BOLD | HL_STANDOUT); 2039 } 2040 else 2041 font = gui.norm_font; 2042 } 2043 else if ((hl_mask_todo & HL_ITALIC) && gui.ital_font != NOFONT) 2044 { 2045 font = gui.ital_font; 2046 hl_mask_todo &= ~HL_ITALIC; 2047 } 2048 else 2049 font = gui.norm_font; 2050 } 2051 # ifdef FEAT_XFONTSET 2052 if (fontset != NOFONTSET) 2053 gui_mch_set_fontset(fontset); 2054 else 2055 # endif 2056 gui_mch_set_font(font); 2057 #endif 2058 2059 draw_flags = 0; 2060 2061 /* Set the color */ 2062 bg_color = gui.back_pixel; 2063 if ((flags & GUI_MON_IS_CURSOR) && gui.in_focus) 2064 { 2065 draw_flags |= DRAW_CURSOR; 2066 fg_color = fg; 2067 bg_color = bg; 2068 sp_color = fg; 2069 } 2070 else if (aep != NULL) 2071 { 2072 fg_color = aep->ae_u.gui.fg_color; 2073 if (fg_color == INVALCOLOR) 2074 fg_color = gui.norm_pixel; 2075 bg_color = aep->ae_u.gui.bg_color; 2076 if (bg_color == INVALCOLOR) 2077 bg_color = gui.back_pixel; 2078 sp_color = aep->ae_u.gui.sp_color; 2079 if (sp_color == INVALCOLOR) 2080 sp_color = fg_color; 2081 } 2082 else 2083 { 2084 fg_color = gui.norm_pixel; 2085 sp_color = fg_color; 2086 } 2087 2088 if (highlight_mask & (HL_INVERSE | HL_STANDOUT)) 2089 { 2090 #if defined(AMIGA) || defined(RISCOS) 2091 gui_mch_set_colors(bg_color, fg_color); 2092 #else 2093 gui_mch_set_fg_color(bg_color); 2094 gui_mch_set_bg_color(fg_color); 2095 #endif 2096 } 2097 else 2098 { 2099 #if defined(AMIGA) || defined(RISCOS) 2100 gui_mch_set_colors(fg_color, bg_color); 2101 #else 2102 gui_mch_set_fg_color(fg_color); 2103 gui_mch_set_bg_color(bg_color); 2104 #endif 2105 } 2106 gui_mch_set_sp_color(sp_color); 2107 2108 /* Clear the selection if we are about to write over it */ 2109 if (!(flags & GUI_MON_NOCLEAR)) 2110 clip_may_clear_selection(gui.row, gui.row); 2111 2112 2113 #ifndef MSWIN16_FASTTEXT 2114 /* If there's no bold font, then fake it */ 2115 if (hl_mask_todo & (HL_BOLD | HL_STANDOUT)) 2116 draw_flags |= DRAW_BOLD; 2117 #endif 2118 2119 /* 2120 * When drawing bold or italic characters the spill-over from the left 2121 * neighbor may be destroyed. Let the caller backup to start redrawing 2122 * just after a blank. 2123 */ 2124 if (back != 0 && ((draw_flags & DRAW_BOLD) || (highlight_mask & HL_ITALIC))) 2125 return FAIL; 2126 2127 #if defined(RISCOS) || defined(HAVE_GTK2) || defined(FEAT_GUI_KDE) 2128 /* If there's no italic font, then fake it. 2129 * For GTK2, we don't need a different font for italic style. */ 2130 if (hl_mask_todo & HL_ITALIC) 2131 draw_flags |= DRAW_ITALIC; 2132 2133 /* Do we underline the text? */ 2134 if (hl_mask_todo & HL_UNDERLINE) 2135 draw_flags |= DRAW_UNDERL; 2136 #else 2137 /* Do we underline the text? */ 2138 if ((hl_mask_todo & HL_UNDERLINE) 2139 # ifndef MSWIN16_FASTTEXT 2140 || (hl_mask_todo & HL_ITALIC) 2141 # endif 2142 ) 2143 draw_flags |= DRAW_UNDERL; 2144 #endif 2145 /* Do we undercurl the text? */ 2146 if (hl_mask_todo & HL_UNDERCURL) 2147 draw_flags |= DRAW_UNDERC; 2148 2149 /* Do we draw transparantly? */ 2150 if (flags & GUI_MON_TRS_CURSOR) 2151 draw_flags |= DRAW_TRANSP; 2152 2153 /* 2154 * Draw the text. 2155 */ 2156 #ifdef HAVE_GTK2 2157 /* The value returned is the length in display cells */ 2158 len = gui_gtk2_draw_string(gui.row, col, s, len, draw_flags); 2159 #else 2160 # ifdef FEAT_MBYTE 2161 if (enc_utf8) 2162 { 2163 int start; /* index of bytes to be drawn */ 2164 int cells; /* cellwidth of bytes to be drawn */ 2165 int thislen; /* length of bytes to be drawin */ 2166 int cn; /* cellwidth of current char */ 2167 int i; /* index of current char */ 2168 int c; /* current char value */ 2169 int cl; /* byte length of current char */ 2170 int comping; /* current char is composing */ 2171 int scol = col; /* screen column */ 2172 int dowide; /* use 'guifontwide' */ 2173 2174 /* Break the string at a composing character, it has to be drawn on 2175 * top of the previous character. */ 2176 start = 0; 2177 cells = 0; 2178 for (i = 0; i < len; i += cl) 2179 { 2180 c = utf_ptr2char(s + i); 2181 cn = utf_char2cells(c); 2182 if (cn > 1 2183 # ifdef FEAT_XFONTSET 2184 && fontset == NOFONTSET 2185 # endif 2186 && gui.wide_font != NOFONT) 2187 dowide = TRUE; 2188 else 2189 dowide = FALSE; 2190 comping = utf_iscomposing(c); 2191 if (!comping) /* count cells from non-composing chars */ 2192 cells += cn; 2193 cl = utf_ptr2len(s + i); 2194 if (cl == 0) /* hit end of string */ 2195 len = i + cl; /* len must be wrong "cannot happen" */ 2196 2197 /* print the string so far if it's the last character or there is 2198 * a composing character. */ 2199 if (i + cl >= len || (comping && i > start) || dowide 2200 # if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_KDE) 2201 || (cn > 1 2202 # ifdef FEAT_XFONTSET 2203 /* No fontset: At least draw char after wide char at 2204 * right position. */ 2205 && fontset == NOFONTSET 2206 # endif 2207 ) 2208 # endif 2209 ) 2210 { 2211 if (comping || dowide) 2212 thislen = i - start; 2213 else 2214 thislen = i - start + cl; 2215 if (thislen > 0) 2216 { 2217 gui_mch_draw_string(gui.row, scol, s + start, thislen, 2218 draw_flags); 2219 start += thislen; 2220 } 2221 scol += cells; 2222 cells = 0; 2223 if (dowide) 2224 { 2225 #ifndef FEAT_GUI_KDE 2226 gui_mch_set_font(gui.wide_font); 2227 #endif 2228 gui_mch_draw_string(gui.row, scol - cn, 2229 s + start, cl, draw_flags); 2230 #ifndef FEAT_GUI_KDE 2231 gui_mch_set_font(font); 2232 #endif 2233 start += cl; 2234 } 2235 2236 # if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_KDE) 2237 /* No fontset: draw a space to fill the gap after a wide char 2238 * */ 2239 if (cn > 1 && (draw_flags & DRAW_TRANSP) == 0 2240 # ifdef FEAT_XFONTSET 2241 && fontset == NOFONTSET 2242 # endif 2243 && !dowide) 2244 gui_mch_draw_string(gui.row, scol - 1, (char_u *)" ", 2245 1, draw_flags); 2246 # endif 2247 } 2248 /* Draw a composing char on top of the previous char. */ 2249 if (comping) 2250 { 2251 # if (defined(__APPLE_CC__) || defined(__MRC__)) && TARGET_API_MAC_CARBON 2252 /* Carbon ATSUI autodraws composing char over previous char */ 2253 gui_mch_draw_string(gui.row, scol, s + i, cl, 2254 draw_flags | DRAW_TRANSP); 2255 # else 2256 gui_mch_draw_string(gui.row, scol - cn, s + i, cl, 2257 draw_flags | DRAW_TRANSP); 2258 # endif 2259 start = i + cl; 2260 } 2261 } 2262 /* The stuff below assumes "len" is the length in screen columns. */ 2263 len = scol - col; 2264 } 2265 else 2266 # endif 2267 { 2268 gui_mch_draw_string(gui.row, col, s, len, draw_flags); 2269 # ifdef FEAT_MBYTE 2270 if (enc_dbcs == DBCS_JPNU) 2271 { 2272 int clen = 0; 2273 int i; 2274 2275 /* Get the length in display cells, this can be different from the 2276 * number of bytes for "euc-jp". */ 2277 for (i = 0; i < len; i += (*mb_ptr2len)(s + i)) 2278 clen += (*mb_ptr2cells)(s + i); 2279 len = clen; 2280 } 2281 # endif 2282 } 2283 #endif /* !HAVE_GTK2 */ 2284 2285 if (!(flags & (GUI_MON_IS_CURSOR | GUI_MON_TRS_CURSOR))) 2286 gui.col = col + len; 2287 2288 /* May need to invert it when it's part of the selection. */ 2289 if (flags & GUI_MON_NOCLEAR) 2290 clip_may_redraw_selection(gui.row, col, len); 2291 2292 if (!(flags & (GUI_MON_IS_CURSOR | GUI_MON_TRS_CURSOR))) 2293 { 2294 /* Invalidate the old physical cursor position if we wrote over it */ 2295 if (gui.cursor_row == gui.row 2296 && gui.cursor_col >= col 2297 && gui.cursor_col < col + len) 2298 gui.cursor_is_valid = FALSE; 2299 } 2300 2301 #ifdef FEAT_SIGN_ICONS 2302 if (draw_sign) 2303 /* Draw the sign on top of the spaces. */ 2304 gui_mch_drawsign(gui.row, col, gui.highlight_mask); 2305 # ifdef FEAT_NETBEANS_INTG 2306 if (multi_sign) 2307 netbeans_draw_multisign_indicator(gui.row); 2308 # endif 2309 #endif 2310 2311 return OK; 2312 } 2313 2314 /* 2315 * Un-draw the cursor. Actually this just redraws the character at the given 2316 * position. The character just before it too, for when it was in bold. 2317 */ 2318 void 2319 gui_undraw_cursor() 2320 { 2321 if (gui.cursor_is_valid) 2322 { 2323 #ifdef FEAT_HANGULIN 2324 if (composing_hangul 2325 && gui.col == gui.cursor_col && gui.row == gui.cursor_row) 2326 (void)gui_outstr_nowrap(composing_hangul_buffer, 2, 2327 GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, 2328 gui.norm_pixel, gui.back_pixel, 0); 2329 else 2330 { 2331 #endif 2332 if (gui_redraw_block(gui.cursor_row, gui.cursor_col, 2333 gui.cursor_row, gui.cursor_col, GUI_MON_NOCLEAR) 2334 && gui.cursor_col > 0) 2335 (void)gui_redraw_block(gui.cursor_row, gui.cursor_col - 1, 2336 gui.cursor_row, gui.cursor_col - 1, GUI_MON_NOCLEAR); 2337 #ifdef FEAT_HANGULIN 2338 if (composing_hangul) 2339 (void)gui_redraw_block(gui.cursor_row, gui.cursor_col + 1, 2340 gui.cursor_row, gui.cursor_col + 1, GUI_MON_NOCLEAR); 2341 } 2342 #endif 2343 /* Cursor_is_valid is reset when the cursor is undrawn, also reset it 2344 * here in case it wasn't needed to undraw it. */ 2345 gui.cursor_is_valid = FALSE; 2346 } 2347 } 2348 2349 void 2350 gui_redraw(x, y, w, h) 2351 int x; 2352 int y; 2353 int w; 2354 int h; 2355 { 2356 int row1, col1, row2, col2; 2357 2358 row1 = Y_2_ROW(y); 2359 col1 = X_2_COL(x); 2360 row2 = Y_2_ROW(y + h - 1); 2361 col2 = X_2_COL(x + w - 1); 2362 2363 (void)gui_redraw_block(row1, col1, row2, col2, GUI_MON_NOCLEAR); 2364 2365 /* 2366 * We may need to redraw the cursor, but don't take it upon us to change 2367 * its location after a scroll. 2368 * (maybe be more strict even and test col too?) 2369 * These things may be outside the update/clipping region and reality may 2370 * not reflect Vims internal ideas if these operations are clipped away. 2371 */ 2372 if (gui.row == gui.cursor_row) 2373 gui_update_cursor(TRUE, TRUE); 2374 } 2375 2376 /* 2377 * Draw a rectangular block of characters, from row1 to row2 (inclusive) and 2378 * from col1 to col2 (inclusive). 2379 * Return TRUE when the character before the first drawn character has 2380 * different attributes (may have to be redrawn too). 2381 */ 2382 int 2383 gui_redraw_block(row1, col1, row2, col2, flags) 2384 int row1; 2385 int col1; 2386 int row2; 2387 int col2; 2388 int flags; /* flags for gui_outstr_nowrap() */ 2389 { 2390 int old_row, old_col; 2391 long_u old_hl_mask; 2392 int off; 2393 sattr_T first_attr; 2394 int idx, len; 2395 int back, nback; 2396 int retval = FALSE; 2397 #ifdef FEAT_MBYTE 2398 int orig_col1, orig_col2; 2399 #endif 2400 2401 /* Don't try to update when ScreenLines is not valid */ 2402 if (!screen_cleared || ScreenLines == NULL) 2403 return retval; 2404 2405 /* Don't try to draw outside the shell! */ 2406 /* Check everything, strange values may be caused by a big border width */ 2407 col1 = check_col(col1); 2408 col2 = check_col(col2); 2409 row1 = check_row(row1); 2410 row2 = check_row(row2); 2411 2412 /* Remember where our cursor was */ 2413 old_row = gui.row; 2414 old_col = gui.col; 2415 old_hl_mask = gui.highlight_mask; 2416 #ifdef FEAT_MBYTE 2417 orig_col1 = col1; 2418 orig_col2 = col2; 2419 #endif 2420 2421 for (gui.row = row1; gui.row <= row2; gui.row++) 2422 { 2423 #ifdef FEAT_MBYTE 2424 /* When only half of a double-wide character is in the block, include 2425 * the other half. */ 2426 col1 = orig_col1; 2427 col2 = orig_col2; 2428 off = LineOffset[gui.row]; 2429 if (enc_dbcs != 0) 2430 { 2431 if (col1 > 0) 2432 col1 -= dbcs_screen_head_off(ScreenLines + off, 2433 ScreenLines + off + col1); 2434 col2 += dbcs_screen_tail_off(ScreenLines + off, 2435 ScreenLines + off + col2); 2436 } 2437 else if (enc_utf8) 2438 { 2439 if (ScreenLines[off + col1] == 0) 2440 --col1; 2441 # ifdef HAVE_GTK2 2442 if (col2 + 1 < Columns && ScreenLines[off + col2 + 1] == 0) 2443 ++col2; 2444 # endif 2445 } 2446 #endif 2447 gui.col = col1; 2448 off = LineOffset[gui.row] + gui.col; 2449 len = col2 - col1 + 1; 2450 2451 /* Find how many chars back this highlighting starts, or where a space 2452 * is. Needed for when the bold trick is used */ 2453 for (back = 0; back < col1; ++back) 2454 if (ScreenAttrs[off - 1 - back] != ScreenAttrs[off] 2455 || ScreenLines[off - 1 - back] == ' ') 2456 break; 2457 retval = (col1 > 0 && ScreenAttrs[off - 1] != 0 && back == 0 2458 && ScreenLines[off - 1] != ' '); 2459 2460 /* Break it up in strings of characters with the same attributes. */ 2461 /* Print UTF-8 characters individually. */ 2462 while (len > 0) 2463 { 2464 first_attr = ScreenAttrs[off]; 2465 gui.highlight_mask = first_attr; 2466 #if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) 2467 if (enc_utf8 && ScreenLinesUC[off] != 0) 2468 { 2469 /* output multi-byte character separately */ 2470 nback = gui_screenchar(off, flags, 2471 (guicolor_T)0, (guicolor_T)0, back); 2472 if (gui.col < Columns && ScreenLines[off + 1] == 0) 2473 idx = 2; 2474 else 2475 idx = 1; 2476 } 2477 else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 2478 { 2479 /* output double-byte, single-width character separately */ 2480 nback = gui_screenchar(off, flags, 2481 (guicolor_T)0, (guicolor_T)0, back); 2482 idx = 1; 2483 } 2484 else 2485 #endif 2486 { 2487 #ifdef HAVE_GTK2 2488 for (idx = 0; idx < len; ++idx) 2489 { 2490 if (enc_utf8 && ScreenLines[off + idx] == 0) 2491 continue; /* skip second half of double-width char */ 2492 if (ScreenAttrs[off + idx] != first_attr) 2493 break; 2494 } 2495 /* gui_screenstr() takes care of multibyte chars */ 2496 nback = gui_screenstr(off, idx, flags, 2497 (guicolor_T)0, (guicolor_T)0, back); 2498 #else 2499 for (idx = 0; idx < len && ScreenAttrs[off + idx] == first_attr; 2500 idx++) 2501 { 2502 # ifdef FEAT_MBYTE 2503 /* Stop at a multi-byte Unicode character. */ 2504 if (enc_utf8 && ScreenLinesUC[off + idx] != 0) 2505 break; 2506 if (enc_dbcs == DBCS_JPNU) 2507 { 2508 /* Stop at a double-byte single-width char. */ 2509 if (ScreenLines[off + idx] == 0x8e) 2510 break; 2511 if (len > 1 && (*mb_ptr2len)(ScreenLines 2512 + off + idx) == 2) 2513 ++idx; /* skip second byte of double-byte char */ 2514 } 2515 # endif 2516 } 2517 nback = gui_outstr_nowrap(ScreenLines + off, idx, flags, 2518 (guicolor_T)0, (guicolor_T)0, back); 2519 #endif 2520 } 2521 if (nback == FAIL) 2522 { 2523 /* Must back up to start drawing where a bold or italic word 2524 * starts. */ 2525 off -= back; 2526 len += back; 2527 gui.col -= back; 2528 } 2529 else 2530 { 2531 off += idx; 2532 len -= idx; 2533 } 2534 back = 0; 2535 } 2536 } 2537 2538 /* Put the cursor back where it was */ 2539 gui.row = old_row; 2540 gui.col = old_col; 2541 gui.highlight_mask = old_hl_mask; 2542 2543 return retval; 2544 } 2545 2546 static void 2547 gui_delete_lines(row, count) 2548 int row; 2549 int count; 2550 { 2551 if (count <= 0) 2552 return; 2553 2554 if (row + count > gui.scroll_region_bot) 2555 /* Scrolled out of region, just blank the lines out */ 2556 gui_clear_block(row, gui.scroll_region_left, 2557 gui.scroll_region_bot, gui.scroll_region_right); 2558 else 2559 { 2560 gui_mch_delete_lines(row, count); 2561 2562 /* If the cursor was in the deleted lines it's now gone. If the 2563 * cursor was in the scrolled lines adjust its position. */ 2564 if (gui.cursor_row >= row 2565 && gui.cursor_col >= gui.scroll_region_left 2566 && gui.cursor_col <= gui.scroll_region_right) 2567 { 2568 if (gui.cursor_row < row + count) 2569 gui.cursor_is_valid = FALSE; 2570 else if (gui.cursor_row <= gui.scroll_region_bot) 2571 gui.cursor_row -= count; 2572 } 2573 } 2574 } 2575 2576 static void 2577 gui_insert_lines(row, count) 2578 int row; 2579 int count; 2580 { 2581 if (count <= 0) 2582 return; 2583 2584 if (row + count > gui.scroll_region_bot) 2585 /* Scrolled out of region, just blank the lines out */ 2586 gui_clear_block(row, gui.scroll_region_left, 2587 gui.scroll_region_bot, gui.scroll_region_right); 2588 else 2589 { 2590 gui_mch_insert_lines(row, count); 2591 2592 if (gui.cursor_row >= gui.row 2593 && gui.cursor_col >= gui.scroll_region_left 2594 && gui.cursor_col <= gui.scroll_region_right) 2595 { 2596 if (gui.cursor_row <= gui.scroll_region_bot - count) 2597 gui.cursor_row += count; 2598 else if (gui.cursor_row <= gui.scroll_region_bot) 2599 gui.cursor_is_valid = FALSE; 2600 } 2601 } 2602 } 2603 2604 /* 2605 * The main GUI input routine. Waits for a character from the keyboard. 2606 * wtime == -1 Wait forever. 2607 * wtime == 0 Don't wait. 2608 * wtime > 0 Wait wtime milliseconds for a character. 2609 * Returns OK if a character was found to be available within the given time, 2610 * or FAIL otherwise. 2611 */ 2612 int 2613 gui_wait_for_chars(wtime) 2614 long wtime; 2615 { 2616 int retval; 2617 2618 /* 2619 * If we're going to wait a bit, update the menus and mouse shape for the 2620 * current State. 2621 */ 2622 if (wtime != 0) 2623 { 2624 #ifdef FEAT_MENU 2625 gui_update_menus(0); 2626 #endif 2627 } 2628 2629 gui_mch_update(); 2630 if (input_available()) /* Got char, return immediately */ 2631 return OK; 2632 if (wtime == 0) /* Don't wait for char */ 2633 return FAIL; 2634 2635 /* Before waiting, flush any output to the screen. */ 2636 gui_mch_flush(); 2637 2638 if (wtime > 0) 2639 { 2640 /* Blink when waiting for a character. Probably only does something 2641 * for showmatch() */ 2642 gui_mch_start_blink(); 2643 retval = gui_mch_wait_for_chars(wtime); 2644 gui_mch_stop_blink(); 2645 return retval; 2646 } 2647 2648 /* 2649 * While we are waiting indefenitely for a character, blink the cursor. 2650 */ 2651 gui_mch_start_blink(); 2652 2653 retval = FAIL; 2654 /* 2655 * We may want to trigger the CursorHold event. First wait for 2656 * 'updatetime' and if nothing is typed within that time put the 2657 * K_CURSORHOLD key in the input buffer. 2658 */ 2659 if (gui_mch_wait_for_chars(p_ut) == OK) 2660 retval = OK; 2661 #ifdef FEAT_AUTOCMD 2662 else if (!did_cursorhold && has_cursorhold() 2663 && get_real_state() == NORMAL_BUSY) 2664 { 2665 char_u buf[3]; 2666 2667 /* Put K_CURSORHOLD in the input buffer. */ 2668 buf[0] = CSI; 2669 buf[1] = KS_EXTRA; 2670 buf[2] = (int)KE_CURSORHOLD; 2671 add_to_input_buf(buf, 3); 2672 2673 retval = OK; 2674 } 2675 #endif 2676 2677 if (retval == FAIL) 2678 { 2679 /* Blocking wait. */ 2680 before_blocking(); 2681 retval = gui_mch_wait_for_chars(-1L); 2682 } 2683 2684 gui_mch_stop_blink(); 2685 return retval; 2686 } 2687 2688 /* 2689 * Fill buffer with mouse coordinates encoded for check_termcode(). 2690 */ 2691 static void 2692 fill_mouse_coord(p, col, row) 2693 char_u *p; 2694 int col; 2695 int row; 2696 { 2697 p[0] = (char_u)(col / 128 + ' ' + 1); 2698 p[1] = (char_u)(col % 128 + ' ' + 1); 2699 p[2] = (char_u)(row / 128 + ' ' + 1); 2700 p[3] = (char_u)(row % 128 + ' ' + 1); 2701 } 2702 2703 /* 2704 * Generic mouse support function. Add a mouse event to the input buffer with 2705 * the given properties. 2706 * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, 2707 * MOUSE_X1, MOUSE_X2 2708 * MOUSE_DRAG, or MOUSE_RELEASE. 2709 * MOUSE_4 and MOUSE_5 are used for a scroll wheel. 2710 * x, y --- Coordinates of mouse in pixels. 2711 * repeated_click --- TRUE if this click comes only a short time after a 2712 * previous click. 2713 * modifiers --- Bit field which may be any of the following modifiers 2714 * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT. 2715 * This function will ignore drag events where the mouse has not moved to a new 2716 * character. 2717 */ 2718 void 2719 gui_send_mouse_event(button, x, y, repeated_click, modifiers) 2720 int button; 2721 int x; 2722 int y; 2723 int repeated_click; 2724 int_u modifiers; 2725 { 2726 static int prev_row = 0, prev_col = 0; 2727 static int prev_button = -1; 2728 static int num_clicks = 1; 2729 char_u string[10]; 2730 enum key_extra button_char; 2731 int row, col; 2732 #ifdef FEAT_CLIPBOARD 2733 int checkfor; 2734 int did_clip = FALSE; 2735 #endif 2736 2737 /* 2738 * Scrolling may happen at any time, also while a selection is present. 2739 */ 2740 switch (button) 2741 { 2742 case MOUSE_X1: 2743 button_char = KE_X1MOUSE; 2744 goto button_set; 2745 case MOUSE_X2: 2746 button_char = KE_X2MOUSE; 2747 goto button_set; 2748 case MOUSE_4: 2749 button_char = KE_MOUSEDOWN; 2750 goto button_set; 2751 case MOUSE_5: 2752 button_char = KE_MOUSEUP; 2753 button_set: 2754 { 2755 /* Don't put events in the input queue now. */ 2756 if (hold_gui_events) 2757 return; 2758 2759 string[3] = CSI; 2760 string[4] = KS_EXTRA; 2761 string[5] = (int)button_char; 2762 2763 /* Pass the pointer coordinates of the scroll event so that we 2764 * know which window to scroll. */ 2765 row = gui_xy2colrow(x, y, &col); 2766 string[6] = (char_u)(col / 128 + ' ' + 1); 2767 string[7] = (char_u)(col % 128 + ' ' + 1); 2768 string[8] = (char_u)(row / 128 + ' ' + 1); 2769 string[9] = (char_u)(row % 128 + ' ' + 1); 2770 2771 if (modifiers == 0) 2772 add_to_input_buf(string + 3, 7); 2773 else 2774 { 2775 string[0] = CSI; 2776 string[1] = KS_MODIFIER; 2777 string[2] = 0; 2778 if (modifiers & MOUSE_SHIFT) 2779 string[2] |= MOD_MASK_SHIFT; 2780 if (modifiers & MOUSE_CTRL) 2781 string[2] |= MOD_MASK_CTRL; 2782 if (modifiers & MOUSE_ALT) 2783 string[2] |= MOD_MASK_ALT; 2784 add_to_input_buf(string, 10); 2785 } 2786 return; 2787 } 2788 } 2789 2790 #ifdef FEAT_CLIPBOARD 2791 /* If a clipboard selection is in progress, handle it */ 2792 if (clip_star.state == SELECT_IN_PROGRESS) 2793 { 2794 clip_process_selection(button, X_2_COL(x), Y_2_ROW(y), repeated_click); 2795 return; 2796 } 2797 2798 /* Determine which mouse settings to look for based on the current mode */ 2799 switch (get_real_state()) 2800 { 2801 case NORMAL_BUSY: 2802 case OP_PENDING: 2803 case NORMAL: checkfor = MOUSE_NORMAL; break; 2804 case VISUAL: checkfor = MOUSE_VISUAL; break; 2805 case REPLACE: 2806 case REPLACE+LANGMAP: 2807 #ifdef FEAT_VREPLACE 2808 case VREPLACE: 2809 case VREPLACE+LANGMAP: 2810 #endif 2811 case INSERT: 2812 case INSERT+LANGMAP: checkfor = MOUSE_INSERT; break; 2813 case ASKMORE: 2814 case HITRETURN: /* At the more- and hit-enter prompt pass the 2815 mouse event for a click on or below the 2816 message line. */ 2817 if (Y_2_ROW(y) >= msg_row) 2818 checkfor = MOUSE_NORMAL; 2819 else 2820 checkfor = MOUSE_RETURN; 2821 break; 2822 2823 /* 2824 * On the command line, use the clipboard selection on all lines 2825 * but the command line. But not when pasting. 2826 */ 2827 case CMDLINE: 2828 case CMDLINE+LANGMAP: 2829 if (Y_2_ROW(y) < cmdline_row && button != MOUSE_MIDDLE) 2830 checkfor = MOUSE_NONE; 2831 else 2832 checkfor = MOUSE_COMMAND; 2833 break; 2834 2835 default: 2836 checkfor = MOUSE_NONE; 2837 break; 2838 }; 2839 2840 /* 2841 * Allow clipboard selection of text on the command line in "normal" 2842 * modes. Don't do this when dragging the status line, or extending a 2843 * Visual selection. 2844 */ 2845 if ((State == NORMAL || State == NORMAL_BUSY || (State & INSERT)) 2846 && Y_2_ROW(y) >= topframe->fr_height 2847 && button != MOUSE_DRAG 2848 # ifdef FEAT_MOUSESHAPE 2849 && !drag_status_line 2850 # ifdef FEAT_VERTSPLIT 2851 && !drag_sep_line 2852 # endif 2853 # endif 2854 ) 2855 checkfor = MOUSE_NONE; 2856 2857 /* 2858 * Use modeless selection when holding CTRL and SHIFT pressed. 2859 */ 2860 if ((modifiers & MOUSE_CTRL) && (modifiers & MOUSE_SHIFT)) 2861 checkfor = MOUSE_NONEF; 2862 2863 /* 2864 * In Ex mode, always use modeless selection. 2865 */ 2866 if (exmode_active) 2867 checkfor = MOUSE_NONE; 2868 2869 /* 2870 * If the mouse settings say to not use the mouse, use the modeless 2871 * selection. But if Visual is active, assume that only the Visual area 2872 * will be selected. 2873 * Exception: On the command line, both the selection is used and a mouse 2874 * key is send. 2875 */ 2876 if (!mouse_has(checkfor) || checkfor == MOUSE_COMMAND) 2877 { 2878 #ifdef FEAT_VISUAL 2879 /* Don't do modeless selection in Visual mode. */ 2880 if (checkfor != MOUSE_NONEF && VIsual_active && (State & NORMAL)) 2881 return; 2882 #endif 2883 2884 /* 2885 * When 'mousemodel' is "popup", shift-left is translated to right. 2886 * But not when also using Ctrl. 2887 */ 2888 if (mouse_model_popup() && button == MOUSE_LEFT 2889 && (modifiers & MOUSE_SHIFT) && !(modifiers & MOUSE_CTRL)) 2890 { 2891 button = MOUSE_RIGHT; 2892 modifiers &= ~ MOUSE_SHIFT; 2893 } 2894 2895 /* If the selection is done, allow the right button to extend it. 2896 * If the selection is cleared, allow the right button to start it 2897 * from the cursor position. */ 2898 if (button == MOUSE_RIGHT) 2899 { 2900 if (clip_star.state == SELECT_CLEARED) 2901 { 2902 if (State & CMDLINE) 2903 { 2904 col = msg_col; 2905 row = msg_row; 2906 } 2907 else 2908 { 2909 col = curwin->w_wcol; 2910 row = curwin->w_wrow + W_WINROW(curwin); 2911 } 2912 clip_start_selection(col, row, FALSE); 2913 } 2914 clip_process_selection(button, X_2_COL(x), Y_2_ROW(y), 2915 repeated_click); 2916 did_clip = TRUE; 2917 } 2918 /* Allow the left button to start the selection */ 2919 else if (button == 2920 # ifdef RISCOS 2921 /* Only start a drag on a drag event. Otherwise 2922 * we don't get a release event. */ 2923 MOUSE_DRAG 2924 # else 2925 MOUSE_LEFT 2926 # endif 2927 ) 2928 { 2929 clip_start_selection(X_2_COL(x), Y_2_ROW(y), repeated_click); 2930 did_clip = TRUE; 2931 } 2932 # ifdef RISCOS 2933 else if (button == MOUSE_LEFT) 2934 { 2935 clip_clear_selection(); 2936 did_clip = TRUE; 2937 } 2938 # endif 2939 2940 /* Always allow pasting */ 2941 if (button != MOUSE_MIDDLE) 2942 { 2943 if (!mouse_has(checkfor) || button == MOUSE_RELEASE) 2944 return; 2945 if (checkfor != MOUSE_COMMAND) 2946 button = MOUSE_LEFT; 2947 } 2948 repeated_click = FALSE; 2949 } 2950 2951 if (clip_star.state != SELECT_CLEARED && !did_clip) 2952 clip_clear_selection(); 2953 #endif 2954 2955 /* Don't put events in the input queue now. */ 2956 if (hold_gui_events) 2957 return; 2958 2959 row = gui_xy2colrow(x, y, &col); 2960 2961 /* 2962 * If we are dragging and the mouse hasn't moved far enough to be on a 2963 * different character, then don't send an event to vim. 2964 */ 2965 if (button == MOUSE_DRAG) 2966 { 2967 if (row == prev_row && col == prev_col) 2968 return; 2969 /* Dragging above the window, set "row" to -1 to cause a scroll. */ 2970 if (y < 0) 2971 row = -1; 2972 } 2973 2974 /* 2975 * If topline has changed (window scrolled) since the last click, reset 2976 * repeated_click, because we don't want starting Visual mode when 2977 * clicking on a different character in the text. 2978 */ 2979 if (curwin->w_topline != gui_prev_topline 2980 #ifdef FEAT_DIFF 2981 || curwin->w_topfill != gui_prev_topfill 2982 #endif 2983 ) 2984 repeated_click = FALSE; 2985 2986 string[0] = CSI; /* this sequence is recognized by check_termcode() */ 2987 string[1] = KS_MOUSE; 2988 string[2] = KE_FILLER; 2989 if (button != MOUSE_DRAG && button != MOUSE_RELEASE) 2990 { 2991 if (repeated_click) 2992 { 2993 /* 2994 * Handle multiple clicks. They only count if the mouse is still 2995 * pointing at the same character. 2996 */ 2997 if (button != prev_button || row != prev_row || col != prev_col) 2998 num_clicks = 1; 2999 else if (++num_clicks > 4) 3000 num_clicks = 1; 3001 } 3002 else 3003 num_clicks = 1; 3004 prev_button = button; 3005 gui_prev_topline = curwin->w_topline; 3006 #ifdef FEAT_DIFF 3007 gui_prev_topfill = curwin->w_topfill; 3008 #endif 3009 3010 string[3] = (char_u)(button | 0x20); 3011 SET_NUM_MOUSE_CLICKS(string[3], num_clicks); 3012 } 3013 else 3014 string[3] = (char_u)button; 3015 3016 string[3] |= modifiers; 3017 fill_mouse_coord(string + 4, col, row); 3018 add_to_input_buf(string, 8); 3019 3020 if (row < 0) 3021 prev_row = 0; 3022 else 3023 prev_row = row; 3024 prev_col = col; 3025 3026 /* 3027 * We need to make sure this is cleared since Athena doesn't tell us when 3028 * he is done dragging. Neither does GTK+ 2 -- at least for now. 3029 */ 3030 #if defined(FEAT_GUI_ATHENA) || defined(HAVE_GTK2) 3031 gui.dragged_sb = SBAR_NONE; 3032 #endif 3033 } 3034 3035 /* 3036 * Convert x and y coordinate to column and row in text window. 3037 * Corrects for multi-byte character. 3038 * returns column in "*colp" and row as return value; 3039 */ 3040 int 3041 gui_xy2colrow(x, y, colp) 3042 int x; 3043 int y; 3044 int *colp; 3045 { 3046 int col = check_col(X_2_COL(x)); 3047 int row = check_row(Y_2_ROW(y)); 3048 3049 #ifdef FEAT_MBYTE 3050 *colp = mb_fix_col(col, row); 3051 #else 3052 *colp = col; 3053 #endif 3054 return row; 3055 } 3056 3057 #if defined(FEAT_MENU) || defined(PROTO) 3058 /* 3059 * Callback function for when a menu entry has been selected. 3060 */ 3061 void 3062 gui_menu_cb(menu) 3063 vimmenu_T *menu; 3064 { 3065 char_u bytes[3 + sizeof(long_u)]; 3066 3067 /* Don't put events in the input queue now. */ 3068 if (hold_gui_events) 3069 return; 3070 3071 bytes[0] = CSI; 3072 bytes[1] = KS_MENU; 3073 bytes[2] = KE_FILLER; 3074 add_long_to_buf((long_u)menu, bytes + 3); 3075 add_to_input_buf(bytes, 3 + sizeof(long_u)); 3076 } 3077 #endif 3078 3079 /* 3080 * Set which components are present. 3081 * If "oldval" is not NULL, "oldval" is the previous value, the new * value is 3082 * in p_go. 3083 */ 3084 /*ARGSUSED*/ 3085 void 3086 gui_init_which_components(oldval) 3087 char_u *oldval; 3088 { 3089 static int prev_which_scrollbars[3] = {-1, -1, -1}; 3090 #ifdef FEAT_MENU 3091 static int prev_menu_is_active = -1; 3092 #endif 3093 #ifdef FEAT_TOOLBAR 3094 static int prev_toolbar = -1; 3095 int using_toolbar = FALSE; 3096 #endif 3097 #ifdef FEAT_FOOTER 3098 static int prev_footer = -1; 3099 int using_footer = FALSE; 3100 #endif 3101 #if defined(FEAT_MENU) && !defined(WIN16) 3102 static int prev_tearoff = -1; 3103 int using_tearoff = FALSE; 3104 #endif 3105 3106 char_u *p; 3107 int i; 3108 #ifdef FEAT_MENU 3109 int grey_old, grey_new; 3110 char_u *temp; 3111 #endif 3112 win_T *wp; 3113 int need_set_size; 3114 int fix_size; 3115 3116 #ifdef FEAT_MENU 3117 if (oldval != NULL && gui.in_use) 3118 { 3119 /* 3120 * Check if the menu's go from grey to non-grey or vise versa. 3121 */ 3122 grey_old = (vim_strchr(oldval, GO_GREY) != NULL); 3123 grey_new = (vim_strchr(p_go, GO_GREY) != NULL); 3124 if (grey_old != grey_new) 3125 { 3126 temp = p_go; 3127 p_go = oldval; 3128 gui_update_menus(MENU_ALL_MODES); 3129 p_go = temp; 3130 } 3131 } 3132 gui.menu_is_active = FALSE; 3133 #endif 3134 3135 for (i = 0; i < 3; i++) 3136 gui.which_scrollbars[i] = FALSE; 3137 for (p = p_go; *p; p++) 3138 switch (*p) 3139 { 3140 case GO_LEFT: 3141 gui.which_scrollbars[SBAR_LEFT] = TRUE; 3142 break; 3143 case GO_RIGHT: 3144 gui.which_scrollbars[SBAR_RIGHT] = TRUE; 3145 break; 3146 #ifdef FEAT_VERTSPLIT 3147 case GO_VLEFT: 3148 if (win_hasvertsplit()) 3149 gui.which_scrollbars[SBAR_LEFT] = TRUE; 3150 break; 3151 case GO_VRIGHT: 3152 if (win_hasvertsplit()) 3153 gui.which_scrollbars[SBAR_RIGHT] = TRUE; 3154 break; 3155 #endif 3156 case GO_BOT: 3157 gui.which_scrollbars[SBAR_BOTTOM] = TRUE; 3158 break; 3159 #ifdef FEAT_MENU 3160 case GO_MENUS: 3161 gui.menu_is_active = TRUE; 3162 break; 3163 #endif 3164 case GO_GREY: 3165 /* make menu's have grey items, ignored here */ 3166 break; 3167 #ifdef FEAT_TOOLBAR 3168 case GO_TOOLBAR: 3169 using_toolbar = TRUE; 3170 break; 3171 #endif 3172 #ifdef FEAT_FOOTER 3173 case GO_FOOTER: 3174 using_footer = TRUE; 3175 break; 3176 #endif 3177 case GO_TEAROFF: 3178 #if defined(FEAT_MENU) && !defined(WIN16) 3179 using_tearoff = TRUE; 3180 #endif 3181 break; 3182 default: 3183 /* Ignore options that are not supported */ 3184 break; 3185 } 3186 if (gui.in_use) 3187 { 3188 need_set_size = FALSE; 3189 fix_size = FALSE; 3190 for (i = 0; i < 3; i++) 3191 { 3192 if (gui.which_scrollbars[i] != prev_which_scrollbars[i]) 3193 { 3194 if (i == SBAR_BOTTOM) 3195 gui_mch_enable_scrollbar(&gui.bottom_sbar, 3196 gui.which_scrollbars[i]); 3197 else 3198 { 3199 FOR_ALL_WINDOWS(wp) 3200 { 3201 gui_do_scrollbar(wp, i, gui.which_scrollbars[i]); 3202 } 3203 } 3204 need_set_size = TRUE; 3205 if (gui.which_scrollbars[i]) 3206 fix_size = TRUE; 3207 } 3208 prev_which_scrollbars[i] = gui.which_scrollbars[i]; 3209 } 3210 3211 #ifdef FEAT_MENU 3212 if (gui.menu_is_active != prev_menu_is_active) 3213 { 3214 /* We don't want a resize event change "Rows" here, save and 3215 * restore it. Resizing is handled below. */ 3216 i = Rows; 3217 gui_mch_enable_menu(gui.menu_is_active); 3218 Rows = i; 3219 prev_menu_is_active = gui.menu_is_active; 3220 need_set_size = TRUE; 3221 if (gui.menu_is_active) 3222 fix_size = TRUE; 3223 } 3224 #endif 3225 3226 #ifdef FEAT_TOOLBAR 3227 if (using_toolbar != prev_toolbar) 3228 { 3229 gui_mch_show_toolbar(using_toolbar); 3230 prev_toolbar = using_toolbar; 3231 need_set_size = TRUE; 3232 if (using_toolbar) 3233 fix_size = TRUE; 3234 } 3235 #endif 3236 #ifdef FEAT_FOOTER 3237 if (using_footer != prev_footer) 3238 { 3239 gui_mch_enable_footer(using_footer); 3240 prev_footer = using_footer; 3241 need_set_size = TRUE; 3242 if (using_footer) 3243 fix_size = TRUE; 3244 } 3245 #endif 3246 #if defined(FEAT_MENU) && !defined(WIN16) && !(defined(WIN3264) && !defined(FEAT_TEAROFF)) 3247 if (using_tearoff != prev_tearoff) 3248 { 3249 gui_mch_toggle_tearoffs(using_tearoff); 3250 prev_tearoff = using_tearoff; 3251 } 3252 #endif 3253 if (need_set_size) 3254 { 3255 #ifdef FEAT_GUI_GTK 3256 long r = Rows; 3257 long c = Columns; 3258 #endif 3259 /* Adjust the size of the window to make the text area keep the 3260 * same size and to avoid that part of our window is off-screen 3261 * and a scrollbar can't be used, for example. */ 3262 gui_set_shellsize(FALSE, fix_size); 3263 3264 #ifdef FEAT_GUI_GTK 3265 /* GTK has the annoying habit of sending us resize events when 3266 * changing the window size ourselves. This mostly happens when 3267 * waiting for a character to arrive, quite unpredictably, and may 3268 * change Columns and Rows when we don't want it. Wait for a 3269 * character here to avoid this effect. 3270 * If you remove this, please test this command for resizing 3271 * effects (with optional left scrollbar): ":vsp|q|vsp|q|vsp|q". 3272 * Don't do this while starting up though. */ 3273 if (!gui.starting) 3274 (void)char_avail(); 3275 Rows = r; 3276 Columns = c; 3277 #endif 3278 } 3279 } 3280 } 3281 3282 3283 /* 3284 * Scrollbar stuff: 3285 */ 3286 3287 void 3288 gui_create_scrollbar(sb, type, wp) 3289 scrollbar_T *sb; 3290 int type; 3291 win_T *wp; 3292 { 3293 static int sbar_ident = 0; 3294 3295 sb->ident = sbar_ident++; /* No check for too big, but would it happen? */ 3296 sb->wp = wp; 3297 sb->type = type; 3298 sb->value = 0; 3299 #ifdef FEAT_GUI_ATHENA 3300 sb->pixval = 0; 3301 #endif 3302 sb->size = 1; 3303 sb->max = 1; 3304 sb->top = 0; 3305 sb->height = 0; 3306 #ifdef FEAT_VERTSPLIT 3307 sb->width = 0; 3308 #endif 3309 sb->status_height = 0; 3310 gui_mch_create_scrollbar(sb, (wp == NULL) ? SBAR_HORIZ : SBAR_VERT); 3311 } 3312 3313 /* 3314 * Find the scrollbar with the given index. 3315 */ 3316 scrollbar_T * 3317 gui_find_scrollbar(ident) 3318 long ident; 3319 { 3320 win_T *wp; 3321 3322 if (gui.bottom_sbar.ident == ident) 3323 return &gui.bottom_sbar; 3324 FOR_ALL_WINDOWS(wp) 3325 { 3326 if (wp->w_scrollbars[SBAR_LEFT].ident == ident) 3327 return &wp->w_scrollbars[SBAR_LEFT]; 3328 if (wp->w_scrollbars[SBAR_RIGHT].ident == ident) 3329 return &wp->w_scrollbars[SBAR_RIGHT]; 3330 } 3331 return NULL; 3332 } 3333 3334 /* 3335 * For most systems: Put a code in the input buffer for a dragged scrollbar. 3336 * 3337 * For Win32, Macintosh and GTK+ 2: 3338 * Scrollbars seem to grab focus and vim doesn't read the input queue until 3339 * you stop dragging the scrollbar. We get here each time the scrollbar is 3340 * dragged another pixel, but as far as the rest of vim goes, it thinks 3341 * we're just hanging in the call to DispatchMessage() in 3342 * process_message(). The DispatchMessage() call that hangs was passed a 3343 * mouse button click event in the scrollbar window. -- webb. 3344 * 3345 * Solution: Do the scrolling right here. But only when allowed. 3346 * Ignore the scrollbars while executing an external command or when there 3347 * are still characters to be processed. 3348 */ 3349 void 3350 gui_drag_scrollbar(sb, value, still_dragging) 3351 scrollbar_T *sb; 3352 long value; 3353 int still_dragging; 3354 { 3355 #ifdef FEAT_WINDOWS 3356 win_T *wp; 3357 #endif 3358 int sb_num; 3359 #ifdef USE_ON_FLY_SCROLL 3360 colnr_T old_leftcol = curwin->w_leftcol; 3361 # ifdef FEAT_SCROLLBIND 3362 linenr_T old_topline = curwin->w_topline; 3363 # endif 3364 # ifdef FEAT_DIFF 3365 int old_topfill = curwin->w_topfill; 3366 # endif 3367 #else 3368 char_u bytes[4 + sizeof(long_u)]; 3369 int byte_count; 3370 #endif 3371 3372 if (sb == NULL) 3373 return; 3374 3375 /* Don't put events in the input queue now. */ 3376 if (hold_gui_events) 3377 return; 3378 3379 #ifdef FEAT_CMDWIN 3380 if (cmdwin_type != 0 && sb->wp != curwin) 3381 return; 3382 #endif 3383 3384 if (still_dragging) 3385 { 3386 if (sb->wp == NULL) 3387 gui.dragged_sb = SBAR_BOTTOM; 3388 else if (sb == &sb->wp->w_scrollbars[SBAR_LEFT]) 3389 gui.dragged_sb = SBAR_LEFT; 3390 else 3391 gui.dragged_sb = SBAR_RIGHT; 3392 gui.dragged_wp = sb->wp; 3393 } 3394 else 3395 { 3396 gui.dragged_sb = SBAR_NONE; 3397 #ifdef HAVE_GTK2 3398 /* Keep the "dragged_wp" value until after the scrolling, for when the 3399 * moust button is released. GTK2 doesn't send the button-up event. */ 3400 gui.dragged_wp = NULL; 3401 #endif 3402 } 3403 3404 /* Vertical sbar info is kept in the first sbar (the left one) */ 3405 if (sb->wp != NULL) 3406 sb = &sb->wp->w_scrollbars[0]; 3407 3408 /* 3409 * Check validity of value 3410 */ 3411 if (value < 0) 3412 value = 0; 3413 #ifdef SCROLL_PAST_END 3414 else if (value > sb->max) 3415 value = sb->max; 3416 #else 3417 if (value > sb->max - sb->size + 1) 3418 value = sb->max - sb->size + 1; 3419 #endif 3420 3421 sb->value = value; 3422 3423 #ifdef USE_ON_FLY_SCROLL 3424 /* When not allowed to do the scrolling right now, return. */ 3425 if (dont_scroll || input_available()) 3426 return; 3427 #endif 3428 3429 #ifdef FEAT_RIGHTLEFT 3430 if (sb->wp == NULL && curwin->w_p_rl) 3431 { 3432 value = sb->max + 1 - sb->size - value; 3433 if (value < 0) 3434 value = 0; 3435 } 3436 #endif 3437 3438 if (sb->wp != NULL) /* vertical scrollbar */ 3439 { 3440 sb_num = 0; 3441 #ifdef FEAT_WINDOWS 3442 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = wp->w_next) 3443 sb_num++; 3444 if (wp == NULL) 3445 return; 3446 #else 3447 if (sb->wp != curwin) 3448 return; 3449 #endif 3450 3451 #ifdef USE_ON_FLY_SCROLL 3452 current_scrollbar = sb_num; 3453 scrollbar_value = value; 3454 if (State & NORMAL) 3455 { 3456 gui_do_scroll(); 3457 setcursor(); 3458 } 3459 else if (State & INSERT) 3460 { 3461 ins_scroll(); 3462 setcursor(); 3463 } 3464 else if (State & CMDLINE) 3465 { 3466 if (msg_scrolled == 0) 3467 { 3468 gui_do_scroll(); 3469 redrawcmdline(); 3470 } 3471 } 3472 # ifdef FEAT_FOLDING 3473 /* Value may have been changed for closed fold. */ 3474 sb->value = sb->wp->w_topline - 1; 3475 # endif 3476 #else 3477 bytes[0] = CSI; 3478 bytes[1] = KS_VER_SCROLLBAR; 3479 bytes[2] = KE_FILLER; 3480 bytes[3] = (char_u)sb_num; 3481 byte_count = 4; 3482 #endif 3483 } 3484 else 3485 { 3486 #ifdef USE_ON_FLY_SCROLL 3487 scrollbar_value = value; 3488 3489 if (State & NORMAL) 3490 gui_do_horiz_scroll(); 3491 else if (State & INSERT) 3492 ins_horscroll(); 3493 else if (State & CMDLINE) 3494 { 3495 if (msg_scrolled == 0) 3496 { 3497 gui_do_horiz_scroll(); 3498 redrawcmdline(); 3499 } 3500 } 3501 if (old_leftcol != curwin->w_leftcol) 3502 { 3503 updateWindow(curwin); /* update window, status and cmdline */ 3504 setcursor(); 3505 } 3506 #else 3507 bytes[0] = CSI; 3508 bytes[1] = KS_HOR_SCROLLBAR; 3509 bytes[2] = KE_FILLER; 3510 byte_count = 3; 3511 #endif 3512 } 3513 3514 #ifdef USE_ON_FLY_SCROLL 3515 # ifdef FEAT_SCROLLBIND 3516 /* 3517 * synchronize other windows, as necessary according to 'scrollbind' 3518 */ 3519 if (curwin->w_p_scb 3520 && ((sb->wp == NULL && curwin->w_leftcol != old_leftcol) 3521 || (sb->wp == curwin && (curwin->w_topline != old_topline 3522 # ifdef FEAT_DIFF 3523 || curwin->w_topfill != old_topfill 3524 # endif 3525 )))) 3526 { 3527 do_check_scrollbind(TRUE); 3528 /* need to update the window right here */ 3529 for (wp = firstwin; wp != NULL; wp = wp->w_next) 3530 if (wp->w_redr_type > 0) 3531 updateWindow(wp); 3532 setcursor(); 3533 } 3534 # endif 3535 out_flush(); 3536 gui_update_cursor(FALSE, TRUE); 3537 #else 3538 add_long_to_buf((long)value, bytes + byte_count); 3539 add_to_input_buf(bytes, byte_count + sizeof(long_u)); 3540 #endif 3541 } 3542 3543 /* 3544 * Scrollbar stuff: 3545 */ 3546 3547 void 3548 gui_update_scrollbars(force) 3549 int force; /* Force all scrollbars to get updated */ 3550 { 3551 win_T *wp; 3552 scrollbar_T *sb; 3553 long val, size, max; /* need 32 bits here */ 3554 int which_sb; 3555 int h, y; 3556 #ifdef FEAT_VERTSPLIT 3557 static win_T *prev_curwin = NULL; 3558 #endif 3559 3560 /* Update the horizontal scrollbar */ 3561 gui_update_horiz_scrollbar(force); 3562 3563 #ifndef WIN3264 3564 /* Return straight away if there is neither a left nor right scrollbar. 3565 * On MS-Windows this is required anyway for scrollwheel messages. */ 3566 if (!gui.which_scrollbars[SBAR_LEFT] && !gui.which_scrollbars[SBAR_RIGHT]) 3567 return; 3568 #endif 3569 3570 /* 3571 * Don't want to update a scrollbar while we're dragging it. But if we 3572 * have both a left and right scrollbar, and we drag one of them, we still 3573 * need to update the other one. 3574 */ 3575 if ( (gui.dragged_sb == SBAR_LEFT 3576 || gui.dragged_sb == SBAR_RIGHT) 3577 && (!gui.which_scrollbars[SBAR_LEFT] 3578 || !gui.which_scrollbars[SBAR_RIGHT]) 3579 && !force) 3580 return; 3581 3582 if (!force && (gui.dragged_sb == SBAR_LEFT || gui.dragged_sb == SBAR_RIGHT)) 3583 { 3584 /* 3585 * If we have two scrollbars and one of them is being dragged, just 3586 * copy the scrollbar position from the dragged one to the other one. 3587 */ 3588 which_sb = SBAR_LEFT + SBAR_RIGHT - gui.dragged_sb; 3589 if (gui.dragged_wp != NULL) 3590 gui_mch_set_scrollbar_thumb( 3591 &gui.dragged_wp->w_scrollbars[which_sb], 3592 gui.dragged_wp->w_scrollbars[0].value, 3593 gui.dragged_wp->w_scrollbars[0].size, 3594 gui.dragged_wp->w_scrollbars[0].max); 3595 return; 3596 } 3597 3598 /* avoid that moving components around generates events */ 3599 ++hold_gui_events; 3600 3601 for (wp = firstwin; wp != NULL; wp = W_NEXT(wp)) 3602 { 3603 if (wp->w_buffer == NULL) /* just in case */ 3604 continue; 3605 #ifdef SCROLL_PAST_END 3606 max = wp->w_buffer->b_ml.ml_line_count - 1; 3607 #else 3608 max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 2; 3609 #endif 3610 if (max < 0) /* empty buffer */ 3611 max = 0; 3612 val = wp->w_topline - 1; 3613 size = wp->w_height; 3614 #ifdef SCROLL_PAST_END 3615 if (val > max) /* just in case */ 3616 val = max; 3617 #else 3618 if (size > max + 1) /* just in case */ 3619 size = max + 1; 3620 if (val > max - size + 1) 3621 val = max - size + 1; 3622 #endif 3623 if (val < 0) /* minimal value is 0 */ 3624 val = 0; 3625 3626 /* 3627 * Scrollbar at index 0 (the left one) contains all the information. 3628 * It would be the same info for left and right so we just store it for 3629 * one of them. 3630 */ 3631 sb = &wp->w_scrollbars[0]; 3632 3633 /* 3634 * Note: no check for valid w_botline. If it's not valid the 3635 * scrollbars will be updated later anyway. 3636 */ 3637 if (size < 1 || wp->w_botline - 2 > max) 3638 { 3639 /* 3640 * This can happen during changing files. Just don't update the 3641 * scrollbar for now. 3642 */ 3643 sb->height = 0; /* Force update next time */ 3644 if (gui.which_scrollbars[SBAR_LEFT]) 3645 gui_do_scrollbar(wp, SBAR_LEFT, FALSE); 3646 if (gui.which_scrollbars[SBAR_RIGHT]) 3647 gui_do_scrollbar(wp, SBAR_RIGHT, FALSE); 3648 continue; 3649 } 3650 if (force || sb->height != wp->w_height 3651 #ifdef FEAT_WINDOWS 3652 || sb->top != wp->w_winrow 3653 || sb->status_height != wp->w_status_height 3654 # ifdef FEAT_VERTSPLIT 3655 || sb->width != wp->w_width 3656 || prev_curwin != curwin 3657 # endif 3658 #endif 3659 ) 3660 { 3661 /* Height, width or position of scrollbar has changed. For 3662 * vertical split: curwin changed. */ 3663 sb->height = wp->w_height; 3664 #ifdef FEAT_WINDOWS 3665 sb->top = wp->w_winrow; 3666 sb->status_height = wp->w_status_height; 3667 # ifdef FEAT_VERTSPLIT 3668 sb->width = wp->w_width; 3669 # endif 3670 #endif 3671 3672 /* Calculate height and position in pixels */ 3673 h = (sb->height + sb->status_height) * gui.char_height; 3674 y = sb->top * gui.char_height + gui.border_offset; 3675 #if defined(FEAT_MENU) && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) && !defined(FEAT_GUI_PHOTON) 3676 if (gui.menu_is_active) 3677 y += gui.menu_height; 3678 #endif 3679 3680 #if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_ATHENA)) 3681 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 3682 # ifdef FEAT_GUI_ATHENA 3683 y += gui.toolbar_height; 3684 # else 3685 # ifdef FEAT_GUI_MSWIN 3686 y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; 3687 # endif 3688 # endif 3689 #endif 3690 3691 #ifdef FEAT_WINDOWS 3692 if (wp->w_winrow == 0) 3693 #endif 3694 { 3695 /* Height of top scrollbar includes width of top border */ 3696 h += gui.border_offset; 3697 y -= gui.border_offset; 3698 } 3699 if (gui.which_scrollbars[SBAR_LEFT]) 3700 { 3701 gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_LEFT], 3702 gui.left_sbar_x, y, 3703 gui.scrollbar_width, h); 3704 gui_do_scrollbar(wp, SBAR_LEFT, TRUE); 3705 } 3706 if (gui.which_scrollbars[SBAR_RIGHT]) 3707 { 3708 gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_RIGHT], 3709 gui.right_sbar_x, y, 3710 gui.scrollbar_width, h); 3711 gui_do_scrollbar(wp, SBAR_RIGHT, TRUE); 3712 } 3713 } 3714 3715 /* Reduce the number of calls to gui_mch_set_scrollbar_thumb() by 3716 * checking if the thumb moved at least a pixel. Only do this for 3717 * Athena, most other GUIs require the update anyway to make the 3718 * arrows work. */ 3719 #ifdef FEAT_GUI_ATHENA 3720 if (max == 0) 3721 y = 0; 3722 else 3723 y = (val * (sb->height + 2) * gui.char_height + max / 2) / max; 3724 if (force || sb->pixval != y || sb->size != size || sb->max != max) 3725 #else 3726 if (force || sb->value != val || sb->size != size || sb->max != max) 3727 #endif 3728 { 3729 /* Thumb of scrollbar has moved */ 3730 sb->value = val; 3731 #ifdef FEAT_GUI_ATHENA 3732 sb->pixval = y; 3733 #endif 3734 sb->size = size; 3735 sb->max = max; 3736 if (gui.which_scrollbars[SBAR_LEFT] && gui.dragged_sb != SBAR_LEFT) 3737 gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_LEFT], 3738 val, size, max); 3739 if (gui.which_scrollbars[SBAR_RIGHT] 3740 && gui.dragged_sb != SBAR_RIGHT) 3741 gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_RIGHT], 3742 val, size, max); 3743 } 3744 } 3745 #ifdef FEAT_VERTSPLIT 3746 prev_curwin = curwin; 3747 #endif 3748 --hold_gui_events; 3749 } 3750 3751 /* 3752 * Enable or disable a scrollbar. 3753 * Check for scrollbars for vertically split windows which are not enabled 3754 * sometimes. 3755 */ 3756 static void 3757 gui_do_scrollbar(wp, which, enable) 3758 win_T *wp; 3759 int which; /* SBAR_LEFT or SBAR_RIGHT */ 3760 int enable; /* TRUE to enable scrollbar */ 3761 { 3762 #ifdef FEAT_VERTSPLIT 3763 int midcol = curwin->w_wincol + curwin->w_width / 2; 3764 int has_midcol = (wp->w_wincol <= midcol 3765 && wp->w_wincol + wp->w_width >= midcol); 3766 3767 /* Only enable scrollbars that contain the middle column of the current 3768 * window. */ 3769 if (gui.which_scrollbars[SBAR_RIGHT] != gui.which_scrollbars[SBAR_LEFT]) 3770 { 3771 /* Scrollbars only on one side. Don't enable scrollbars that don't 3772 * contain the middle column of the current window. */ 3773 if (!has_midcol) 3774 enable = FALSE; 3775 } 3776 else 3777 { 3778 /* Scrollbars on both sides. Don't enable scrollbars that neither 3779 * contain the middle column of the current window nor are on the far 3780 * side. */ 3781 if (midcol > Columns / 2) 3782 { 3783 if (which == SBAR_LEFT ? wp->w_wincol != 0 : !has_midcol) 3784 enable = FALSE; 3785 } 3786 else 3787 { 3788 if (which == SBAR_RIGHT ? wp->w_wincol + wp->w_width != Columns 3789 : !has_midcol) 3790 enable = FALSE; 3791 } 3792 } 3793 #endif 3794 gui_mch_enable_scrollbar(&wp->w_scrollbars[which], enable); 3795 } 3796 3797 /* 3798 * Scroll a window according to the values set in the globals current_scrollbar 3799 * and scrollbar_value. Return TRUE if the cursor in the current window moved 3800 * or FALSE otherwise. 3801 */ 3802 int 3803 gui_do_scroll() 3804 { 3805 win_T *wp, *save_wp; 3806 int i; 3807 long nlines; 3808 pos_T old_cursor; 3809 linenr_T old_topline; 3810 #ifdef FEAT_DIFF 3811 int old_topfill; 3812 #endif 3813 3814 for (wp = firstwin, i = 0; i < current_scrollbar; wp = W_NEXT(wp), i++) 3815 if (wp == NULL) 3816 break; 3817 if (wp == NULL) 3818 /* Couldn't find window */ 3819 return FALSE; 3820 3821 /* 3822 * Compute number of lines to scroll. If zero, nothing to do. 3823 */ 3824 nlines = (long)scrollbar_value + 1 - (long)wp->w_topline; 3825 if (nlines == 0) 3826 return FALSE; 3827 3828 save_wp = curwin; 3829 old_topline = wp->w_topline; 3830 #ifdef FEAT_DIFF 3831 old_topfill = wp->w_topfill; 3832 #endif 3833 old_cursor = wp->w_cursor; 3834 curwin = wp; 3835 curbuf = wp->w_buffer; 3836 if (nlines < 0) 3837 scrolldown(-nlines, gui.dragged_wp == NULL); 3838 else 3839 scrollup(nlines, gui.dragged_wp == NULL); 3840 /* Reset dragged_wp after using it. "dragged_sb" will have been reset for 3841 * the mouse-up event already, but we still want it to behave like when 3842 * dragging. But not the next click in an arrow. */ 3843 if (gui.dragged_sb == SBAR_NONE) 3844 gui.dragged_wp = NULL; 3845 3846 if (old_topline != wp->w_topline 3847 #ifdef FEAT_DIFF 3848 || old_topfill != wp->w_topfill 3849 #endif 3850 ) 3851 { 3852 if (p_so != 0) 3853 { 3854 cursor_correct(); /* fix window for 'so' */ 3855 update_topline(); /* avoid up/down jump */ 3856 } 3857 if (old_cursor.lnum != wp->w_cursor.lnum) 3858 coladvance(wp->w_curswant); 3859 #ifdef FEAT_SCROLLBIND 3860 wp->w_scbind_pos = wp->w_topline; 3861 #endif 3862 } 3863 3864 /* Make sure wp->w_leftcol and wp->w_skipcol are correct. */ 3865 validate_cursor(); 3866 3867 curwin = save_wp; 3868 curbuf = save_wp->w_buffer; 3869 3870 /* 3871 * Don't call updateWindow() when nothing has changed (it will overwrite 3872 * the status line!). 3873 */ 3874 if (old_topline != wp->w_topline 3875 || wp->w_redr_type != 0 3876 #ifdef FEAT_DIFF 3877 || old_topfill != wp->w_topfill 3878 #endif 3879 ) 3880 { 3881 redraw_win_later(wp, VALID); 3882 updateWindow(wp); /* update window, status line, and cmdline */ 3883 } 3884 3885 return (wp == curwin && !equalpos(curwin->w_cursor, old_cursor)); 3886 } 3887 3888 3889 /* 3890 * Horizontal scrollbar stuff: 3891 */ 3892 3893 /* 3894 * Return length of line "lnum" for horizontal scrolling. 3895 */ 3896 static colnr_T 3897 scroll_line_len(lnum) 3898 linenr_T lnum; 3899 { 3900 char_u *p; 3901 colnr_T col; 3902 int w; 3903 3904 p = ml_get(lnum); 3905 col = 0; 3906 if (*p != NUL) 3907 for (;;) 3908 { 3909 w = chartabsize(p, col); 3910 mb_ptr_adv(p); 3911 if (*p == NUL) /* don't count the last character */ 3912 break; 3913 col += w; 3914 } 3915 return col; 3916 } 3917 3918 /* Remember which line is currently the longest, so that we don't have to 3919 * search for it when scrolling horizontally. */ 3920 static linenr_T longest_lnum = 0; 3921 3922 static void 3923 gui_update_horiz_scrollbar(force) 3924 int force; 3925 { 3926 long value, size, max; /* need 32 bit ints here */ 3927 3928 if (!gui.which_scrollbars[SBAR_BOTTOM]) 3929 return; 3930 3931 if (!force && gui.dragged_sb == SBAR_BOTTOM) 3932 return; 3933 3934 if (!force && curwin->w_p_wrap && gui.prev_wrap) 3935 return; 3936 3937 /* 3938 * It is possible for the cursor to be invalid if we're in the middle of 3939 * something (like changing files). If so, don't do anything for now. 3940 */ 3941 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) 3942 { 3943 gui.bottom_sbar.value = -1; 3944 return; 3945 } 3946 3947 size = W_WIDTH(curwin); 3948 if (curwin->w_p_wrap) 3949 { 3950 value = 0; 3951 #ifdef SCROLL_PAST_END 3952 max = 0; 3953 #else 3954 max = W_WIDTH(curwin) - 1; 3955 #endif 3956 } 3957 else 3958 { 3959 value = curwin->w_leftcol; 3960 3961 /* Calculate maximum for horizontal scrollbar. Check for reasonable 3962 * line numbers, topline and botline can be invalid when displaying is 3963 * postponed. */ 3964 if (vim_strchr(p_go, GO_HORSCROLL) == NULL 3965 && curwin->w_topline <= curwin->w_cursor.lnum 3966 && curwin->w_botline > curwin->w_cursor.lnum 3967 && curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) 3968 { 3969 linenr_T lnum; 3970 colnr_T n; 3971 3972 /* Use maximum of all visible lines. Remember the lnum of the 3973 * longest line, clostest to the cursor line. Used when scrolling 3974 * below. */ 3975 max = 0; 3976 for (lnum = curwin->w_topline; lnum < curwin->w_botline; ++lnum) 3977 { 3978 n = scroll_line_len(lnum); 3979 if (n > (colnr_T)max) 3980 { 3981 max = n; 3982 longest_lnum = lnum; 3983 } 3984 else if (n == (colnr_T)max 3985 && abs((int)(lnum - curwin->w_cursor.lnum)) 3986 < abs((int)(longest_lnum - curwin->w_cursor.lnum))) 3987 longest_lnum = lnum; 3988 } 3989 } 3990 else 3991 /* Use cursor line only. */ 3992 max = scroll_line_len(curwin->w_cursor.lnum); 3993 #ifdef FEAT_VIRTUALEDIT 3994 if (virtual_active()) 3995 { 3996 /* May move the cursor even further to the right. */ 3997 if (curwin->w_virtcol >= (colnr_T)max) 3998 max = curwin->w_virtcol; 3999 } 4000 #endif 4001 4002 #ifndef SCROLL_PAST_END 4003 max += W_WIDTH(curwin) - 1; 4004 #endif 4005 /* The line number isn't scrolled, thus there is less space when 4006 * 'number' is set (also for 'foldcolumn'). */ 4007 size -= curwin_col_off(); 4008 #ifndef SCROLL_PAST_END 4009 max -= curwin_col_off(); 4010 #endif 4011 } 4012 4013 #ifndef SCROLL_PAST_END 4014 if (value > max - size + 1) 4015 value = max - size + 1; /* limit the value to allowable range */ 4016 #endif 4017 4018 #ifdef FEAT_RIGHTLEFT 4019 if (curwin->w_p_rl) 4020 { 4021 value = max + 1 - size - value; 4022 if (value < 0) 4023 { 4024 size += value; 4025 value = 0; 4026 } 4027 } 4028 #endif 4029 if (!force && value == gui.bottom_sbar.value && size == gui.bottom_sbar.size 4030 && max == gui.bottom_sbar.max) 4031 return; 4032 4033 gui.bottom_sbar.value = value; 4034 gui.bottom_sbar.size = size; 4035 gui.bottom_sbar.max = max; 4036 gui.prev_wrap = curwin->w_p_wrap; 4037 4038 gui_mch_set_scrollbar_thumb(&gui.bottom_sbar, value, size, max); 4039 } 4040 4041 /* 4042 * Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise. 4043 */ 4044 int 4045 gui_do_horiz_scroll() 4046 { 4047 /* no wrapping, no scrolling */ 4048 if (curwin->w_p_wrap) 4049 return FALSE; 4050 4051 if (curwin->w_leftcol == scrollbar_value) 4052 return FALSE; 4053 4054 curwin->w_leftcol = scrollbar_value; 4055 4056 /* When the line of the cursor is too short, move the cursor to the 4057 * longest visible line. Do a sanity check on "longest_lnum", just in 4058 * case. */ 4059 if (vim_strchr(p_go, GO_HORSCROLL) == NULL 4060 && longest_lnum >= curwin->w_topline 4061 && longest_lnum < curwin->w_botline 4062 && !virtual_active()) 4063 { 4064 if (scrollbar_value > scroll_line_len(curwin->w_cursor.lnum)) 4065 { 4066 curwin->w_cursor.lnum = longest_lnum; 4067 curwin->w_cursor.col = 0; 4068 } 4069 } 4070 4071 return leftcol_changed(); 4072 } 4073 4074 /* 4075 * Check that none of the colors are the same as the background color 4076 */ 4077 void 4078 gui_check_colors() 4079 { 4080 if (gui.norm_pixel == gui.back_pixel || gui.norm_pixel == INVALCOLOR) 4081 { 4082 gui_set_bg_color((char_u *)"White"); 4083 if (gui.norm_pixel == gui.back_pixel || gui.norm_pixel == INVALCOLOR) 4084 gui_set_fg_color((char_u *)"Black"); 4085 } 4086 } 4087 4088 static void 4089 gui_set_fg_color(name) 4090 char_u *name; 4091 { 4092 gui.norm_pixel = gui_get_color(name); 4093 hl_set_fg_color_name(vim_strsave(name)); 4094 } 4095 4096 static void 4097 gui_set_bg_color(name) 4098 char_u *name; 4099 { 4100 gui.back_pixel = gui_get_color(name); 4101 hl_set_bg_color_name(vim_strsave(name)); 4102 } 4103 4104 /* 4105 * Allocate a color by name. 4106 * Returns INVALCOLOR and gives an error message when failed. 4107 */ 4108 guicolor_T 4109 gui_get_color(name) 4110 char_u *name; 4111 { 4112 guicolor_T t; 4113 4114 if (*name == NUL) 4115 return INVALCOLOR; 4116 t = gui_mch_get_color(name); 4117 4118 if (t == INVALCOLOR 4119 #if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_KDE) 4120 && gui.in_use 4121 #endif 4122 ) 4123 EMSG2(_("E254: Cannot allocate color %s"), name); 4124 return t; 4125 } 4126 4127 /* 4128 * Return the grey value of a color (range 0-255). 4129 */ 4130 int 4131 gui_get_lightness(pixel) 4132 guicolor_T pixel; 4133 { 4134 long_u rgb = gui_mch_get_rgb(pixel); 4135 4136 return ( (((rgb >> 16) & 0xff) * 299) 4137 + (((rgb >> 8) & 0xff) * 587) 4138 + ((rgb & 0xff) * 114)) / 1000; 4139 } 4140 4141 #if defined(FEAT_GUI_X11) || defined(PROTO) 4142 void 4143 gui_new_scrollbar_colors() 4144 { 4145 win_T *wp; 4146 4147 /* Nothing to do if GUI hasn't started yet. */ 4148 if (!gui.in_use) 4149 return; 4150 4151 FOR_ALL_WINDOWS(wp) 4152 { 4153 gui_mch_set_scrollbar_colors(&(wp->w_scrollbars[SBAR_LEFT])); 4154 gui_mch_set_scrollbar_colors(&(wp->w_scrollbars[SBAR_RIGHT])); 4155 } 4156 gui_mch_set_scrollbar_colors(&gui.bottom_sbar); 4157 } 4158 #endif 4159 4160 /* 4161 * Call this when focus has changed. 4162 */ 4163 void 4164 gui_focus_change(in_focus) 4165 int in_focus; 4166 { 4167 /* 4168 * Skip this code to avoid drawing the cursor when debugging and switching 4169 * between the debugger window and gvim. 4170 */ 4171 #if 1 4172 gui.in_focus = in_focus; 4173 out_flush(); /* make sure output has been written */ 4174 gui_update_cursor(TRUE, FALSE); 4175 4176 # ifdef FEAT_XIM 4177 xim_set_focus(in_focus); 4178 # endif 4179 4180 ui_focus_change(in_focus); 4181 #endif 4182 } 4183 4184 /* 4185 * Called when the mouse moved (but not when dragging). 4186 */ 4187 void 4188 gui_mouse_moved(x, y) 4189 int x; 4190 int y; 4191 { 4192 win_T *wp; 4193 char_u st[6]; 4194 4195 #ifdef FEAT_MOUSESHAPE 4196 /* Get window pointer, and update mouse shape as well. */ 4197 wp = xy2win(x, y); 4198 #endif 4199 4200 /* Only handle this when 'mousefocus' set and ... */ 4201 if (p_mousef 4202 && !hold_gui_events /* not holding events */ 4203 && (State & (NORMAL|INSERT))/* Normal/Visual/Insert mode */ 4204 && State != HITRETURN /* but not hit-return prompt */ 4205 && msg_scrolled == 0 /* no scrolled message */ 4206 && !need_mouse_correct /* not moving the pointer */ 4207 && gui.in_focus) /* gvim in focus */ 4208 { 4209 /* Don't move the mouse when it's left or right of the Vim window */ 4210 if (x < 0 || x > Columns * gui.char_width) 4211 return; 4212 #ifndef FEAT_MOUSESHAPE 4213 wp = xy2win(x, y); 4214 #endif 4215 if (wp == curwin || wp == NULL) 4216 return; /* still in the same old window, or none at all */ 4217 4218 /* 4219 * format a mouse click on status line input 4220 * ala gui_send_mouse_event(0, x, y, 0, 0); 4221 * Trick: Use a column number -1, so that get_pseudo_mouse_code() will 4222 * generate a K_LEFTMOUSE_NM key code. 4223 */ 4224 if (finish_op) 4225 { 4226 /* abort the current operator first */ 4227 st[0] = ESC; 4228 add_to_input_buf(st, 1); 4229 } 4230 st[0] = CSI; 4231 st[1] = KS_MOUSE; 4232 st[2] = KE_FILLER; 4233 st[3] = (char_u)MOUSE_LEFT; 4234 fill_mouse_coord(st + 4, 4235 #ifdef FEAT_VERTSPLIT 4236 wp->w_wincol == 0 ? -1 : wp->w_wincol + MOUSE_COLOFF, 4237 #else 4238 -1, 4239 #endif 4240 wp->w_height + W_WINROW(wp)); 4241 4242 add_to_input_buf(st, 8); 4243 st[3] = (char_u)MOUSE_RELEASE; 4244 add_to_input_buf(st, 8); 4245 #ifdef FEAT_GUI_GTK 4246 /* Need to wake up the main loop */ 4247 if (gtk_main_level() > 0) 4248 gtk_main_quit(); 4249 #endif 4250 } 4251 } 4252 4253 /* 4254 * Called when mouse should be moved to window with focus. 4255 */ 4256 void 4257 gui_mouse_correct() 4258 { 4259 int x, y; 4260 win_T *wp = NULL; 4261 4262 need_mouse_correct = FALSE; 4263 4264 if (!(gui.in_use && p_mousef)) 4265 return; 4266 4267 gui_mch_getmouse(&x, &y); 4268 /* Don't move the mouse when it's left or right of the Vim window */ 4269 if (x < 0 || x > Columns * gui.char_width) 4270 return; 4271 if (y >= 0) 4272 wp = xy2win(x, y); 4273 if (wp != curwin && wp != NULL) /* If in other than current window */ 4274 { 4275 validate_cline_row(); 4276 gui_mch_setmouse((int)W_ENDCOL(curwin) * gui.char_width - 3, 4277 (W_WINROW(curwin) + curwin->w_wrow) * gui.char_height 4278 + (gui.char_height) / 2); 4279 } 4280 } 4281 4282 /* 4283 * Find window where the mouse pointer "y" coordinate is in. 4284 */ 4285 /*ARGSUSED*/ 4286 static win_T * 4287 xy2win(x, y) 4288 int x; 4289 int y; 4290 { 4291 #ifdef FEAT_WINDOWS 4292 int row; 4293 int col; 4294 win_T *wp; 4295 4296 row = Y_2_ROW(y); 4297 col = X_2_COL(x); 4298 if (row < 0 || col < 0) /* before first window */ 4299 return NULL; 4300 wp = mouse_find_win(&row, &col); 4301 # ifdef FEAT_MOUSESHAPE 4302 if (State == HITRETURN || State == ASKMORE) 4303 { 4304 if (Y_2_ROW(y) >= msg_row) 4305 update_mouseshape(SHAPE_IDX_MOREL); 4306 else 4307 update_mouseshape(SHAPE_IDX_MORE); 4308 } 4309 else if (row > wp->w_height) /* below status line */ 4310 update_mouseshape(SHAPE_IDX_CLINE); 4311 # ifdef FEAT_VERTSPLIT 4312 else if (!(State & CMDLINE) && W_VSEP_WIDTH(wp) > 0 && col == wp->w_width 4313 && (row != wp->w_height || !stl_connected(wp))) 4314 update_mouseshape(SHAPE_IDX_VSEP); 4315 # endif 4316 else if (!(State & CMDLINE) && W_STATUS_HEIGHT(wp) > 0 4317 && row == wp->w_height) 4318 update_mouseshape(SHAPE_IDX_STATUS); 4319 else 4320 update_mouseshape(-2); 4321 # endif 4322 return wp; 4323 #else 4324 return firstwin; 4325 #endif 4326 } 4327 4328 /* 4329 * ":gui" and ":gvim": Change from the terminal version to the GUI version. 4330 * File names may be given to redefine the args list. 4331 */ 4332 void 4333 ex_gui(eap) 4334 exarg_T *eap; 4335 { 4336 char_u *arg = eap->arg; 4337 4338 /* 4339 * Check for "-f" argument: foreground, don't fork. 4340 * Also don't fork when started with "gvim -f". 4341 * Do fork when using "gui -b". 4342 */ 4343 if (arg[0] == '-' 4344 && (arg[1] == 'f' || arg[1] == 'b') 4345 && (arg[2] == NUL || vim_iswhite(arg[2]))) 4346 { 4347 gui.dofork = (arg[1] == 'b'); 4348 eap->arg = skipwhite(eap->arg + 2); 4349 } 4350 if (!gui.in_use) 4351 { 4352 /* Clear the command. Needed for when forking+exiting, to avoid part 4353 * of the argument ending up after the shell prompt. */ 4354 msg_clr_eos_force(); 4355 gui_start(); 4356 } 4357 if (!ends_excmd(*eap->arg)) 4358 ex_next(eap); 4359 } 4360 4361 #if ((defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_W32) \ 4362 || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_KDE)) && defined(FEAT_TOOLBAR)) || defined(PROTO) 4363 /* 4364 * This is shared between Athena, Motif and GTK. 4365 */ 4366 static void gfp_setname __ARGS((char_u *fname, void *cookie)); 4367 4368 /* 4369 * Callback function for do_in_runtimepath(). 4370 */ 4371 static void 4372 gfp_setname(fname, cookie) 4373 char_u *fname; 4374 void *cookie; 4375 { 4376 char_u *gfp_buffer = cookie; 4377 4378 if (STRLEN(fname) >= MAXPATHL) 4379 *gfp_buffer = NUL; 4380 else 4381 STRCPY(gfp_buffer, fname); 4382 } 4383 4384 /* 4385 * Find the path of bitmap "name" with extension "ext" in 'runtimepath'. 4386 * Return FAIL for failure and OK if buffer[MAXPATHL] contains the result. 4387 */ 4388 int 4389 gui_find_bitmap(name, buffer, ext) 4390 char_u *name; 4391 char_u *buffer; 4392 char *ext; 4393 { 4394 if (STRLEN(name) > MAXPATHL - 14) 4395 return FAIL; 4396 vim_snprintf((char *)buffer, MAXPATHL, "bitmaps/%s.%s", name, ext); 4397 if (do_in_runtimepath(buffer, FALSE, gfp_setname, buffer) == FAIL 4398 || *buffer == NUL) 4399 return FAIL; 4400 return OK; 4401 } 4402 4403 # if !defined(HAVE_GTK2) || defined(PROTO) 4404 /* 4405 * Given the name of the "icon=" argument, try finding the bitmap file for the 4406 * icon. If it is an absolute path name, use it as it is. Otherwise append 4407 * "ext" and search for it in 'runtimepath'. 4408 * The result is put in "buffer[MAXPATHL]". If something fails "buffer" 4409 * contains "name". 4410 */ 4411 void 4412 gui_find_iconfile(name, buffer, ext) 4413 char_u *name; 4414 char_u *buffer; 4415 char *ext; 4416 { 4417 char_u buf[MAXPATHL + 1]; 4418 4419 expand_env(name, buffer, MAXPATHL); 4420 if (!mch_isFullName(buffer) && gui_find_bitmap(buffer, buf, ext) == OK) 4421 STRCPY(buffer, buf); 4422 } 4423 # endif 4424 #endif 4425 4426 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_KDE) || defined(FEAT_GUI_X11) || defined(PROTO) 4427 void 4428 display_errors() 4429 { 4430 char_u *p; 4431 4432 if (isatty(2)) 4433 fflush(stderr); 4434 else if (error_ga.ga_data != NULL) 4435 { 4436 /* avoid putting up a message box with blanks only */ 4437 for (p = (char_u *)error_ga.ga_data; *p != NUL; ++p) 4438 if (!isspace(*p)) 4439 { 4440 /* Truncate a very long message, it will go off-screen. */ 4441 if (STRLEN(p) > 2000) 4442 STRCPY(p + 2000 - 14, "...(truncated)"); 4443 (void)do_dialog(VIM_ERROR, (char_u *)_("Error"), 4444 p, (char_u *)_("&Ok"), 1, NULL); 4445 break; 4446 } 4447 ga_clear(&error_ga); 4448 } 4449 } 4450 #endif 4451 4452 #if defined(NO_CONSOLE_INPUT) || defined(PROTO) 4453 /* 4454 * Return TRUE if still starting up and there is no place to enter text. 4455 * For GTK and X11 we check if stderr is not a tty, which means we were 4456 * (probably) started from the desktop. Also check stdin, "vim >& file" does 4457 * allow typing on stdin. 4458 */ 4459 int 4460 no_console_input() 4461 { 4462 return ((!gui.in_use || gui.starting) 4463 # ifndef NO_CONSOLE 4464 && !isatty(0) && !isatty(2) 4465 # endif 4466 ); 4467 } 4468 #endif 4469 4470 #if defined(FIND_REPLACE_DIALOG) || defined(FEAT_SUN_WORKSHOP) \ 4471 || defined(PROTO) 4472 /* 4473 * Update the current window and the screen. 4474 */ 4475 void 4476 gui_update_screen() 4477 { 4478 update_topline(); 4479 validate_cursor(); 4480 update_screen(0); /* may need to update the screen */ 4481 setcursor(); 4482 out_flush(); /* make sure output has been written */ 4483 gui_update_cursor(TRUE, FALSE); 4484 gui_mch_flush(); 4485 } 4486 #endif 4487 4488 #if defined(FIND_REPLACE_DIALOG) || defined(PROTO) 4489 static void concat_esc __ARGS((garray_T *gap, char_u *text, int what)); 4490 4491 /* 4492 * Get the text to use in a find/replace dialog. Uses the last search pattern 4493 * if the argument is empty. 4494 * Returns an allocated string. 4495 */ 4496 char_u * 4497 get_find_dialog_text(arg, wwordp, mcasep) 4498 char_u *arg; 4499 int *wwordp; /* return: TRUE if \< \> found */ 4500 int *mcasep; /* return: TRUE if \C found */ 4501 { 4502 char_u *text; 4503 4504 if (*arg == NUL) 4505 text = last_search_pat(); 4506 else 4507 text = arg; 4508 if (text != NULL) 4509 { 4510 text = vim_strsave(text); 4511 if (text != NULL) 4512 { 4513 int len = STRLEN(text); 4514 int i; 4515 4516 /* Remove "\V" */ 4517 if (len >= 2 && STRNCMP(text, "\\V", 2) == 0) 4518 { 4519 mch_memmove(text, text + 2, (size_t)(len - 1)); 4520 len -= 2; 4521 } 4522 4523 /* Recognize "\c" and "\C" and remove. */ 4524 if (len >= 2 && *text == '\\' && (text[1] == 'c' || text[1] == 'C')) 4525 { 4526 *mcasep = (text[1] == 'C'); 4527 mch_memmove(text, text + 2, (size_t)(len - 1)); 4528 len -= 2; 4529 } 4530 4531 /* Recognize "\<text\>" and remove. */ 4532 if (len >= 4 4533 && STRNCMP(text, "\\<", 2) == 0 4534 && STRNCMP(text + len - 2, "\\>", 2) == 0) 4535 { 4536 *wwordp = TRUE; 4537 mch_memmove(text, text + 2, (size_t)(len - 4)); 4538 text[len - 4] = NUL; 4539 } 4540 4541 /* Recognize "\/" or "\?" and remove. */ 4542 for (i = 0; i + 1 < len; ++i) 4543 if (text[i] == '\\' && (text[i + 1] == '/' 4544 || text[i + 1] == '?')) 4545 { 4546 mch_memmove(text + i, text + i + 1, (size_t)(len - i)); 4547 --len; 4548 } 4549 } 4550 } 4551 return text; 4552 } 4553 4554 /* 4555 * Concatenate "text" to grow array "gap", escaping "what" with a backslash. 4556 */ 4557 static void 4558 concat_esc(gap, text, what) 4559 garray_T *gap; 4560 char_u *text; 4561 int what; 4562 { 4563 while (*text != NUL) 4564 { 4565 #ifdef FEAT_MBYTE 4566 int l = (*mb_ptr2len)(text); 4567 if (l > 1) 4568 { 4569 while (--l >= 0) 4570 ga_append(gap, *text++); 4571 continue; 4572 } 4573 #endif 4574 if (*text == what) 4575 ga_append(gap, '\\'); 4576 ga_append(gap, *text); 4577 ++text; 4578 } 4579 } 4580 4581 /* 4582 * Handle the press of a button in the find-replace dialog. 4583 * Return TRUE when something was added to the input buffer. 4584 */ 4585 int 4586 gui_do_findrepl(flags, find_text, repl_text, down) 4587 int flags; /* one of FRD_REPLACE, FRD_FINDNEXT, etc. */ 4588 char_u *find_text; 4589 char_u *repl_text; 4590 int down; /* Search downwards. */ 4591 { 4592 garray_T ga; 4593 int i; 4594 int type = (flags & FRD_TYPE_MASK); 4595 char_u *p; 4596 regmatch_T regmatch; 4597 int save_did_emsg = did_emsg; 4598 4599 ga_init2(&ga, 1, 100); 4600 if (type == FRD_REPLACEALL) 4601 ga_concat(&ga, (char_u *)"%s/"); 4602 4603 ga_concat(&ga, (char_u *)"\\V"); 4604 if (flags & FRD_MATCH_CASE) 4605 ga_concat(&ga, (char_u *)"\\C"); 4606 else 4607 ga_concat(&ga, (char_u *)"\\c"); 4608 if (flags & FRD_WHOLE_WORD) 4609 ga_concat(&ga, (char_u *)"\\<"); 4610 if (type == FRD_REPLACEALL || down) 4611 concat_esc(&ga, find_text, '/'); /* escape slashes */ 4612 else 4613 concat_esc(&ga, find_text, '?'); /* escape '?' */ 4614 if (flags & FRD_WHOLE_WORD) 4615 ga_concat(&ga, (char_u *)"\\>"); 4616 4617 if (type == FRD_REPLACEALL) 4618 { 4619 ga_concat(&ga, (char_u *)"/"); 4620 concat_esc(&ga, repl_text, '/'); /* escape slashes */ 4621 ga_concat(&ga, (char_u *)"/g"); 4622 } 4623 ga_append(&ga, NUL); 4624 4625 if (type == FRD_REPLACE) 4626 { 4627 /* Do the replacement when the text at the cursor matches. Thus no 4628 * replacement is done if the cursor was moved! */ 4629 regmatch.regprog = vim_regcomp(ga.ga_data, RE_MAGIC + RE_STRING); 4630 regmatch.rm_ic = 0; 4631 if (regmatch.regprog != NULL) 4632 { 4633 p = ml_get_cursor(); 4634 if (vim_regexec_nl(®match, p, (colnr_T)0) 4635 && regmatch.startp[0] == p) 4636 { 4637 /* Clear the command line to remove any old "No match" 4638 * error. */ 4639 msg_end_prompt(); 4640 4641 if (u_save_cursor() == OK) 4642 { 4643 /* A button was pressed thus undo should be synced. */ 4644 if (no_u_sync == 0) 4645 u_sync(); 4646 4647 del_bytes((long)(regmatch.endp[0] - regmatch.startp[0]), 4648 FALSE); 4649 ins_str(repl_text); 4650 } 4651 } 4652 else 4653 MSG(_("No match at cursor, finding next")); 4654 vim_free(regmatch.regprog); 4655 } 4656 } 4657 4658 if (type == FRD_REPLACEALL) 4659 { 4660 /* A button was pressed, thus undo should be synced. */ 4661 if (no_u_sync == 0) 4662 u_sync(); 4663 do_cmdline_cmd(ga.ga_data); 4664 } 4665 else 4666 { 4667 /* Search for the next match. */ 4668 i = msg_scroll; 4669 do_search(NULL, down ? '/' : '?', ga.ga_data, 1L, 4670 SEARCH_MSG + SEARCH_MARK); 4671 msg_scroll = i; /* don't let an error message set msg_scroll */ 4672 } 4673 4674 /* Don't want to pass did_emsg to other code, it may cause disabling 4675 * syntax HL if we were busy redrawing. */ 4676 did_emsg = save_did_emsg; 4677 4678 if (State & (NORMAL | INSERT)) 4679 { 4680 gui_update_screen(); /* update the screen */ 4681 msg_didout = 0; /* overwrite any message */ 4682 need_wait_return = FALSE; /* don't wait for return */ 4683 } 4684 4685 vim_free(ga.ga_data); 4686 return (ga.ga_len > 0); 4687 } 4688 4689 #endif 4690 4691 #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ 4692 || defined(FEAT_GUI_MSWIN) \ 4693 || defined(FEAT_GUI_MAC) \ 4694 || defined(PROTO) 4695 4696 #ifdef FEAT_WINDOWS 4697 static void gui_wingoto_xy __ARGS((int x, int y)); 4698 4699 /* 4700 * Jump to the window at specified point (x, y). 4701 */ 4702 static void 4703 gui_wingoto_xy(x, y) 4704 int x; 4705 int y; 4706 { 4707 int row = Y_2_ROW(y); 4708 int col = X_2_COL(x); 4709 win_T *wp; 4710 4711 if (row >= 0 && col >= 0) 4712 { 4713 wp = mouse_find_win(&row, &col); 4714 if (wp != NULL && wp != curwin) 4715 win_goto(wp); 4716 } 4717 } 4718 #endif 4719 4720 /* 4721 * Process file drop. Mouse cursor position, key modifiers, name of files 4722 * and count of files are given. Argument "fnames[count]" has full pathnames 4723 * of dropped files, they will be freed in this function, and caller can't use 4724 * fnames after call this function. 4725 */ 4726 /*ARGSUSED*/ 4727 void 4728 gui_handle_drop(x, y, modifiers, fnames, count) 4729 int x; 4730 int y; 4731 int_u modifiers; 4732 char_u **fnames; 4733 int count; 4734 { 4735 int i; 4736 char_u *p; 4737 4738 /* 4739 * When the cursor is at the command line, add the file names to the 4740 * command line, don't edit the files. 4741 */ 4742 if (State & CMDLINE) 4743 { 4744 shorten_filenames(fnames, count); 4745 for (i = 0; i < count; ++i) 4746 { 4747 if (fnames[i] != NULL) 4748 { 4749 if (i > 0) 4750 add_to_input_buf((char_u*)" ", 1); 4751 4752 /* We don't know what command is used thus we can't be sure 4753 * about which characters need to be escaped. Only escape the 4754 * most common ones. */ 4755 # ifdef BACKSLASH_IN_FILENAME 4756 p = vim_strsave_escaped(fnames[i], (char_u *)" \t\"|"); 4757 # else 4758 p = vim_strsave_escaped(fnames[i], (char_u *)"\\ \t\"|"); 4759 # endif 4760 if (p != NULL) 4761 add_to_input_buf(p, (int)STRLEN(p)); 4762 vim_free(p); 4763 vim_free(fnames[i]); 4764 } 4765 } 4766 vim_free(fnames); 4767 } 4768 else 4769 { 4770 /* Go to the window under mouse cursor, then shorten given "fnames" by 4771 * current window, because a window can have local current dir. */ 4772 # ifdef FEAT_WINDOWS 4773 gui_wingoto_xy(x, y); 4774 # endif 4775 shorten_filenames(fnames, count); 4776 4777 /* If Shift held down, remember the first item. */ 4778 if ((modifiers & MOUSE_SHIFT) != 0) 4779 p = vim_strsave(fnames[0]); 4780 else 4781 p = NULL; 4782 4783 /* Handle the drop, :edit or :split to get to the file. This also 4784 * frees fnames[]. Skip this if there is only one item it's a 4785 * directory and Shift is held down. */ 4786 if (count == 1 && (modifiers & MOUSE_SHIFT) != 0 4787 && mch_isdir(fnames[0])) 4788 { 4789 vim_free(fnames[0]); 4790 vim_free(fnames); 4791 } 4792 else 4793 handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0); 4794 4795 /* If Shift held down, change to first file's directory. If the first 4796 * item is a directory, change to that directory (and let the explorer 4797 * plugin show the contents). */ 4798 if (p != NULL) 4799 { 4800 if (mch_isdir(p)) 4801 { 4802 if (mch_chdir((char *)p) == 0) 4803 shorten_fnames(TRUE); 4804 } 4805 else if (vim_chdirfile(p) == OK) 4806 shorten_fnames(TRUE); 4807 vim_free(p); 4808 } 4809 4810 /* Update the screen display */ 4811 update_screen(NOT_VALID); 4812 # ifdef FEAT_MENU 4813 gui_update_menus(0); 4814 # endif 4815 setcursor(); 4816 out_flush(); 4817 gui_update_cursor(FALSE, FALSE); 4818 gui_mch_flush(); 4819 } 4820 } 4821 #endif 4822