diff options
Diffstat (limited to 'src/gui.c')
-rw-r--r-- | src/gui.c | 4740 |
1 files changed, 4740 insertions, 0 deletions
diff --git a/src/gui.c b/src/gui.c new file mode 100644 index 0000000000..3ffa2257d8 --- /dev/null +++ b/src/gui.c @@ -0,0 +1,4740 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * GUI/Motif support by Robert Webb + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#include "vim.h" + +/* Structure containing all the GUI information */ +gui_T gui; + +#if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) +static void set_guifontwide __ARGS((char_u *font_name)); +#endif +static void gui_check_pos __ARGS((void)); +static void gui_position_components __ARGS((int)); +static void gui_outstr __ARGS((char_u *, int)); +static int gui_screenchar __ARGS((int off, int flags, guicolor_T fg, guicolor_T bg, int back)); +#ifdef HAVE_GTK2 +static int gui_screenstr __ARGS((int off, int len, int flags, guicolor_T fg, guicolor_T bg, int back)); +#endif +static void gui_delete_lines __ARGS((int row, int count)); +static void gui_insert_lines __ARGS((int row, int count)); +static void fill_mouse_coord __ARGS((char_u *p, int col, int row)); +static void gui_do_scrollbar __ARGS((win_T *wp, int which, int enable)); +static colnr_T scroll_line_len __ARGS((linenr_T lnum)); +static void gui_update_horiz_scrollbar __ARGS((int)); +static win_T *xy2win __ARGS((int x, int y)); + +static int can_update_cursor = TRUE; /* can display the cursor */ + +/* + * The Athena scrollbars can move the thumb to after the end of the scrollbar, + * this makes the thumb indicate the part of the text that is shown. Motif + * can't do this. + */ +#if defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MAC) +# define SCROLL_PAST_END +#endif + +/* + * gui_start -- Called when user wants to start the GUI. + * + * Careful: This function can be called recursively when there is a ":gui" + * command in the .gvimrc file. Only the first call should fork, not the + * recursive call. + */ + void +gui_start() +{ + char_u *old_term; +#if defined(UNIX) && !defined(__BEOS__) && !defined(MACOS_X) +# define MAY_FORK + int dofork = TRUE; +#endif + static int recursive = 0; + + old_term = vim_strsave(T_NAME); + + /* + * Set_termname() will call gui_init() to start the GUI. + * Set the "starting" flag, to indicate that the GUI will start. + * + * We don't want to open the GUI shell until after we've read .gvimrc, + * otherwise we don't know what font we will use, and hence we don't know + * what size the shell should be. So if there are errors in the .gvimrc + * file, they will have to go to the terminal: Set full_screen to FALSE. + * full_screen will be set to TRUE again by a successful termcapinit(). + */ + settmode(TMODE_COOK); /* stop RAW mode */ + if (full_screen) + cursor_on(); /* needed for ":gui" in .vimrc */ + gui.starting = TRUE; + full_screen = FALSE; + +#ifdef MAY_FORK + if (!gui.dofork || vim_strchr(p_go, GO_FORG) || recursive) + dofork = FALSE; +#endif + ++recursive; + + termcapinit((char_u *)"builtin_gui"); + gui.starting = recursive - 1; + + if (!gui.in_use) /* failed to start GUI */ + { + termcapinit(old_term); /* back to old term settings */ + settmode(TMODE_RAW); /* restart RAW mode */ +#ifdef FEAT_TITLE + set_title_defaults(); /* set 'title' and 'icon' again */ +#endif + } + + vim_free(old_term); + +#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11) + if (gui.in_use) + /* Display error messages in a dialog now. */ + display_errors(); +#endif + +#if defined(MAY_FORK) && !defined(__QNXNTO__) + /* + * Quit the current process and continue in the child. + * Makes "gvim file" disconnect from the shell it was started in. + * Don't do this when Vim was started with "-f" or the 'f' flag is present + * in 'guioptions'. + */ + if (gui.in_use && dofork) + { + int pipefd[2]; /* pipe between parent and child */ + int pipe_error; + char dummy; + pid_t pid = -1; + + /* Setup a pipe between the child and the parent, so that the parent + * knows when the child has done the setsid() call and is allowed to + * exit. */ + pipe_error = (pipe(pipefd) < 0); + pid = fork(); + if (pid > 0) /* Parent */ + { + /* Give the child some time to do the setsid(), otherwise the + * exit() may kill the child too (when starting gvim from inside a + * gvim). */ + if (pipe_error) + ui_delay(300L, TRUE); + else + { + /* The read returns when the child closes the pipe (or when + * the child dies for some reason). */ + close(pipefd[1]); + (void)read(pipefd[0], &dummy, (size_t)1); + close(pipefd[0]); + } + + /* When swapping screens we may need to go to the next line, e.g., + * after a hit-enter prompt and using ":gui". */ + if (newline_on_exit) + mch_errmsg("\r\n"); + + /* + * The parent must skip the normal exit() processing, the child + * will do it. For example, GTK messes up signals when exiting. + */ + _exit(0); + } + +# if defined(HAVE_SETSID) || defined(HAVE_SETPGID) + /* + * Change our process group. On some systems/shells a CTRL-C in the + * shell where Vim was started would otherwise kill gvim! + */ + if (pid == 0) /* child */ +# if defined(HAVE_SETSID) + (void)setsid(); +# else + (void)setpgid(0, 0); +# endif +# endif + if (!pipe_error) + { + close(pipefd[0]); + close(pipefd[1]); + } + +# if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) + /* Tell the session manager our new PID */ + gui_mch_forked(); +# endif + } +#else +# if defined(__QNXNTO__) + if (gui.in_use && dofork) + procmgr_daemon(0, PROCMGR_DAEMON_KEEPUMASK | PROCMGR_DAEMON_NOCHDIR | + PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL); +# endif +#endif + +#ifdef FEAT_AUTOCMD + /* If the GUI started successfully, trigger the GUIEnter event */ + if (gui.in_use) + apply_autocmds(EVENT_GUIENTER, NULL, NULL, FALSE, curbuf); +#endif + + --recursive; +} + +/* + * Call this when vim starts up, whether or not the GUI is started + */ + void +gui_prepare(argc, argv) + int *argc; + char **argv; +{ + gui.in_use = FALSE; /* No GUI yet (maybe later) */ + gui.starting = FALSE; /* No GUI yet (maybe later) */ + gui_mch_prepare(argc, argv); +} + +/* + * Try initializing the GUI and check if it can be started. + * Used from main() to check early if "vim -g" can start the GUI. + * Used from gui_init() to prepare for starting the GUI. + * Returns FAIL or OK. + */ + int +gui_init_check() +{ + static int result = MAYBE; + + if (result != MAYBE) + { + if (result == FAIL) + EMSG(_("E229: Cannot start the GUI")); + return result; + } + + gui.shell_created = FALSE; + gui.dying = FALSE; + gui.in_focus = TRUE; /* so the guicursor setting works */ + gui.dragged_sb = SBAR_NONE; + gui.dragged_wp = NULL; + gui.pointer_hidden = FALSE; + gui.col = 0; + gui.row = 0; + gui.num_cols = Columns; + gui.num_rows = Rows; + + gui.cursor_is_valid = FALSE; + gui.scroll_region_top = 0; + gui.scroll_region_bot = Rows - 1; + gui.scroll_region_left = 0; + gui.scroll_region_right = Columns - 1; + gui.highlight_mask = HL_NORMAL; + gui.char_width = 1; + gui.char_height = 1; + gui.char_ascent = 0; + gui.border_width = 0; + + gui.norm_font = NOFONT; +#ifndef HAVE_GTK2 + gui.bold_font = NOFONT; + gui.ital_font = NOFONT; + gui.boldital_font = NOFONT; +# ifdef FEAT_XFONTSET + gui.fontset = NOFONTSET; +# endif +#endif + +#ifdef FEAT_MENU +# ifndef HAVE_GTK2 +# ifdef FONTSET_ALWAYS + gui.menu_fontset = NOFONTSET; +# else + gui.menu_font = NOFONT; +# endif +# endif + gui.menu_is_active = TRUE; /* default: include menu */ +# ifndef FEAT_GUI_GTK + gui.menu_height = MENU_DEFAULT_HEIGHT; + gui.menu_width = 0; +# endif +#endif +#if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) + gui.toolbar_height = 0; +#endif +#if defined(FEAT_FOOTER) && defined(FEAT_GUI_MOTIF) + gui.footer_height = 0; +#endif +#ifdef FEAT_BEVAL_TIP + gui.tooltip_fontset = NOFONTSET; +#endif + + gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH; + gui.prev_wrap = -1; + +#ifdef ALWAYS_USE_GUI + result = OK; +#else + result = gui_mch_init_check(); +#endif + return result; +} + +/* + * This is the call which starts the GUI. + */ + void +gui_init() +{ + win_T *wp; + static int recursive = 0; + + /* + * It's possible to use ":gui" in a .gvimrc file. The first halve of this + * function will then be executed at the first call, the rest by the + * recursive call. This allow the shell to be opened halfway reading a + * gvimrc file. + */ + if (!recursive) + { + ++recursive; + + clip_init(TRUE); + + /* If can't initialize, don't try doing the rest */ + if (gui_init_check() == FAIL) + { + --recursive; + clip_init(FALSE); + return; + } + + /* + * Set up system-wide default menus. + */ +#if defined(SYS_MENU_FILE) && defined(FEAT_MENU) + if (vim_strchr(p_go, GO_NOSYSMENU) == NULL) + { + sys_menu = TRUE; + do_source((char_u *)SYS_MENU_FILE, FALSE, FALSE); + sys_menu = FALSE; + } +#endif + + /* + * Switch on the mouse by default, unless the user changed it already. + * This can then be changed in the .gvimrc. + */ + if (!option_was_set((char_u *)"mouse")) + set_string_option_direct((char_u *)"mouse", -1, + (char_u *)"a", OPT_FREE); + + /* + * If -U option given, use only the initializations from that file and + * nothing else. Skip all initializations for "-U NONE" or "-u NORC". + */ + if (use_gvimrc != NULL) + { + if (STRCMP(use_gvimrc, "NONE") != 0 + && STRCMP(use_gvimrc, "NORC") != 0 + && do_source(use_gvimrc, FALSE, FALSE) != OK) + EMSG2(_("E230: Cannot read from \"%s\""), use_gvimrc); + } + else + { + /* + * Get system wide defaults for gvim, only when file name defined. + */ +#ifdef SYS_GVIMRC_FILE + do_source((char_u *)SYS_GVIMRC_FILE, FALSE, FALSE); +#endif + + /* + * Try to read GUI initialization commands from the following + * places: + * - environment variable GVIMINIT + * - the user gvimrc file (~/.gvimrc) + * - the second user gvimrc file ($VIM/.gvimrc for Dos) + * - the third user gvimrc file ($VIM/.gvimrc for Amiga) + * The first that exists is used, the rest is ignored. + */ + if (process_env((char_u *)"GVIMINIT", FALSE) == FAIL + && do_source((char_u *)USR_GVIMRC_FILE, TRUE, FALSE) == FAIL +#ifdef USR_GVIMRC_FILE2 + && do_source((char_u *)USR_GVIMRC_FILE2, TRUE, FALSE) == FAIL +#endif + ) + { +#ifdef USR_GVIMRC_FILE3 + (void)do_source((char_u *)USR_GVIMRC_FILE3, TRUE, FALSE); +#endif + } + + /* + * Read initialization commands from ".gvimrc" in current + * directory. This is only done if the 'exrc' option is set. + * Because of security reasons we disallow shell and write + * commands now, except for unix if the file is owned by the user + * or 'secure' option has been reset in environment of global + * ".gvimrc". + * Only do this if GVIMRC_FILE is not the same as USR_GVIMRC_FILE, + * USR_GVIMRC_FILE2, USR_GVIMRC_FILE3 or SYS_GVIMRC_FILE. + */ + if (p_exrc) + { +#ifdef UNIX + { + struct stat s; + + /* if ".gvimrc" file is not owned by user, set 'secure' + * mode */ + if (mch_stat(GVIMRC_FILE, &s) || s.st_uid != getuid()) + secure = p_secure; + } +#else + secure = p_secure; +#endif + + if ( fullpathcmp((char_u *)USR_GVIMRC_FILE, + (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME +#ifdef SYS_GVIMRC_FILE + && fullpathcmp((char_u *)SYS_GVIMRC_FILE, + (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME +#endif +#ifdef USR_GVIMRC_FILE2 + && fullpathcmp((char_u *)USR_GVIMRC_FILE2, + (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME +#endif +#ifdef USR_GVIMRC_FILE3 + && fullpathcmp((char_u *)USR_GVIMRC_FILE3, + (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME +#endif + ) + do_source((char_u *)GVIMRC_FILE, TRUE, FALSE); + + if (secure == 2) + need_wait_return = TRUE; + secure = 0; + } + } + + if (need_wait_return || msg_didany) + wait_return(TRUE); + + --recursive; + } + + /* If recursive call opened the shell, return here from the first call */ + if (gui.in_use) + return; + + /* + * Create the GUI shell. + */ + gui.in_use = TRUE; /* Must be set after menus have been set up */ + if (gui_mch_init() == FAIL) + goto error; + + /* Avoid a delay for an error message that was printed in the terminal + * where Vim was started. */ + emsg_on_display = FALSE; + msg_scrolled = 0; + need_wait_return = FALSE; + msg_didany = FALSE; + + /* + * Check validity of any generic resources that may have been loaded. + */ + if (gui.border_width < 0) + gui.border_width = 0; + + /* + * Set up the fonts. First use a font specified with "-fn" or "-font". + */ + if (font_argument != NULL) + set_option_value((char_u *)"gfn", 0L, (char_u *)font_argument, 0); + if ( +#ifdef FEAT_XFONTSET + (*p_guifontset == NUL + || gui_init_font(p_guifontset, TRUE) == FAIL) && +#endif + gui_init_font(*p_guifont == NUL ? hl_get_font_name() + : p_guifont, FALSE) == FAIL) + { + EMSG(_("E665: Cannot start GUI, no valid font found")); + goto error2; + } +#ifdef FEAT_MBYTE + if (gui_get_wide_font() == FAIL) + EMSG(_("E231: 'guifontwide' invalid")); +#endif + + gui.num_cols = Columns; + gui.num_rows = Rows; + gui_reset_scroll_region(); + + /* Create initial scrollbars */ + FOR_ALL_WINDOWS(wp) + { + gui_create_scrollbar(&wp->w_scrollbars[SBAR_LEFT], SBAR_LEFT, wp); + gui_create_scrollbar(&wp->w_scrollbars[SBAR_RIGHT], SBAR_RIGHT, wp); + } + gui_create_scrollbar(&gui.bottom_sbar, SBAR_BOTTOM, NULL); + +#ifdef FEAT_MENU + gui_create_initial_menus(root_menu); +#endif +#ifdef FEAT_SUN_WORKSHOP + if (usingSunWorkShop) + workshop_init(); +#endif +#ifdef FEAT_SIGN_ICONS + sign_gui_started(); +#endif + + /* Configure the desired menu and scrollbars */ + gui_init_which_components(NULL); + + /* All components of the GUI have been created now */ + gui.shell_created = TRUE; + +#ifndef FEAT_GUI_GTK + /* Set the shell size, adjusted for the screen size. For GTK this only + * works after the shell has been opened, thus it is further down. */ + gui_set_shellsize(FALSE, TRUE); +#endif +#if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) + /* Need to set the size of the menubar after all the menus have been + * created. */ + gui_mch_compute_menu_height((Widget)0); +#endif + + /* + * Actually open the GUI shell. + */ + if (gui_mch_open() != FAIL) + { +#ifdef FEAT_TITLE + maketitle(); + resettitle(); +#endif + init_gui_options(); +#ifdef FEAT_ARABIC + /* Our GUI can't do bidi. */ + p_tbidi = FALSE; +#endif +#ifdef FEAT_GUI_GTK + /* Give GTK+ a chance to put all widget's into place. */ + gui_mch_update(); + /* Now make sure the shell fits on the screen. */ + gui_set_shellsize(FALSE, TRUE); +#endif +#ifdef FEAT_NETBEANS_INTG + if (starting == 0 && usingNetbeans) + /* Tell the client that it can start sending commands. */ + netbeans_startup_done(); +#endif +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + if (!im_xim_isvalid_imactivate()) + EMSG(_("E599: Value of 'imactivatekey' is invalid")); +#endif + + return; + } + +error2: +#ifdef FEAT_GUI_X11 + /* undo gui_mch_init() */ + gui_mch_uninit(); +#endif + +error: + gui.in_use = FALSE; + clip_init(FALSE); +} + + + void +gui_exit(rc) + int rc; +{ +#ifndef __BEOS__ + /* don't free the fonts, it leads to a BUS error + * richard@whitequeen.com Jul 99 */ + free_highlight_fonts(); +#endif + gui.in_use = FALSE; + gui_mch_exit(rc); +} + +#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_PHOTON) || defined(FEAT_GUI_MAC) || defined(PROTO) +/* + * Called when the GUI shell is closed by the user. If there are no changed + * files Vim exits, otherwise there will be a dialog to ask the user what to + * do. + * When this function returns, Vim should NOT exit! + */ + void +gui_shell_closed() +{ + cmdmod_T save_cmdmod; + + save_cmdmod = cmdmod; + + /* Only exit when there are no changed files */ + exiting = TRUE; +# ifdef FEAT_BROWSE + cmdmod.browse = TRUE; +# endif +# if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + cmdmod.confirm = TRUE; +# endif + /* If there are changed buffers, present the user with a dialog if + * possible, otherwise give an error message. */ + if (!check_changed_any(FALSE)) + getout(0); + + exiting = FALSE; + cmdmod = save_cmdmod; + setcursor(); /* position cursor */ + out_flush(); +} +#endif + +/* + * Set the font. "font_list" is a a comma separated list of font names. The + * first font name that works is used. If none is found, use the default + * font. + * If "fontset" is TRUE, the "font_list" is used as one name for the fontset. + * Return OK when able to set the font. When it failed FAIL is returned and + * the fonts are unchanged. + */ +/*ARGSUSED*/ + int +gui_init_font(font_list, fontset) + char_u *font_list; + int fontset; +{ +#define FONTLEN 320 + char_u font_name[FONTLEN]; + int font_list_empty = FALSE; + int ret = FAIL; + + if (!gui.in_use) + return FAIL; + + font_name[0] = NUL; + if (*font_list == NUL) + font_list_empty = TRUE; + else + { +#ifdef FEAT_XFONTSET + /* When using a fontset, the whole list of fonts is one name. */ + if (fontset) + ret = gui_mch_init_font(font_list, TRUE); + else +#endif + while (*font_list != NUL) + { + /* Isolate one comma separated font name. */ + (void)copy_option_part(&font_list, font_name, FONTLEN, ","); + + /* Careful!!! The Win32 version of gui_mch_init_font(), when + * called with "*" will change p_guifont to the selected font + * name, which frees the old value. This makes font_list + * invalid. Thus when OK is returned here, font_list must no + * longer be used! */ + if (gui_mch_init_font(font_name, FALSE) == OK) + { +#if defined(FEAT_MBYTE) && !defined(HAVE_GTK2) + /* If it's a Unicode font, try setting 'guifontwide' to a + * similar double-width font. */ + if ((p_guifontwide == NULL || *p_guifontwide == NUL) + && strstr((char *)font_name, "10646") != NULL) + set_guifontwide(font_name); +#endif + ret = OK; + break; + } + } + } + + if (ret != OK + && STRCMP(font_list, "*") != 0 + && (font_list_empty || gui.norm_font == NOFONT)) + { + /* + * Couldn't load any font in 'font_list', keep the current font if + * there is one. If 'font_list' is empty, or if there is no current + * font, tell gui_mch_init_font() to try to find a font we can load. + */ + ret = gui_mch_init_font(NULL, FALSE); + } + + if (ret == OK) + { +#ifndef HAVE_GTK2 + /* Set normal font as current font */ +# ifdef FEAT_XFONTSET + if (gui.fontset != NOFONTSET) + gui_mch_set_fontset(gui.fontset); + else +# endif + gui_mch_set_font(gui.norm_font); +#endif + gui_set_shellsize(FALSE, +#ifdef MSWIN + TRUE +#else + FALSE +#endif + ); + } + + return ret; +} + +#if defined(FEAT_MBYTE) || defined(PROTO) +# ifndef HAVE_GTK2 +/* + * Try setting 'guifontwide' to a font twice as wide as "name". + */ + static void +set_guifontwide(name) + char_u *name; +{ + int i = 0; + char_u wide_name[FONTLEN + 10]; /* room for 2 * width and '*' */ + char_u *wp = NULL; + char_u *p; + GuiFont font; + + wp = wide_name; + for (p = name; *p != NUL; ++p) + { + *wp++ = *p; + if (*p == '-') + { + ++i; + if (i == 6) /* font type: change "--" to "-*-" */ + { + if (p[1] == '-') + *wp++ = '*'; + } + else if (i == 12) /* found the width */ + { + ++p; + i = getdigits(&p); + if (i != 0) + { + /* Double the width specification. */ + sprintf((char *)wp, "%d%s", i * 2, p); + font = gui_mch_get_font(wide_name, FALSE); + if (font != NOFONT) + { + gui.wide_font = font; + set_string_option_direct((char_u *)"gfw", -1, + wide_name, OPT_FREE); + } + } + break; + } + } + } +} +# endif /* !HAVE_GTK2 */ + +/* + * Get the font for 'guifontwide'. + * Return FAIL for an invalid font name. + */ + int +gui_get_wide_font() +{ + GuiFont font = NOFONT; + char_u font_name[FONTLEN]; + char_u *p; + + if (!gui.in_use) /* Can't allocate font yet, assume it's OK. */ + return OK; /* Will give an error message later. */ + + if (p_guifontwide != NULL && *p_guifontwide != NUL) + { + for (p = p_guifontwide; *p != NUL; ) + { + /* Isolate one comma separated font name. */ + (void)copy_option_part(&p, font_name, FONTLEN, ","); + font = gui_mch_get_font(font_name, FALSE); + if (font != NOFONT) + break; + } + if (font == NOFONT) + return FAIL; + } + + gui_mch_free_font(gui.wide_font); +#ifdef HAVE_GTK2 + /* Avoid unnecessary overhead if 'guifontwide' is equal to 'guifont'. */ + if (font != NOFONT && gui.norm_font != NOFONT + && pango_font_description_equal(font, gui.norm_font)) + { + gui.wide_font = NOFONT; + gui_mch_free_font(font); + } + else +#endif + gui.wide_font = font; + return OK; +} +#endif + + void +gui_set_cursor(row, col) + int row; + int col; +{ + gui.row = row; + gui.col = col; +} + +/* + * gui_check_pos - check if the cursor is on the screen. + */ + static void +gui_check_pos() +{ + if (gui.row >= screen_Rows) + gui.row = screen_Rows - 1; + if (gui.col >= screen_Columns) + gui.col = screen_Columns - 1; + if (gui.cursor_row >= screen_Rows || gui.cursor_col >= screen_Columns) + gui.cursor_is_valid = FALSE; +} + +/* + * Redraw the cursor if necessary or when forced. + * Careful: The contents of ScreenLines[] must match what is on the screen, + * otherwise this goes wrong. May need to call out_flush() first. + */ + void +gui_update_cursor(force, clear_selection) + int force; /* when TRUE, update even when not moved */ + int clear_selection;/* clear selection under cursor */ +{ + int cur_width = 0; + int cur_height = 0; + int old_hl_mask; + int idx; + int id; + guicolor_T cfg, cbg, cc; /* cursor fore-/background color */ + int cattr; /* cursor attributes */ + int attr; + attrentry_T *aep = NULL; + + /* Don't update the cursor when halfway busy scrolling. + * ScreenLines[] isn't valid then. */ + if (!can_update_cursor) + return; + + gui_check_pos(); + if (!gui.cursor_is_valid || force + || gui.row != gui.cursor_row || gui.col != gui.cursor_col) + { + gui_undraw_cursor(); + if (gui.row < 0) + return; +#ifdef USE_IM_CONTROL + if (gui.row != gui.cursor_row || gui.col != gui.cursor_col) + im_set_position(gui.row, gui.col); +#endif + gui.cursor_row = gui.row; + gui.cursor_col = gui.col; + + /* Only write to the screen after ScreenLines[] has been initialized */ + if (!screen_cleared || ScreenLines == NULL) + return; + + /* Clear the selection if we are about to write over it */ + if (clear_selection) + clip_may_clear_selection(gui.row, gui.row); + /* Check that the cursor is inside the shell (resizing may have made + * it invalid) */ + if (gui.row >= screen_Rows || gui.col >= screen_Columns) + return; + + gui.cursor_is_valid = TRUE; + + /* + * How the cursor is drawn depends on the current mode. + */ + idx = get_shape_idx(FALSE); + if (State & LANGMAP) + id = shape_table[idx].id_lm; + else + id = shape_table[idx].id; + + /* get the colors and attributes for the cursor. Default is inverted */ + cfg = INVALCOLOR; + cbg = INVALCOLOR; + cattr = HL_INVERSE; + gui_mch_set_blinking(shape_table[idx].blinkwait, + shape_table[idx].blinkon, + shape_table[idx].blinkoff); + if (id > 0) + { + cattr = syn_id2colors(id, &cfg, &cbg); +#if defined(USE_IM_CONTROL) || defined(FEAT_HANGULIN) + { + static int iid; + guicolor_T fg, bg; + + if (im_get_status()) + { + iid = syn_name2id((char_u *)"CursorIM"); + if (iid > 0) + { + syn_id2colors(iid, &fg, &bg); + if (bg != INVALCOLOR) + cbg = bg; + if (fg != INVALCOLOR) + cfg = fg; + } + } + } +#endif + } + + /* + * Get the attributes for the character under the cursor. + * When no cursor color was given, use the character color. + */ + attr = ScreenAttrs[LineOffset[gui.row] + gui.col]; + if (attr > HL_ALL) + aep = syn_gui_attr2entry(attr); + if (aep != NULL) + { + attr = aep->ae_attr; + if (cfg == INVALCOLOR) + cfg = ((attr & HL_INVERSE) ? aep->ae_u.gui.bg_color + : aep->ae_u.gui.fg_color); + if (cbg == INVALCOLOR) + cbg = ((attr & HL_INVERSE) ? aep->ae_u.gui.fg_color + : aep->ae_u.gui.bg_color); + } + if (cfg == INVALCOLOR) + cfg = (attr & HL_INVERSE) ? gui.back_pixel : gui.norm_pixel; + if (cbg == INVALCOLOR) + cbg = (attr & HL_INVERSE) ? gui.norm_pixel : gui.back_pixel; + +#ifdef FEAT_XIM + if (aep != NULL) + { + xim_bg_color = ((attr & HL_INVERSE) ? aep->ae_u.gui.fg_color + : aep->ae_u.gui.bg_color); + xim_fg_color = ((attr & HL_INVERSE) ? aep->ae_u.gui.bg_color + : aep->ae_u.gui.fg_color); + if (xim_bg_color == INVALCOLOR) + xim_bg_color = (attr & HL_INVERSE) ? gui.norm_pixel + : gui.back_pixel; + if (xim_fg_color == INVALCOLOR) + xim_fg_color = (attr & HL_INVERSE) ? gui.back_pixel + : gui.norm_pixel; + } + else + { + xim_bg_color = (attr & HL_INVERSE) ? gui.norm_pixel + : gui.back_pixel; + xim_fg_color = (attr & HL_INVERSE) ? gui.back_pixel + : gui.norm_pixel; + } +#endif + + attr &= ~HL_INVERSE; + if (cattr & HL_INVERSE) + { + cc = cbg; + cbg = cfg; + cfg = cc; + } + cattr &= ~HL_INVERSE; + + /* + * When we don't have window focus, draw a hollow cursor. + */ + if (!gui.in_focus) + { + gui_mch_draw_hollow_cursor(cbg); + return; + } + + old_hl_mask = gui.highlight_mask; + if (shape_table[idx].shape == SHAPE_BLOCK +#ifdef FEAT_HANGULIN + || composing_hangul +#endif + ) + { + /* + * Draw the text character with the cursor colors. Use the + * character attributes plus the cursor attributes. + */ + gui.highlight_mask = (cattr | attr); +#ifdef FEAT_HANGULIN + if (composing_hangul) + (void)gui_outstr_nowrap(composing_hangul_buffer, 2, + GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, cfg, cbg, 0); + else +#endif + (void)gui_screenchar(LineOffset[gui.row] + gui.col, + GUI_MON_IS_CURSOR | GUI_MON_NOCLEAR, cfg, cbg, 0); + } + else + { +#if defined(FEAT_MBYTE) && defined(FEAT_RIGHTLEFT) + int col_off = FALSE; +#endif + /* + * First draw the partial cursor, then overwrite with the text + * character, using a transparent background. + */ + if (shape_table[idx].shape == SHAPE_VER) + { + cur_height = gui.char_height; + cur_width = (gui.char_width * shape_table[idx].percentage + + 99) / 100; + } + else + { + cur_height = (gui.char_height * shape_table[idx].percentage + + 99) / 100; + cur_width = gui.char_width; + } +#ifdef FEAT_MBYTE + if (has_mbyte && (*mb_off2cells)(LineOffset[gui.row] + gui.col) > 1) + { + /* Double wide character. */ + if (shape_table[idx].shape != SHAPE_VER) + cur_width += gui.char_width; +# ifdef FEAT_RIGHTLEFT + if (CURSOR_BAR_RIGHT) + { + /* gui.col points to the left halve of the character but + * the vertical line needs to be on the right halve. + * A double-wide horizontal line is also drawn from the + * right halve in gui_mch_draw_part_cursor(). */ + col_off = TRUE; + ++gui.col; + } +# endif + } +#endif + gui_mch_draw_part_cursor(cur_width, cur_height, cbg); +#if defined(FEAT_MBYTE) && defined(FEAT_RIGHTLEFT) + if (col_off) + --gui.col; +#endif + +#ifndef FEAT_GUI_MSWIN /* doesn't seem to work for MSWindows */ + gui.highlight_mask = ScreenAttrs[LineOffset[gui.row] + gui.col]; + (void)gui_screenchar(LineOffset[gui.row] + gui.col, + GUI_MON_TRS_CURSOR | GUI_MON_NOCLEAR, + (guicolor_T)0, (guicolor_T)0, 0); +#endif + } + gui.highlight_mask = old_hl_mask; + } +} + +#if defined(FEAT_MENU) || defined(PROTO) + void +gui_position_menu() +{ +# if !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) + if (gui.menu_is_active && gui.in_use) + gui_mch_set_menu_pos(0, 0, gui.menu_width, gui.menu_height); +# endif +} +#endif + +/* + * Position the various GUI components (text area, menu). The vertical + * scrollbars are NOT handled here. See gui_update_scrollbars(). + */ +/*ARGSUSED*/ + static void +gui_position_components(total_width) + int total_width; +{ + int text_area_x; + int text_area_y; + int text_area_width; + int text_area_height; + + /* avoid that moving components around generates events */ + ++hold_gui_events; + + text_area_x = 0; + if (gui.which_scrollbars[SBAR_LEFT]) + text_area_x += gui.scrollbar_width; + + text_area_y = 0; +#if defined(FEAT_MENU) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON)) + gui.menu_width = total_width; + if (gui.menu_is_active) + text_area_y += gui.menu_height; +#endif +#if defined(FEAT_TOOLBAR) && defined(FEAT_GUI_MSWIN) + if (vim_strchr(p_go, GO_TOOLBAR) != NULL) + text_area_y = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; +#endif + +#if defined(FEAT_TOOLBAR) && (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) + if (vim_strchr(p_go, GO_TOOLBAR) != NULL) + { +# ifdef FEAT_GUI_ATHENA + gui_mch_set_toolbar_pos(0, text_area_y, + gui.menu_width, gui.toolbar_height); +# endif + text_area_y += gui.toolbar_height; + } +#endif + + text_area_width = gui.num_cols * gui.char_width + gui.border_offset * 2; + text_area_height = gui.num_rows * gui.char_height + gui.border_offset * 2; + + gui_mch_set_text_area_pos(text_area_x, + text_area_y, + text_area_width, + text_area_height +#if defined(FEAT_XIM) && !defined(HAVE_GTK2) + + xim_get_status_area_height() +#endif + ); +#ifdef FEAT_MENU + gui_position_menu(); +#endif + if (gui.which_scrollbars[SBAR_BOTTOM]) + gui_mch_set_scrollbar_pos(&gui.bottom_sbar, + text_area_x, + text_area_y + text_area_height, + text_area_width, + gui.scrollbar_height); + gui.left_sbar_x = 0; + gui.right_sbar_x = text_area_x + text_area_width; + + --hold_gui_events; +} + + int +gui_get_base_width() +{ + int base_width; + + base_width = 2 * gui.border_offset; + if (gui.which_scrollbars[SBAR_LEFT]) + base_width += gui.scrollbar_width; + if (gui.which_scrollbars[SBAR_RIGHT]) + base_width += gui.scrollbar_width; + return base_width; +} + + int +gui_get_base_height() +{ + int base_height; + + base_height = 2 * gui.border_offset; + if (gui.which_scrollbars[SBAR_BOTTOM]) + base_height += gui.scrollbar_height; +#ifdef FEAT_GUI_GTK + /* We can't take the sizes properly into account until anything is + * realized. Therefore we recalculate all the values here just before + * setting the size. (--mdcki) */ +#else +# ifdef FEAT_MENU + if (gui.menu_is_active) + base_height += gui.menu_height; +# endif +# ifdef FEAT_TOOLBAR + if (vim_strchr(p_go, GO_TOOLBAR) != NULL) +# if defined(FEAT_GUI_MSWIN) && defined(FEAT_TOOLBAR) + base_height += (TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT); +# else + base_height += gui.toolbar_height; +# endif +# endif +# ifdef FEAT_FOOTER + if (vim_strchr(p_go, GO_FOOTER) != NULL) + base_height += gui.footer_height; +# endif +# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) + base_height += gui_mch_text_area_extra_height(); +# endif +#endif + return base_height; +} + +/* + * Should be called after the GUI shell has been resized. Its arguments are + * the new width and height of the shell in pixels. + */ + void +gui_resize_shell(pixel_width, pixel_height) + int pixel_width; + int pixel_height; +{ + static int busy = FALSE; + + if (!gui.shell_created) /* ignore when still initializing */ + return; + + /* + * Can't resize the screen while it is being redrawn. Remember the new + * size and handle it later. + */ + if (updating_screen || busy) + { + new_pixel_width = pixel_width; + new_pixel_height = pixel_height; + return; + } + +again: + busy = TRUE; + +#ifdef FEAT_GUI_BEOS + vim_lock_screen(); +#endif + + /* Flush pending output before redrawing */ + out_flush(); |