📄 ui.c
字号:
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* ui.c: functions that handle the user interface.
* 1. Keyboard input stuff, and a bit of windowing stuff. These are called
* before the machine specific stuff (mch_*) so that we can call the GUI
* stuff instead if the GUI is running.
* 2. Clipboard stuff.
* 3. Input buffer stuff.
*/
#include "vim.h"
void
ui_write(s, len)
char_u *s;
int len;
{
#ifdef USE_GUI
if (gui.in_use && !gui.dying)
{
gui_write(s, len);
if (p_wd)
gui_wait_for_chars(p_wd);
return;
}
#endif
#ifndef NO_CONSOLE
/* Don't output anything in silent mode ("ex -s") */
if (!silent_mode)
mch_write(s, len);
#endif
}
/*
* ui_inchar(): low level input funcion.
* Get a characters from the keyboard.
* Return the number of characters that are available.
* If wtime == 0 do not wait for characters.
* If wtime == -1 wait forever for characters.
* If wtime > 0 wait wtime milliseconds for a character.
*/
int
ui_inchar(buf, maxlen, wtime)
char_u *buf;
int maxlen;
long wtime; /* don't use "time", MIPS cannot handle it */
{
#ifdef NO_CONSOLE
/* Don't wait for character input when the window hasn't been opened yet.
* Must return something, otherwise we'll loop forever. */
if (!gui.in_use || gui.starting)
{
buf[0] = CR;
return 1;
}
#endif
#ifdef USE_GUI
if (gui.in_use)
{
if (!gui_wait_for_chars(wtime))
return 0;
return read_from_input_buf(buf, (long)maxlen);
}
#endif
#ifndef NO_CONSOLE
return mch_inchar(buf, maxlen, wtime);
#endif
}
/*
* return non-zero if a character is available
*/
int
ui_char_avail()
{
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_update();
return !vim_is_input_buf_empty();
}
#endif
#ifndef NO_CONSOLE
return mch_char_avail();
#else
return 0;
#endif
}
/*
* Delay for the given number of milliseconds. If ignoreinput is FALSE then we
* cancel the delay if a key is hit.
*/
void
ui_delay(msec, ignoreinput)
long msec;
int ignoreinput;
{
#ifdef USE_GUI
if (gui.in_use && !ignoreinput)
gui_wait_for_chars(msec);
else
#endif
mch_delay(msec, ignoreinput);
}
/*
* If the machine has job control, use it to suspend the program,
* otherwise fake it by starting a new shell.
* When running the GUI iconify the window.
*/
void
ui_suspend()
{
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_iconify();
return;
}
#endif
mch_suspend();
}
/*
* When the OS can't really suspend, call this function to start a shell.
*/
void
suspend_shell()
{
MSG_PUTS("new shell started\n");
(void)mch_call_shell(NULL, SHELL_COOKED);
need_check_timestamps = TRUE;
}
int
ui_can_restore_title()
{
#ifdef USE_GUI
/*
* If GUI is (going to be) used, we can always set the window title.
* Saves a bit of time, because the X11 display server does not need to be
* contacted.
*/
if (gui.starting || gui.in_use)
return TRUE;
#endif
return mch_can_restore_title();
}
int
ui_can_restore_icon()
{
#ifdef USE_GUI
/*
* If GUI is (going to be) used, we can always set the icon name.
* Saves a bit of time, because the X11 display server does not need to be
* contacted.
*/
if (gui.starting || gui.in_use)
return TRUE;
#endif
return mch_can_restore_icon();
}
/*
* Try to get the current window size. Put the result in Rows and Columns.
* Return OK when size could be determined, FAIL otherwise.
*/
int
ui_get_winsize()
{
int retval;
#ifdef USE_GUI
if (gui.in_use)
retval = gui_get_winsize();
else
#endif
retval = mch_get_winsize();
/* adjust the default for 'lines' and 'columns' */
if (retval == OK)
{
set_number_default("lines", Rows);
set_number_default("columns", Columns);
}
return retval;
}
/*
* Set the size of the window according to Rows and Columns, if possible.
*/
void
ui_set_winsize()
{
#ifdef USE_GUI
if (gui.in_use)
gui_set_winsize(
#ifdef WIN32
TRUE
#else
FALSE
#endif
);
else
#endif
mch_set_winsize();
}
void
ui_breakcheck()
{
#ifdef USE_GUI
if (gui.in_use)
gui_mch_update();
else
#endif /* USE_GUI */
mch_breakcheck();
}
/*****************************************************************************
* Functions for copying and pasting text between applications.
* This is always included in a GUI version, but may also be included when the
* clipboard and mouse is available to a terminal version such as xterm.
* Note: there are some more functions in ops.c that handle selection stuff.
*/
#ifdef USE_CLIPBOARD
static void clip_invert_area __ARGS((int, int, int, int));
static void clip_yank_non_visual_selection __ARGS((int, int, int, int));
static void clip_get_word_boundaries __ARGS((VimClipboard *, int, int));
static int clip_get_line_end __ARGS((int));
static void clip_update_non_visual_selection __ARGS((VimClipboard *, int, int,
int, int));
#define char_class(c) (c <= ' ' ? ' ' : vim_iswordc(c))
/*
* Selection stuff using Visual mode, for cutting and pasting text to other
* windows.
*/
/*
* Call this to initialise the clipboard. Pass it FALSE if the clipboard code
* is included, but the clipboard can not be used, or TRUE if the clipboard can
* be used. Eg unix may call this with FALSE, then call it again with TRUE if
* the GUI starts.
*/
void
clip_init(can_use)
int can_use;
{
clipboard.available = can_use;
clipboard.owned = FALSE;
clipboard.start.lnum = 0;
clipboard.start.col = 0;
clipboard.end.lnum = 0;
clipboard.end.col = 0;
clipboard.state = SELECT_CLEARED;
}
/*
* Check whether the VIsual area has changed, and if so try to become the owner
* of the selection, and free any old converted selection we may still have
* lying around. If the VIsual mode has ended, make a copy of what was
* selected so we can still give it to others. Will probably have to make sure
* this is called whenever VIsual mode is ended.
*/
void
clip_update_selection()
{
FPOS start, end;
/* If visual mode is only due to a redo command ("."), then ignore it */
if (!redo_VIsual_busy && VIsual_active)
{
if (lt(VIsual, curwin->w_cursor))
{
start = VIsual;
end = curwin->w_cursor;
}
else
{
start = curwin->w_cursor;
end = VIsual;
}
if (!equal(clipboard.start, start) || !equal(clipboard.end, end)
|| clipboard.vmode != VIsual_mode)
{
clip_clear_selection();
clipboard.start = start;
clipboard.end = end;
clipboard.vmode = VIsual_mode;
clip_free_selection();
clip_own_selection();
clip_mch_set_selection();
}
}
}
void
clip_own_selection()
{
/*
* Also want to check somehow that we are reading from the keyboard rather
* than a mapping etc.
*/
if (!clipboard.owned)
clipboard.owned = (clip_mch_own_selection() == OK);
}
void
clip_lose_selection()
{
clip_free_selection();
clipboard.owned = FALSE;
clip_clear_selection();
clip_mch_lose_selection();
}
void
clip_copy_selection()
{
if (VIsual_active)
{
if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
clip_update_selection();
clip_free_selection();
clip_own_selection();
if (clipboard.owned)
clip_get_selection();
clip_mch_set_selection();
}
}
void
clip_auto_select()
{
if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
clip_copy_selection();
}
#ifdef USE_GUI
/*
* Stuff for general mouse selection, without using Visual mode.
*/
static int clip_compare_pos __ARGS((int row1, int col1, int row2, int col2));
/*
* Compare two screen positions ala strcmp()
*/
static int
clip_compare_pos(row1, col1, row2, col2)
int row1;
int col1;
int row2;
int col2;
{
if (row1 > row2) return( 1);
if (row1 < row2) return(-1);
if (col1 > col2) return( 1);
if (col1 < col2) return(-1);
return( 0);
}
/*
* Start out the selection
*/
/* ARGSUSED */
void
clip_start_selection(button, x, y, repeated_click, modifiers)
int button;
int x;
int y;
int repeated_click;
int_u modifiers;
{
VimClipboard *cb = &clipboard;
if (cb->state == SELECT_DONE)
clip_clear_selection();
cb->start.lnum = check_row(Y_2_ROW(y));
cb->start.col = check_col(X_2_COL(x));
cb->end = cb->start;
cb->origin_row = (short_u)cb->start.lnum;
cb->state = SELECT_IN_PROGRESS;
if (repeated_click)
{
if (++(cb->mode) > SELECT_MODE_LINE)
cb->mode = SELECT_MODE_CHAR;
}
else
cb->mode = SELECT_MODE_CHAR;
#ifdef USE_GUI
/* clear the cursor until the selection is made */
gui_undraw_cursor();
#endif
switch (cb->mode)
{
case SELECT_MODE_CHAR:
cb->origin_start_col = cb->start.col;
cb->word_end_col = clip_get_line_end((int)cb->start.lnum);
break;
case SELECT_MODE_WORD:
clip_get_word_boundaries(cb, (int)cb->start.lnum, cb->start.col);
cb->origin_start_col = cb->word_start_col;
cb->origin_end_col = cb->word_end_col;
clip_invert_area((int)cb->start.lnum, cb->word_start_col,
(int)cb->end.lnum, cb->word_end_col);
cb->start.col = cb->word_start_col;
cb->end.col = cb->word_end_col;
break;
case SELECT_MODE_LINE:
clip_invert_area((int)cb->start.lnum, 0, (int)cb->start.lnum,
(int)Columns);
cb->start.col = 0;
cb->end.col = Columns;
break;
}
cb->prev = cb->start;
#ifdef DEBUG_SELECTION
printf("Selection started at (%u,%u)\n", cb->start.lnum, cb->start.col);
#endif
}
/*
* Continue processing the selection
*/
/* ARGSUSED */
void
clip_process_selection(button, x, y, repeated_click, modifiers)
int button;
int x;
int y;
int repeated_click;
int_u modifiers;
{
VimClipboard *cb = &clipboard;
int row;
int_u col;
int diff;
if (button == MOUSE_RELEASE)
{
/* Check to make sure we have something selected */
if (cb->start.lnum == cb->end.lnum && cb->start.col == cb->end.col)
{
#ifdef USE_GUI
if (gui.in_use)
gui_update_cursor(FALSE, FALSE);
#endif
cb->state = SELECT_CLEARED;
return;
}
#ifdef DEBUG_SELECTION
printf("Selection ended: (%u,%u) to (%u,%u)\n", cb->start.lnum,
cb->start.col, cb->end.lnum, cb->end.col);
#endif
clip_free_selection();
clip_own_selection();
clip_yank_non_visual_selection((int)cb->start.lnum, cb->start.col,
(int)cb->end.lnum, cb->end.col);
clip_mch_set_selection();
#ifdef USE_GUI
if (gui.in_use)
gui_update_cursor(FALSE, FALSE);
#endif
cb->state = SELECT_DONE;
return;
}
row = check_row(Y_2_ROW(y));
col = check_col(X_2_COL(x));
if (col == cb->prev.col && row == cb->prev.lnum)
return;
/*
* When extending the selection with the right mouse button, swap the
* start and end if the position is before half the selection
*/
if (cb->state == SELECT_DONE && button == MOUSE_RIGHT)
{
/*
* If the click is before the start, or the click is inside the
* selection and the start is the closest side, set the origin to the
* end of the selection.
*/
if (clip_compare_pos(row, col, (int)cb->start.lnum, cb->start.col) < 0
|| (clip_compare_pos(row, col,
(int)cb->end.lnum, cb->end.col) < 0
&& (((cb->start.lnum == cb->end.lnum
&& cb->end.col - col > col - cb->start.col))
|| ((diff = (cb->end.lnum - row) -
(row - cb->start.lnum)) > 0
|| (diff == 0 && col < (cb->start.col +
cb->end.col) / 2)))))
{
cb->origin_row = (short_u)cb->end.lnum;
cb->origin_start_col = cb->end.col - 1;
cb->origin_end_col = cb->end.col;
}
else
{
cb->origin_row = (short_u)cb->start.lnum;
cb->origin_start_col = cb->start.col;
cb->origin_end_col = cb->start.col;
}
if (cb->mode == SELECT_MODE_WORD)
{
clip_get_word_boundaries(cb, cb->origin_row, cb->origin_start_col);
cb->origin_start_col = cb->word_start_col;
cb->origin_end_col = cb->word_end_col;
}
}
/* set state, for when using the right mouse button */
cb->state = SELECT_IN_PROGRESS;
#ifdef DEBUG_SELECTION
printf("Selection extending to (%d,%d)\n", row, col);
#endif
switch (cb->mode)
{
case SELECT_MODE_CHAR:
/* If we're on a different line, find where the line ends */
if (row != cb->prev.lnum)
cb->word_end_col = clip_get_line_end(row);
/* See if we are before or after the origin of the selection */
if (clip_compare_pos(row, col, cb->origin_row,
cb->origin_start_col) >= 0)
{
if (col >= (int)cb->word_end_col)
clip_update_non_visual_selection(cb, cb->origin_row,
cb->origin_start_col, row, (int)Columns);
else
clip_update_non_visual_selection(cb, cb->origin_row,
cb->origin_start_col, row, col + 1);
}
else
{
if (col >= (int)cb->word_end_col)
clip_update_non_visual_selection(cb, row, cb->word_end_col,
cb->origin_row, cb->origin_start_col + 1);
else
clip_update_non_visual_selection(cb, row, col,
cb->origin_row, cb->origin_start_col + 1);
}
break;
case SELECT_MODE_WORD:
/* If we are still within the same word, do nothing */
if (row == cb->prev.lnum && col >= (int)cb->word_start_col
&& col < (int)cb->word_end_col)
return;
/* Get new word boundaries */
clip_get_word_boundaries(cb, row, col);
/* Handle being after the origin point of selection */
if (clip_compare_pos(row, col, cb->origin_row,
cb->origin_start_col) >= 0)
clip_update_non_visual_selection(cb, cb->origin_row,
cb->origin_start_col, row, cb->word_end_col);
else
clip_update_non_visual_selection(cb, row, cb->word_start_col,
cb->origin_row, cb->origin_end_col);
break;
case SELECT_MODE_LINE:
if (row == cb->prev.lnum)
return;
if (clip_compare_pos(row, col, cb->origin_row,
cb->origin_start_col) >= 0)
clip_update_non_visual_selection(cb, cb->origin_row, 0, row,
(int)Columns);
else
clip_update_non_visual_selection(cb, row, 0, cb->origin_row,
(int)Columns);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -