summaryrefslogtreecommitdiffstats
path: root/src/gui.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui.c')
-rw-r--r--src/gui.c4740
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();