📄 gui.c
字号:
/* 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.
*/
#include "vim.h"
/* Structure containing all the GUI information */
Gui gui;
/* Set to TRUE after adding/removing menus to ensure they are updated */
int force_menu_update = FALSE;
static void gui_check_screen __ARGS((void));
static void gui_position_components __ARGS((int, int));
static void gui_outstr __ARGS((char_u *, int));
static void gui_delete_lines __ARGS((int row, int count));
static void gui_insert_lines __ARGS((int row, int count));
static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
static char_u *popup_mode_name __ARGS((char_u *name, int idx));
static void gui_update_menus_recurse __ARGS((GuiMenu *, int));
#ifdef USE_GUI_WIN32
static int gui_add_menu_path __ARGS((char_u *, int, int *, void (*)(), char_u *, int, int));
#else
static int gui_add_menu_path __ARGS((char_u *, int, int *, void (*)(), char_u *, int));
#endif
static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int, int silent));
static void gui_free_menu __ARGS((GuiMenu *));
static void gui_free_menu_string __ARGS((GuiMenu *, int));
static int gui_show_menus __ARGS((char_u *, int));
static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
static int menu_name_equal __ARGS((char_u *name, GuiMenu *menu));
static int menu_namecmp __ARGS((char_u *name, char_u *mname));
static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
static void gui_update_scrollbars __ARGS((int));
static void gui_update_horiz_scrollbar __ARGS((int));
static WIN *y2win __ARGS((int y));
static int get_menu_mode __ARGS((void));
#ifdef USE_GUI_WIN32
static void gui_create_tearoffs_recurse __ARGS((GuiMenu *menu, const char_u *pname, int *pri_tab, int pri_idx));
static void gui_add_tearoff __ARGS((char_u *tearpath, int *pri_tab, int pri_idx));
static void gui_destroy_tearoffs_recurse __ARGS((GuiMenu *menu));
static int s_tearoffs = FALSE;
#endif
/*
* 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(USE_GUI_ATHENA) || defined(macintosh)
# define SCROLL_PAST_END
#endif
/*
* While defining the system menu, gui_sys_menu is TRUE. This avoids
* overruling of menus that the user already defined.
*/
static int gui_sys_menu = FALSE;
/*
* gui_start -- Called when user wants to start the GUI.
*/
void
gui_start()
{
char_u *old_term;
#if defined(UNIX) && !defined(__BEOS__)
pid_t pid;
#endif
old_term = vim_strsave(T_NAME);
mch_setmouse(FALSE); /* first switch mouse off */
/*
* 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 window 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 window 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;
termcapinit((char_u *)"builtin_gui");
gui.starting = FALSE;
if (!gui.in_use) /* failed to start GUI */
{
termcapinit(old_term); /* back to old term settings */
settmode(TMODE_RAW); /* restart RAW mode */
set_title_defaults(); /* set 'title' and 'icon' again */
}
vim_free(old_term);
#if defined(UNIX) && !defined(__BEOS__)
/*
* 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 && gui.dofork && vim_strchr(p_guioptions, GO_FORG) == NULL)
{
pid = fork();
if (pid > 0) /* Parent */
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
}
#endif
}
/*
* Call this when vim starts up, whether or not the GUI is started
*/
void
gui_prepare(argc, argv)
int *argc;
char **argv;
{
/* Menu items may be added before the GUI is started, so set this now */
gui.root_menu = NULL;
gui.in_use = FALSE; /* No GUI yet (maybe later) */
gui.starting = FALSE; /* No GUI yet (maybe later) */
gui.dofork = TRUE; /* default is to use fork() */
gui_mch_prepare(argc, argv);
}
/*
* This is the call which starts the GUI.
*/
void
gui_init()
{
WIN *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 window to be opened halfway reading a
* gvimrc file.
*/
if (!recursive)
{
++recursive;
gui.window_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 = gui.num_cols = 0;
gui.row = gui.num_rows = 0;
gui.cursor_is_valid = FALSE;
gui.scroll_region_top = 0;
gui.scroll_region_bot = Rows - 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 = (GuiFont)NULL;
gui.bold_font = (GuiFont)NULL;
gui.ital_font = (GuiFont)NULL;
gui.boldital_font = (GuiFont)NULL;
clip_init(TRUE);
gui.menu_is_active = TRUE; /* default: include menu */
gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH;
gui.menu_height = MENU_DEFAULT_HEIGHT;
gui.menu_width = 0;
gui.prev_wrap = -1;
/*
* Set up system-wide default menus.
*/
#ifdef SYS_MENU_FILE
gui_sys_menu = TRUE;
do_source((char_u *)SYS_MENU_FILE, FALSE, FALSE);
gui_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", TRUE);
/*
* If -U option given, use only the initializations from that file and
* nothing else. Skip all initializations for "-U NONE".
*/
if (use_gvimrc != NULL)
{
if (STRCMP(use_gvimrc, "NONE") != 0
&& do_source(use_gvimrc, FALSE, FALSE) != OK)
EMSG2("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 first that exists is used, the rest is ignored.
*/
if (process_env((char_u *)"GVIMINIT") == FAIL
&& do_source((char_u *)USR_GVIMRC_FILE, TRUE, FALSE) == FAIL)
{
#ifdef USR_GVIMRC_FILE2
(void)do_source((char_u *)USR_GVIMRC_FILE2, 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 or SYS_GVIMRC_FILE.
*/
if (p_exrc)
{
#ifdef UNIX
{
struct stat s;
/* if ".gvimrc" file is not owned by user, set 'secure'
* mode */
if (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
)
do_source((char_u *)GVIMRC_FILE, FALSE, 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 window, return here from the first call */
if (gui.in_use)
return;
/*
* Create the GUI windows ready for opening.
*/
gui.in_use = TRUE; /* Must be set after menus have been set up */
if (gui_mch_init() == FAIL)
goto error;
/*
* Check validity of any generic resources that may have been loaded.
*/
if (gui.border_width < 0)
gui.border_width = 0;
/*
* Set up the fonts.
*/
if (font_opt)
set_option_value((char_u *)"gfn", 0L, (char_u *)font_opt);
if (gui_init_font(p_guifont) == FAIL)
goto error;
gui.num_cols = Columns;
gui.num_rows = Rows;
gui_reset_scroll_region();
/* Create initial scrollbars */
for (wp = firstwin; wp; wp = wp->w_next)
{
gui_create_scrollbar(&wp->w_scrollbars[SBAR_LEFT], wp);
gui_create_scrollbar(&wp->w_scrollbars[SBAR_RIGHT], wp);
}
gui_create_scrollbar(&gui.bottom_sbar, NULL);
gui_create_initial_menus(gui.root_menu, NULL);
/* Configure the desired menu and scrollbars */
gui_init_which_components(NULL);
/* All components of the window have been created now */
gui.window_created = TRUE;
gui_set_winsize(TRUE);
/*
* Actually open the GUI window.
*/
if (gui_mch_open() != FAIL)
{
maketitle();
init_gui_options();
return;
}
error:
gui.in_use = FALSE;
clip_init(FALSE);
}
void
gui_exit(rc)
int rc;
{
free_highlight_fonts();
gui.in_use = FALSE;
gui_mch_exit(rc);
}
/*
* Set the font. Uses the 'font' option. The first font name that works is
* used. If none is found, use the default font.
* Return OK when able to set the font.
*/
int
gui_init_font(font_list)
char_u *font_list;
{
#define FONTLEN 100
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
while (*font_list != NUL)
{
/* Isolate one font name */
(void)copy_option_part(&font_list, font_name, FONTLEN, ",");
if (gui_mch_init_font(font_name) == OK)
{
ret = OK;
break;
}
}
if (ret != OK && STRCMP(font_name, "*") != 0
&& (font_list_empty || gui.norm_font == (GuiFont)0))
{
/*
* 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);
}
if (ret == OK)
{
/* Set normal font as current font */
gui_mch_set_font(gui.norm_font);
gui_set_winsize(
#ifdef WIN32
TRUE
#else
FALSE
#endif
);
}
return ret;
}
void
gui_set_cursor(row, col)
int row;
int col;
{
gui.row = row;
gui.col = col;
}
/*
* gui_check_screen - check if the cursor is on the screen.
*/
static void
gui_check_screen()
{
if (gui.row >= Rows)
gui.row = Rows - 1;
if (gui.col >= Columns)
gui.col = Columns - 1;
if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
gui.cursor_is_valid = FALSE;
}
/*
* Redraw the cursor if necessary or when forced.
* Careful: The contents of LinePointers[] 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;
long_u old_hl_mask;
int idx;
int id;
GuiColor cfg, cbg, cc; /* cursor fore-/background color */
int cattr; /* cursor attributes */
int attr;
struct attr_entry *aep = NULL;
gui_check_screen();
if (!gui.cursor_is_valid || force
|| gui.row != gui.cursor_row || gui.col != gui.cursor_col)
{
gui_undraw_cursor();
if (gui.row <0)
return;
gui.cursor_row = gui.row;
gui.cursor_col = gui.col;
gui.cursor_is_valid = TRUE;
/* Only write to the screen after LinePointers[] has been initialized */
if (!screen_cleared || NextScreen == 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 window (resizing may have made
* it invalid) */
if (gui.row >= screen_Rows || gui.col >= screen_Columns)
return;
/*
* How the cursor is drawn depends on the current mode.
*/
idx = get_cursor_idx();
id = cursor_table[idx].id;
/* get the colors and attributes for the cursor. Default is inverted */
cfg = (GuiColor)-1;
cbg = (GuiColor)-1;
cattr = HL_INVERSE;
gui_mch_set_blinking(cursor_table[idx].blinkwait,
cursor_table[idx].blinkon,
cursor_table[idx].blinkoff);
if (id > 0)
{
cattr = syn_id2colors(id, &cfg, &cbg);
--cbg;
--cfg;
}
/*
* Get the attributes for the character under the cursor.
* When no cursor color was given, use the character color.
*/
attr = *(LinePointers[gui.row] + gui.col + screen_Columns);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -