home *** CD-ROM | disk | FTP | other *** search
- /* vi:set ts=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"
- #include "globals.h"
- #include "proto.h"
- #include "option.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_outstr __ARGS((char_u *, int));
- static void gui_update_selection __ARGS((void));
- static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
- static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
- static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
- 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 char_u *gui_menu_name_skip __ARGS((char_u *name));
- static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
- static void gui_update_scrollbars __ARGS((void));
- static void gui_update_horiz_scrollbar __ARGS((void));
-
- /*
- * 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.
- */
- #ifdef USE_GUI_ATHENA
- # define SCROLL_PAST_END
- #endif
-
- /*
- * gui_start -- Called when user wants to start the GUI.
- */
- void
- gui_start()
- {
- char_u *old_term;
- int pid;
-
- old_term = strsave(term_strings[KS_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.
- */
- settmode(0); /* stop RAW mode */
- 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 */
- full_screen = TRUE;
- settmode(1); /* restart RAW mode */
- }
- full_screen = TRUE;
-
- vim_free(old_term);
-
- /*
- * 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
- }
- }
-
- /*
- * 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);
- }
-
- static struct default_menu
- {
- char *name; /* name of menu item */
- int mode; /* mode where menu is valid */
- char *command; /* resulting command */
- } default_menus[] =
- {
- /* Help menu. Some reason Motif is happier if this is added first. */
- {"Help.Overview <F1>", MENU_NORMAL_MODE, ":help\r"},
- {"Help.Overview <F1>", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:help\r"},
- {"Help.How to\\.\\.\\.",MENU_NORMAL_MODE, ":help how_to\r"},
- {"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:help how_to\r"},
- {"Help.GUI", MENU_NORMAL_MODE, ":help gui\r"},
- {"Help.GUI", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:help gui\r"},
- {"Help.Version", MENU_NORMAL_MODE, ":version\r"},
- {"Help.Version", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:version\r"},
- {"Help.Credits", MENU_NORMAL_MODE, ":help credits\r"},
- {"Help.Credits", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:help credits\r"},
- {"Help.Copying", MENU_NORMAL_MODE, ":help uganda\r"},
- {"Help.Copying", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:help uganda\r"},
-
- /* File menu */
- {"File.Save :w", MENU_NORMAL_MODE, ":w\r"},
- {"File.Save :w", MENU_INSERT_MODE, "\017:w\r"},
-
- {"File.Close :q", MENU_NORMAL_MODE, ":q\r"},
- {"File.Close :q", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:q\r"},
-
- {"File.Quit :qa", MENU_NORMAL_MODE, ":qa\r"},
- {"File.Quit :qa", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:qa\r"},
-
- {"File.Save-Quit :wqa",MENU_NORMAL_MODE, ":wqa\r"},
- {"File.Save-Quit :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\033:wqa\r"},
-
- /* Edit menu */
- {"Edit.Undo", MENU_NORMAL_MODE, "u"},
- {"Edit.Redo", MENU_NORMAL_MODE, "\022"},
-
- {"Edit.Cut", MENU_VISUAL_MODE, "x"},
- {"Edit.Copy", MENU_VISUAL_MODE, "y"},
- {"Edit.Put Before", MENU_NORMAL_MODE, "[p"},
- {"Edit.Put Before", MENU_INSERT_MODE, "\017[p"},
- {"Edit.Put After", MENU_NORMAL_MODE, "]p"},
- {"Edit.Put After", MENU_INSERT_MODE, "\017]p"},
- {"Edit.Paste", MENU_NORMAL_MODE, "i\022*\033"}, /* CTRL-R * */
- {"Edit.Paste", MENU_INSERT_MODE|MENU_CMDLINE_MODE,
- "\022*"}, /* CTRL-R * */
- {NULL, 0, NULL}
- };
-
- /*
- * This is the call which starts the GUI.
- */
- void
- gui_init()
- {
- char_u *env_str;
- int i;
-
- gui.dying = FALSE;
- gui.in_focus = FALSE;
- gui.dragged_sb = SB_NONE;
- gui.dragged_wp = NULL;
- gui.col = gui.num_cols = 0;
- gui.row = gui.num_rows = 0;
-
- /* Initialise gui.cursor_row: */
- INVALIDATE_CURSOR();
- gui.scroll_region_top = 0;
- gui.scroll_region_bot = Rows - 1;
- gui.highlight_mask = HL_NORMAL;
- gui.char_width = 0;
- gui.char_height = 0;
- gui.char_ascent = 0;
- gui.border_width = 0;
-
- gui.selection.owned = FALSE;
- gui.selection.start.lnum = 0;
- gui.selection.start.col = 0;
- gui.selection.end.lnum = 0;
- gui.selection.end.col = 0;
- gui.selection.state = SELECT_CLEARED;
-
- gui.root_menu = NULL;
- gui.menu_is_active = TRUE; /* default: include menu */
-
- gui.scrollbar_width = SB_DEFAULT_WIDTH;
- gui.menu_height = MENU_DEFAULT_HEIGHT;
- for (i = 0; i < 3; i++)
- gui.new_sb[i] = FALSE;
-
- gui.prev_wrap = -1;
-
- for (i = 0; default_menus[i].name != NULL; ++i)
- gui_add_menu_path((char_u *)default_menus[i].name,
- default_menus[i].mode, gui_menu_cb,
- (char_u *)default_menus[i].command, TRUE);
-
- /*
- * Switch on the mouse by default.
- * This can be changed in the .gvimrc.
- */
- set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);
-
- /*
- * Get system wide defaults for gvim (Unix only)
- */
- #ifdef HAVE_CONFIG_H
- do_source(sys_gvimrc_fname, FALSE);
- #endif
-
- /*
- * Try to read GUI initialization commands from the following places:
- * - environment variable GVIMINIT
- * - the user gvimrc file (~/.gvimrc for Unix)
- * The first that exists is used, the rest is ignored.
- */
- if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
- {
- sourcing_name = (char_u *)"GVIMINIT";
- do_cmdline(env_str, TRUE, TRUE);
- sourcing_name = NULL;
- }
- else
- do_source((char_u *)USR_GVIMRC_FILE, FALSE);
-
- /*
- * 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 or sys_gvimrc_fname.
- */
- 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
-
- i = FAIL;
- if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
- (char_u *)GVIMRC_FILE) != FPC_SAME
- #ifdef HAVE_CONFIG_H
- && fullpathcmp(sys_gvimrc_fname,
- (char_u *)GVIMRC_FILE) != FPC_SAME
- #endif
- )
- i = do_source((char_u *)GVIMRC_FILE, FALSE);
-
- if (secure == 2)
- need_wait_return = TRUE;
- secure = 0;
- }
-
- /*
- * Actually start the GUI itself.
- */
- gui.in_use = TRUE; /* Must be set after menus have been set up */
- if (gui_mch_init() == FAIL)
- {
- gui.in_use = FALSE;
- return;
- }
-
- maketitle();
-
- gui_create_initial_menus(gui.root_menu, NULL);
- }
-
- void
- gui_exit()
- {
- gui.in_use = FALSE;
- gui_mch_exit();
- }
-
- /*
- * Set the font. Uses the 'font' option. The first font name that works is
- * used. If none is found, use the default font.
- */
- int
- gui_init_font()
- {
- #define FONTLEN 100
- char_u *font_list;
- char_u font_name[FONTLEN];
-
- if (!gui.in_use)
- return FAIL;
-
- for (font_list = p_guifont; *font_list != NUL; )
- {
- /* Isolate one font name */
- (void)copy_option_part(&font_list, font_name, FONTLEN, ",");
- if (gui_mch_init_font(font_name) == OK)
- return OK;
- }
-
- /*
- * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
- * find a font we can load.
- */
- return gui_mch_init_font(NULL);
- }
-
- 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)
- INVALIDATE_CURSOR();
- }
-
- void
- gui_update_cursor()
- {
- gui_check_screen();
- if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
- {
- gui_undraw_cursor();
- gui.cursor_row = gui.row;
- gui.cursor_col = gui.col;
- gui_mch_draw_cursor();
- }
- }
-
- /*
- * Should be called after the GUI window has been resized. Its arguments are
- * the new width and height of the window in pixels.
- */
- void
- gui_resize_window(pixel_width, pixel_height)
- int pixel_width;
- int pixel_height;
- {
- gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
- gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;
-
- gui_reset_scroll_region();
- /*
- * At the "more" prompt there is no redraw, put the cursor at the last
- * line here (why does it have to be one row too low?).
- */
- if (State == ASKMORE)
- gui.row = gui.num_rows;
-
- if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
- set_winsize(0, 0, FALSE);
- gui_update_cursor();
- }
-
- /*
- * Make scroll region cover whole screen.
- */
- void
- gui_reset_scroll_region()
- {
- gui.scroll_region_top = 0;
- gui.scroll_region_bot = gui.num_rows - 1;
- }
-
- void
- gui_start_highlight(mask)
- long_u mask;
- {
- gui.highlight_mask |= mask;
- }
-
- void
- gui_stop_highlight(mask)
- long_u mask;
- {
- gui.highlight_mask &= ~mask;
- }
-
- void
- gui_write(s, len)
- char_u *s;
- int len;
- {
- char_u *p;
- int arg1 = 0, arg2 = 0;
-
- /* #define DEBUG_GUI_WRITE */
- #ifdef DEBUG_GUI_WRITE
- {
- int i;
- char_u *str;
-
- printf("gui_write(%d):\n ", len);
- for (i = 0; i < len; i++)
- if (s[i] == ESC)
- {
- if (i != 0)
- printf("\n ");
- printf("<ESC>");
- }
- else
- {
- str = transchar(s[i]);
- if (str[0] && str[1])
- printf("<%s>", (char *)str);
- else
- printf("%s", (char *)str);
- }
- printf("\n");
- }
- #endif
- while (len)
- {
- if (s[0] == '\n')
- {
- len--;
- s++;
- gui.col = 0;
- if (gui.row < gui.scroll_region_bot)
- gui.row++;
- else
- gui_mch_delete_lines(gui.scroll_region_top, 1);
- }
- else if (s[0] == '\r')
- {
- len--;
- s++;
- gui.col = 0;
- }
- else if (s[0] == Ctrl('G')) /* Beep */
- {
- gui_mch_beep();
- len--;
- s++;
- }
- else if (s[0] == ESC && s[1] == '|')
- {
- p = s + 2;
- if (isdigit(*p))
- {
- arg1 = getdigits(&p);
- if (p > s + len)
- break;
- if (*p == ';')
- {
- ++p;
- arg2 = getdigits(&p);
- if (p > s + len)
- break;
- }
- }
- switch (*p)
- {
- case 'C': /* Clear screen */
- gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
- break;
- case 'M': /* Move cursor */
- gui_set_cursor(arg1, arg2);
- break;
- case 'R': /* Set scroll region */
- if (arg1 < arg2)
- {
- gui.scroll_region_top = arg1;
- gui.scroll_region_bot = arg2;
- }
- else
- {
- gui.scroll_region_top = arg2;
- gui.scroll_region_bot = arg1;
- }
- break;
- case 'd': /* Delete line */
- gui_mch_delete_lines(gui.row, 1);
- break;
- case 'D': /* Delete lines */
- gui_mch_delete_lines(gui.row, arg1);
- break;
- case 'i': /* Insert line */
- gui_mch_insert_lines(gui.row, 1);
- break;
- case 'I': /* Insert lines */
- gui_mch_insert_lines(gui.row, arg1);
- break;
- case '$': /* Clear to end-of-line */
- gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
- break;
- case 'h': /* Turn on highlighting */
- gui_start_highlight(arg1);
- break;
- case 'H': /* Turn off highlighting */
- gui_stop_highlight(arg1);
- break;
- case 'f': /* flash the window (visual bell) */
- gui_mch_flash();
- break;
- default:
- p = s + 1; /* Skip the ESC */
- break;
- }
- len -= ++p - s;
- s = p;
- }
- else if (s[0] < 0x20) /* Ctrl character, shouldn't happen */
- {
- /*
- * For some reason vim sends me a ^M after hitting return on the
- * ':' line. Make sure we ignore this here.
- */
- len--; /* Skip this char */
- s++;
- }
- else
- {
- p = s;
- while (len && *p >= 0x20)
- {
- len--;
- p++;
- }
- gui_outstr(s, p - s);
- s = p;
- }
- }
- gui_update_cursor();
- gui_update_scrollbars();
- gui_update_horiz_scrollbar();
-
- /*
- * We need to make sure this is cleared since Athena doesn't tell us when
- * he is done dragging.
- */
- gui.dragged_sb = SB_NONE;
-
- if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
- gui_update_selection();
- gui_mch_flush(); /* In case vim decides to take a nap */
- }
-
- static void
- gui_outstr(s, len)
- char_u *s;
- int len;
- {
- int this_len;
-
- if (len == 0)
- return;
-
- if (len < 0)
- len = STRLEN(s);
-
- while (gui.col + len > Columns)
- {
- this_len = Columns - gui.col;
- gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
- s += this_len;
- len -= this_len;
- }
- gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
- }
-
- /*
- * Un-draw the cursor. Actually this just redraws the character at the given
- * position.
- */
- void
- gui_undraw_cursor()
- {
- if (IS_CURSOR_VALID())
- gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
- gui.cursor_col);
- }
-
- void
- gui_redraw(x, y, w, h)
- int x;
- int y;
- int w;
- int h;
- {
- int row1, col1, row2, col2;
-
- row1 = Y_2_ROW(y);
- col1 = X_2_COL(x);
- row2 = Y_2_ROW(y + h - 1);
- col2 = X_2_COL(x + w - 1);
-
- gui_redraw_block(row1, col1, row2, col2);
-
- /* We may need to redraw the cursor */
- gui_update_cursor();
- }
-
- void
- gui_redraw_block(row1, col1, row2, col2)
- int row1;
- int col1;
- int row2;
- int col2;
- {
- int old_row, old_col;
- long_u old_hl_mask;
- char_u *screenp, *attrp, first_attr;
- int idx, len;
-
- /* Don't try to draw outside the window! */
- /* Check everything, strange values may be caused by big border width */
- col1 = check_col(col1);
- col2 = check_col(col2);
- row1 = check_row(row1);
- row2 = check_row(row2);
-
- /* Don't try to update when NextScreen is not valid */
- if (!screen_cleared || NextScreen == NULL)
- return;
-
- /* Remember where our cursor was */
- old_row = gui.row;
- old_col = gui.col;
- old_hl_mask = gui.highlight_mask;
-
- for (gui.row = row1; gui.row <= row2; gui.row++)
- {
- gui.col = col1;
- screenp = LinePointers[gui.row] + gui.col;
- attrp = screenp + Columns;
- len = col2 - col1 + 1;
- while (len > 0)
- {
- switch (attrp[0])
- {
- case CHAR_INVERT:
- gui.highlight_mask = HL_INVERSE;
- break;
- case CHAR_UNDERL:
- gui.highlight_mask = HL_UNDERLINE;
- break;
- case CHAR_BOLD:
- gui.highlight_mask = HL_BOLD;
- break;
- case CHAR_STDOUT:
- gui.highlight_mask = HL_STANDOUT;
- break;
- case CHAR_ITALIC:
- gui.highlight_mask = HL_ITAL;
- break;
- case CHAR_NORMAL:
- default:
- gui.highlight_mask = HL_NORMAL;
- break;
- }
- first_attr = attrp[0];
- for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
- --len;
- gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
- screenp += idx;
- attrp += idx;
- }
- }
-
- /* Put the cursor back where it was */
- gui.row = old_row;
- gui.col = old_col;
- gui.highlight_mask = old_hl_mask;
- }
-
- /*
- * Check bounds for column number
- */
- int
- check_col(col)
- int col;
- {
- if (col < 0)
- return 0;
- if (col >= (int)Columns)
- return (int)Columns - 1;
- return col;
- }
-
- /*
- * Check bounds for row number
- */
- int
- check_row(row)
- int row;
- {
- if (row < 0)
- return 0;
- if (row >= (int)Rows)
- return (int)Rows - 1;
- return row;
- }
-
- /*
- * Generic mouse support function. Add a mouse event to the input buffer with
- * the given properties.
- * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
- * MOUSE_DRAG, or MOUSE_RELEASE.
- * x, y --- Coordinates of mouse in pixels.
- * repeated_click --- TRUE if this click comes only a short time after a
- * previous click.
- * modifiers --- Bit field which may be any of the following modifiers
- * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
- * This function will ignore drag events where the mouse has not moved to a new
- * character.
- */
- void
- gui_send_mouse_event(button, x, y, repeated_click, modifiers)
- int button;
- int x;
- int y;
- int repeated_click;
- int_u modifiers;
- {
- static int prev_row = 0, prev_col = 0;
- static int prev_button = -1;
- static linenr_t prev_topline = 0;
- static int num_clicks = 1;
- char_u string[6];
- int row, col;
-
- row = Y_2_ROW(y);
- col = X_2_COL(x);
-
- /*
- * If we are dragging and the mouse hasn't moved far enough to be on a
- * different character, then don't send an event to vim.
- */
- if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
- return;
-
- /*
- * If topline has changed (window scrolled) since the last click, reset
- * repeated_click, because we don't want starting Visual mode when
- * clicking on a different character in the text.
- */
- if (curwin->w_topline != prev_topline)
- repeated_click = FALSE;
-
- string[0] = CSI; /* this sequence is recognized by check_termcode() */
- string[1] = KS_MOUSE;
- string[2] = K_FILLER;
- if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
- {
- if (repeated_click)
- {
- /*
- * Handle multiple clicks. They only count if the mouse is still
- * pointing at the same character.
- */
- if (button != prev_button || row != prev_row || col != prev_col)
- num_clicks = 1;
- else if (++num_clicks > 4)
- num_clicks = 1;
- }
- else
- num_clicks = 1;
- prev_button = button;
- prev_topline = curwin->w_topline;
-
- string[3] = (char_u)(button | 0x20);
- SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
- }
- else
- string[3] = (char_u)button;
-
- string[3] |= modifiers;
- string[4] = (char_u)(col + ' ' + 1);
- string[5] = (char_u)(row + ' ' + 1);
- add_to_input_buf(string, 6);
-
- prev_row = row;
- prev_col = col;
- }
-
- /*
- * Selection stuff, for cutting and pasting text to other windows.
- */
-
- /*
- * 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.
- */
- static void
- gui_update_selection()
- {
- /* If visual mode is only due to a redo command ("."), then ignore it */
- if (redo_VIsual_busy)
- return;
- if (!VIsual_active)
- {
- gui_mch_clear_selection();
- gui.selection.start = gui.selection.end = VIsual;
- }
- else if (lt(VIsual, curwin->w_cursor))
- {
- if (!equal(gui.selection.start, VIsual) ||
- !equal(gui.selection.end, curwin->w_cursor))
- {
- gui_mch_clear_selection();
- gui.selection.start = VIsual;
- gui.selection.end = curwin->w_cursor;
- gui_free_selection();
- gui_own_selection();
- }
- }
- else
- {
- if (!equal(gui.selection.start, curwin->w_cursor) ||
- !equal(gui.selection.end, VIsual))
- {
- gui_mch_clear_selection();
- gui.selection.start = curwin->w_cursor;
- gui.selection.end = VIsual;
- gui_free_selection();
- gui_own_selection();
- }
- }
- }
-
- void
- gui_own_selection()
- {
- /*
- * Also want to check somehow that we are reading from the keyboard rather
- * than a mapping etc.
- */
- if (!gui.selection.owned && gui_mch_own_selection())
- {
- gui_free_selection();
- gui.selection.owned = TRUE;
- }
- }
-
- void
- gui_lose_selection()
- {
- gui_free_selection();
- gui.selection.owned = FALSE;
- gui_mch_lose_selection();
- }
-
- void
- gui_copy_selection()
- {
- if (VIsual_active)
- {
- if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
- gui_update_selection();
- gui_own_selection();
- if (gui.selection.owned)
- gui_get_selection();
- }
- }
-
- void
- gui_auto_select()
- {
- if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
- gui_copy_selection();
- }
-
- /*
- * Menu stuff.
- */
-
- void
- gui_menu_cb(menu)
- GuiMenu *menu;
- {
- char_u bytes[3 + sizeof(long_u)];
-
- bytes[0] = CSI;
- bytes[1] = KS_MENU;
- bytes[2] = K_FILLER;
- add_long_to_buf((long_u)menu, bytes + 3);
- add_to_input_buf(bytes, 3 + sizeof(long_u));
- }
-
- /*
- * Return the index into the menu->strings or menu->noremap arrays for the
- * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
- * given menu in the current mode.
- */
- int
- gui_get_menu_index(menu, state)
- GuiMenu *menu;
- int state;
- {
- int idx;
-
- if (VIsual_active)
- idx = MENU_INDEX_VISUAL;
- else if ((state & NORMAL))
- idx = MENU_INDEX_NORMAL;
- else if ((state & INSERT))
- idx = MENU_INDEX_INSERT;
- else if ((state & CMDLINE))
- idx = MENU_INDEX_CMDLINE;
- else
- idx = MENU_INDEX_INVALID;
-
- if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
- idx = MENU_INDEX_INVALID;
- return idx;
- }
-
- /*
- * Return the modes specified by the given menu command (eg :menu! returns
- * MENU_CMDLINE_MODE | MENU_INSERT_MODE). If noremap is not NULL, then the
- * flag it points to is set according to whether the command is a "nore"
- * command. If unmenu is not NULL, then the flag it points to is set
- * according to whether the command is an "unmenu" command.
- */
- static int
- gui_get_menu_cmd_modes(cmd, forceit, noremap, unmenu)
- char_u *cmd;
- int forceit; /* Was there a "!" after the command? */
- int *noremap;
- int *unmenu;
- {
- int modes = 0x0;
-
- if (*cmd == 'n' && cmd[1] != 'o') /* nmenu, nnoremenu */
- {
- modes |= MENU_NORMAL_MODE;
- cmd++;
- }
- else if (*cmd == 'v') /* vmenu, vnoremenu */
- {
- modes |= MENU_VISUAL_MODE;
- cmd++;
- }
- else if (*cmd == 'i') /* imenu, inoremenu */
- {
- modes |= MENU_INSERT_MODE;
- cmd++;
- }
- else if (*cmd == 'c') /* cmenu, cnoremenu */
- {
- modes |= MENU_CMDLINE_MODE;
- cmd++;
- }
- else if (forceit) /* menu!, noremenu! */
- modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
- else /* menu, noremenu */
- modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;
-
- if (noremap != NULL)
- *noremap = (*cmd == 'n');
- if (unmenu != NULL)
- *unmenu = (*cmd == 'u');
- return modes;
- }
-
- /*
- * Do the :menu commands.
- */
- void
- gui_do_menu(cmd, arg, forceit)
- char_u *cmd;
- char_u *arg;
- int forceit;
- {
- char_u *menu_path;
- int modes;
- char_u *map_to;
- int noremap;
- int unmenu;
- char_u *map_buf;
-
- modes = gui_get_menu_cmd_modes(cmd, forceit, &noremap, &unmenu);
- menu_path = arg;
- if (*menu_path == NUL)
- {
- gui_show_menus(menu_path, modes);
- return;
- }
- while (*arg && !vim_iswhite(*arg))
- {
- if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
- arg++;
- arg++;
- }
- if (*arg != NUL)
- *arg++ = NUL;
- arg = skipwhite(arg);
- map_to = arg;
- if (*map_to == NUL && !unmenu)
- {
- gui_show_menus(menu_path, modes);
- return;
- }
- else if (*map_to != NUL && unmenu)
- {
- EMSG("Trailing characters");
- return;
- }
- if (unmenu)
- {
- if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
- menu_path = (char_u *)"";
- gui_remove_menu(&gui.root_menu, menu_path, modes);
- }
- else
- {
- /* Replace special key codes */
- map_to = replace_termcodes(map_to, &map_buf, FALSE);
- gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
- vim_free(map_buf);
- }
- }
-
- /*
- * Add the menu with the given name to the menu hierarchy
- */
- static int
- gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
- char_u *path_name;
- int modes;
- void (*call_back)();
- char_u *call_data;
- int noremap;
- {
- GuiMenu **menup;
- GuiMenu *menu = NULL;
- GuiMenu *parent;
- char_u *p;
- char_u *name;
- int i;
-
- /* Make a copy so we can stuff around with it, since it could be const */
- path_name = strsave(path_name);
- if (path_name == NULL)
- return FAIL;
- menup = &gui.root_menu;
- parent = NULL;
- name = path_name;
- while (*name)
- {
- /* Get name of this element in the menu hierarchy */
- p = gui_menu_name_skip(name);
-
- /* See if it's already there */
- menu = *menup;
- while (menu != NULL)
- {
- if (STRCMP(name, menu->name) == 0)
- {
- if (*p == NUL && menu->children != NULL)
- {
- EMSG("Menu path must not lead to a sub-menu");
- vim_free(path_name);
- return FAIL;
- }
- else if (*p != NUL && menu->children == NULL)
- {
- EMSG("Part of menu-item path is not sub-menu");
- vim_free(path_name);
- return FAIL;
- }
- break;
- }
- menup = &menu->next;
- menu = menu->next;
- }
- if (menu == NULL)
- {
- if (*p == NUL && parent == NULL)
- {
- EMSG("Must not add menu items directly to menu bar");
- vim_free(path_name);
- return FAIL;
- }
-
- /* Not already there, so lets add it */
- menu = (GuiMenu *)alloc(sizeof(GuiMenu));
- if (menu == NULL)
- {
- vim_free(path_name);
- return FAIL;
- }
- menu->modes = modes;
- menu->name = strsave(name);
- menu->cb = NULL;
- for (i = 0; i < 4; i++)
- {
- menu->strings[i] = NULL;
- menu->noremap[i] = FALSE;
- }
- menu->children = NULL;
- menu->next = NULL;
- if (gui.in_use) /* Otherwise it will be added when GUI starts */
- {
- if (*p == NUL)
- {
- /* Real menu item, not sub-menu */
- gui_mch_add_menu_item(menu, parent);
-
- /* Want to update menus now even if mode not changed */
- force_menu_update = TRUE;
- }
- else
- {
- /* Sub-menu (not at end of path yet) */
- gui_mch_add_menu(menu, parent);
- }
- }
- *menup = menu;
- }
- else
- {
- /*
- * If this menu option was previously only available in other
- * modes, then make sure it's available for this one now
- */
- menu->modes |= modes;
- }
-
- menup = &menu->children;
- parent = menu;
- name = p;
- }
- vim_free(path_name);
-
- if (menu != NULL)
- {
- menu->cb = call_back;
- p = (call_data == NULL) ? NULL : strsave(call_data);
-
- /* May match more than one of these */
- if (modes & MENU_NORMAL_MODE)
- {
- gui_free_menu_string(menu, MENU_INDEX_NORMAL);
- menu->strings[MENU_INDEX_NORMAL] = p;
- menu->noremap[MENU_INDEX_NORMAL] = noremap;
- }
- if (modes & MENU_VISUAL_MODE)
- {
- gui_free_menu_string(menu, MENU_INDEX_VISUAL);
- menu->strings[MENU_INDEX_VISUAL] = p;
- menu->noremap[MENU_INDEX_VISUAL] = noremap;
- }
- if (modes & MENU_INSERT_MODE)
- {
- gui_free_menu_string(menu, MENU_INDEX_INSERT);
- menu->strings[MENU_INDEX_INSERT] = p;
- menu->noremap[MENU_INDEX_INSERT] = noremap;
- }
- if (modes & MENU_CMDLINE_MODE)
- {
- gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
- menu->strings[MENU_INDEX_CMDLINE] = p;
- menu->noremap[MENU_INDEX_CMDLINE] = noremap;
- }
- }
- return OK;
- }
-
- /*
- * Remove the (sub)menu with the given name from the menu hierarchy
- * Called recursively.
- */
- static int
- gui_remove_menu(menup, name, modes)
- GuiMenu **menup;
- char_u *name;
- int modes;
- {
- GuiMenu *menu;
- GuiMenu *child;
- char_u *p;
-
- if (*menup == NULL)
- return OK; /* Got to bottom of hierarchy */
-
- /* Get name of this element in the menu hierarchy */
- p = gui_menu_name_skip(name);
-
- /* Find the menu */
- menu = *menup;
- while (menu != NULL)
- {
- if (*name == NUL || STRCMP(name, menu->name) == 0)
- {
- if (*p != NUL && menu->children == NULL)
- {
- EMSG("Part of menu-item path is not sub-menu");
- return FAIL;
- }
- if ((menu->modes & modes) != 0x0)
- {
- if (gui_remove_menu(&menu->children, p, modes) == FAIL)
- return FAIL;
- }
- else if (*name != NUL)
- {
- EMSG("Menu only exists in another mode");
- return FAIL;
- }
-
- /*
- * When name is empty, we are removing all menu items for the given
- * modes, so keep looping, otherwise we are just removing the named
- * menu item (which has been found) so break here.
- */
- if (*name != NUL)
- break;
-
- /* Remove the menu item for the given mode[s] */
- menu->modes &= ~modes;
-
- if (menu->modes == 0x0)
- {
- /* The menu item is no longer valid in ANY mode, so delete it */
- *menup = menu->next;
- gui_free_menu(menu);
- }
- else
- menup = &menu->next;
- }
- else
- menup = &menu->next;
- menu = *menup;
- }
- if (*name != NUL)
- {
- if (menu == NULL)
- {
- EMSG("No menu of that name");
- return FAIL;
- }
-
- /* Recalculate modes for menu based on the new updated children */
- menu->modes = 0x0;
- for (child = menu->children; child != NULL; child = child->next)
- menu->modes |= child->modes;
- if (menu->modes == 0x0)
- {
- /* The menu item is no longer valid in ANY mode, so delete it */
- *menup = menu->next;
- gui_free_menu(menu);
- }
- }
-
- return OK;
- }
-
- /*
- * Free the given menu structure
- */
- static void
- gui_free_menu(menu)
- GuiMenu *menu;
- {
- int i;
-
- /* Free machine specific menu structures (only when already created) */
- if (gui.in_use)
- gui_mch_destroy_menu(menu);
- vim_free(menu->name);
- for (i = 0; i < 4; i++)
- gui_free_menu_string(menu, i);
- vim_free(menu);
-
- /* Want to update menus now even if mode not changed */
- force_menu_update = TRUE;
- }
-
- /*
- * Free the menu->string with the given index.
- */
- static void
- gui_free_menu_string(menu, idx)
- GuiMenu *menu;
- int idx;
- {
- int count = 0;
- int i;
-
- for (i = 0; i < 4; i++)
- if (menu->strings[i] == menu->strings[idx])
- count++;
- if (count == 1)
- vim_free(menu->strings[idx]);
- menu->strings[idx] = NULL;
- }
-
- /*
- * Show the mapping associated with a menu item or hierarchy in a sub-menu.
- */
- static int
- gui_show_menus(path_name, modes)
- char_u *path_name;
- int modes;
- {
- char_u *p;
- char_u *name;
- GuiMenu *menu;
- GuiMenu *parent = NULL;
-
- menu = gui.root_menu;
- name = path_name = strsave(path_name);
- if (path_name == NULL)
- return FAIL;
-
- /* First, find the (sub)menu with the given name */
- while (*name)
- {
- p = gui_menu_name_skip(name);
- while (menu != NULL)
- {
- if (STRCMP(name, menu->name) == 0)
- {
- /* Found menu */
- if (*p != NUL && menu->children == NULL)
- {
- EMSG("Part of menu-item path is not sub-menu");
- vim_free(path_name);
- return FAIL;
- }
- else if ((menu->modes & modes) == 0x0)
- {
- EMSG("Menu only exists in another mode");
- vim_free(path_name);
- return FAIL;
- }
- break;
- }
- menu = menu->next;
- }
- if (menu == NULL)
- {
- EMSG("No menu of that name");
- vim_free(path_name);
- return FAIL;
- }
- name = p;
- parent = menu;
- menu = menu->children;
- }
-
- /* Now we have found the matching menu, and we list the mappings */
- set_highlight('t'); /* Highlight title */
- start_highlight();
- MSG_OUTSTR("\n--- Menus ---");
- stop_highlight();
-
- gui_show_menus_recursive(parent, modes, 0);
- return OK;
- }
-
- /*
- * Recursively show the mappings associated with the menus under the given one
- */
- static void
- gui_show_menus_recursive(menu, modes, depth)
- GuiMenu *menu;
- int modes;
- int depth;
- {
- int i;
- int bit;
-
- if (menu != NULL && (menu->modes & modes) == 0x0)
- return;
-
- if (menu != NULL)
- {
- msg_outchar('\n');
- if (got_int) /* "q" hit for "--more--" */
- return;
- for (i = 0; i < depth; i++)
- MSG_OUTSTR(" ");
- set_highlight('d'); /* Same as for directories!? */
- start_highlight();
- msg_outstr(menu->name);
- stop_highlight();
- }
-
- if (menu != NULL && menu->children == NULL)
- {
- for (bit = 0; bit < 4; bit++)
- if ((menu->modes & modes & (1 << bit)) != 0)
- {
- msg_outchar('\n');
- if (got_int) /* "q" hit for "--more--" */
- return;
- for (i = 0; i < depth + 2; i++)
- MSG_OUTSTR(" ");
- msg_outchar("nvic"[bit]);
- if (menu->noremap[bit])
- msg_outchar('*');
- else
- msg_outchar(' ');
- MSG_OUTSTR(" ");
- msg_outtrans_special(menu->strings[bit], TRUE);
- }
- }
- else
- {
- if (menu == NULL)
- {
- menu = gui.root_menu;
- depth--;
- }
- else
- menu = menu->children;
- for (; menu != NULL; menu = menu->next)
- gui_show_menus_recursive(menu, modes, depth + 1);
- }
- }
-
- /*
- * Used when expanding menu names.
- */
- static GuiMenu *expand_menu = NULL;
- static int expand_modes = 0x0;
-
- /*
- * Work out what to complete when doing command line completion of menu names.
- */
- char_u *
- gui_set_context_in_menu_cmd(cmd, arg, forceit)
- char_u *cmd;
- char_u *arg;
- int forceit;
- {
- char_u *after_dot;
- char_u *p;
- char_u *path_name = NULL;
- char_u *name;
- int unmenu;
- GuiMenu *menu;
-
- expand_context = EXPAND_UNSUCCESSFUL;
-
- after_dot = arg;
- for (p = arg; *p && !vim_iswhite(*p); ++p)
- {
- if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
- p++;
- else if (*p == '.')
- after_dot = p + 1;
- }
- if (*p == NUL) /* Complete the menu name */
- {
- /*
- * With :unmenu, you only want to match menus for the appropriate mode.
- * With :menu though you might want to add a menu with the same name as
- * one in another mode, so match menus fom other modes too.
- */
- expand_modes = gui_get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
- if (!unmenu)
- expand_modes = MENU_ALL_MODES;
-
- menu = gui.root_menu;
- if (after_dot != arg)
- {
- path_name = alloc(after_dot - arg);
- if (path_name == NULL)
- return NULL;
- STRNCPY(path_name, arg, after_dot - arg - 1);
- path_name[after_dot - arg - 1] = NUL;
- }
- name = path_name;
- while (name != NULL && *name)
- {
- p = gui_menu_name_skip(name);
- while (menu != NULL)
- {
- if (STRCMP(name, menu->name) == 0)
- {
- /* Found menu */
- if ((*p != NUL && menu->children == NULL)
- || ((menu->modes & expand_modes) == 0x0))
- {
- /*
- * Menu path continues, but we have reached a leaf.
- * Or menu exists only in another mode.
- */
- vim_free(path_name);
- return NULL;
- }
- break;
- }
- menu = menu->next;
- }
- if (menu == NULL)
- {
- /* No menu found with the name we were looking for */
- vim_free(path_name);
- return NULL;
- }
- name = p;
- menu = menu->children;
- }
-
- expand_context = EXPAND_MENUS;
- expand_pattern = after_dot;
- expand_menu = menu;
- }
- else /* We're in the mapping part */
- expand_context = EXPAND_NOTHING;
- return NULL;
- }
-
- /*
- * Expand the menu names.
- */
- int
- gui_ExpandMenuNames(prog, num_file, file)
- regexp *prog;
- int *num_file;
- char_u ***file;
- {
- GuiMenu *menu;
- int round;
- int count;
-
- /*
- * round == 1: Count the matches.
- * round == 2: Save the matches into the array.
- */
- for (round = 1; round <= 2; ++round)
- {
- count = 0;
- for (menu = expand_menu; menu != NULL; menu = menu->next)
- if ((menu->modes & expand_modes) != 0x0
- && vim_regexec(prog, menu->name, TRUE))
- {
- if (round == 1)
- count++;
- else
- (*file)[count++] = strsave_escaped(menu->name,
- (char_u *)" \t\\.");
- }
- if (round == 1)
- {
- *num_file = count;
- if (count == 0 || (*file = (char_u **)
- alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
- return FAIL;
- }
- }
- return OK;
- }
-
- /*
- * Skip over this element of the menu path and return the start of the next
- * element. Any \ and ^Vs are removed from the current element.
- */
- static char_u *
- gui_menu_name_skip(name)
- char_u *name;
- {
- char_u *p;
-
- for (p = name; *p && *p != '.'; p++)
- if (*p == '\\' || *p == Ctrl('V'))
- {
- STRCPY(p, p + 1);
- if (*p == NUL)
- break;
- }
- if (*p)
- *p++ = NUL;
- return p;
- }
-
- /*
- * After we have started the GUI, then we can create any menus that have been
- * defined. This is done once here. gui_add_menu_path() may have already been
- * called to define these menus, and may be called again. This function calls
- * itself recursively. Should be called at the top level with:
- * gui_create_initial_menus(gui.root_menu, NULL);
- */
- static void
- gui_create_initial_menus(menu, parent)
- GuiMenu *menu;
- GuiMenu *parent;
- {
- while (menu)
- {
- if (menu->children != NULL)
- {
- gui_mch_add_menu(menu, parent);
- gui_create_initial_menus(menu->children, menu);
- }
- else
- gui_mch_add_menu_item(menu, parent);
- menu = menu->next;
- }
- }
-
-
- /*
- * Set which components are present.
- * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
- * in p_guioptions.
- */
- void
- gui_init_which_components(oldval)
- char_u *oldval;
- {
- char_u *p;
- int i;
- int grey_old, grey_new;
- char_u *temp;
-
- if (oldval != NULL && gui.in_use)
- {
- /*
- * Check if the menu's go from grey to non-grey or vise versa.
- */
- grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
- grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
- if (grey_old != grey_new)
- {
- temp = p_guioptions;
- p_guioptions = oldval;
- gui_x11_update_menus(MENU_ALL_MODES);
- p_guioptions = temp;
- }
- }
-
- gui.menu_is_active = FALSE;
- for (i = 0; i < 3; i++)
- gui.which_scrollbars[i] = FALSE;
- for (p = p_guioptions; *p; p++)
- switch (*p)
- {
- case GO_LEFT:
- gui.which_scrollbars[SB_LEFT] = TRUE;
- break;
- case GO_RIGHT:
- gui.which_scrollbars[SB_RIGHT] = TRUE;
- break;
- case GO_BOT:
- gui.which_scrollbars[SB_BOTTOM] = TRUE;
- break;
- case GO_MENUS:
- gui.menu_is_active = TRUE;
- break;
- case GO_GREY:
- /* make menu's have grey items, ignored here */
- break;
- default:
- /* Should give error message for internal error */
- break;
- }
- if (gui.in_use)
- gui_mch_create_which_components();
- }
-
-
- /*
- * Vertical scrollbar stuff:
- */
-
- static void
- gui_update_scrollbars()
- {
- WIN *wp;
- int worst_update = SB_UPDATE_NOTHING;
- int val, size, max;
- int which_sb;
- int cmdline_height;
-
- /*
- * Don't want to update a scrollbar while we're dragging it. But if we
- * have both a left and right scrollbar, and we drag one of them, we still
- * need to update the other one.
- */
- if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
- (!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
- return;
-
- if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
- {
- /*
- * If we have two scrollbars and one of them is being dragged, just
- * copy the scrollbar position from the dragged one to the other one.
- */
- which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
- if (gui.dragged_wp != NULL)
- gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
- else
- gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;
-
- gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
- return;
- }
-
- /* Return straight away if there is neither a left nor right scrollbar */
- if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
- return;
-
- cmdline_height = Rows;
- for (wp = firstwin; wp; wp = wp->w_next)
- {
- cmdline_height -= wp->w_height + wp->w_status_height;
- if (wp->w_buffer == NULL) /* just in case */
- continue;
- #ifdef SCROLL_PAST_END
- max = wp->w_buffer->b_ml.ml_line_count;
- #else
- max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
- #endif
- if (max < 1) /* empty buffer */
- max = 1;
- val = wp->w_topline;
- size = wp->w_height;
- #ifdef SCROLL_PAST_END
- if (val > max) /* just in case */
- val = max;
- #else
- if (size > max) /* just in case */
- size = max;
- if (val > max - size + 1)
- val = max - size + 1;
- if (val < 1) /* minimal value is 1 */
- val = 1;
- #endif
- if (size < 1 || wp->w_botline - 1 > max)
- {
- /*
- * This can happen during changing files. Just don't update the
- * scrollbar for now.
- */
- }
- else if (wp->w_scrollbar.height == 0)
- {
- /* Must be a new window */
- wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
- wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
- wp->w_scrollbar.value = val;
- wp->w_scrollbar.size = size;
- wp->w_scrollbar.max = max;
- wp->w_scrollbar.top = wp->w_winpos;
- wp->w_scrollbar.height = wp->w_height;
- wp->w_scrollbar.status_height = wp->w_status_height;
- gui.num_scrollbars++;
- worst_update = SB_UPDATE_CREATE;
- }
- else if (wp->w_scrollbar.top != wp->w_winpos
- || wp->w_scrollbar.height != wp->w_height
- || wp->w_scrollbar.status_height != wp->w_status_height)
- {
- /* Height of scrollbar has changed */
- wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
- wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
- wp->w_scrollbar.value = val;
- wp->w_scrollbar.size = size;
- wp->w_scrollbar.max = max;
- wp->w_scrollbar.top = wp->w_winpos;
- wp->w_scrollbar.height = wp->w_height;
- wp->w_scrollbar.status_height = wp->w_status_height;
- if (worst_update < SB_UPDATE_HEIGHT)
- worst_update = SB_UPDATE_HEIGHT;
- }
- else if (wp->w_scrollbar.value != val
- || wp->w_scrollbar.size != size
- || wp->w_scrollbar.max != max)
- {
- /* Thumb of scrollbar has moved */
- wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
- wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
- wp->w_scrollbar.value = val;
- wp->w_scrollbar.size = size;
- wp->w_scrollbar.max = max;
- if (worst_update < SB_UPDATE_VALUE)
- worst_update = SB_UPDATE_VALUE;
- }
-
- /*
- * We may have just created the left scrollbar say, when we already had
- * the right one.
- */
- if (gui.new_sb[SB_LEFT])
- wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
- if (gui.new_sb[SB_RIGHT])
- wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
- }
- if (cmdline_height < 1)
- cmdline_height = 1; /* Shouldn't happen, but just in case */
-
- /* Check the command line scrollbar */
- if (gui.cmdline_sb.height != cmdline_height
- || gui.cmdline_sb.status_height != lastwin->w_status_height)
- {
- /* Height of scrollbar has changed */
- gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
- gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
- gui.cmdline_sb.value = 0;
- gui.cmdline_sb.size = 1; /* No thumb */
- gui.cmdline_sb.max = 0;
- gui.cmdline_sb.top = Rows - cmdline_height;
- gui.cmdline_sb.height = cmdline_height;
- gui.cmdline_sb.status_height = lastwin->w_status_height;
- if (worst_update < SB_UPDATE_HEIGHT)
- worst_update = SB_UPDATE_HEIGHT;
- }
-
- /*
- * If we have just created the left or right scrollbar-box, then we need to
- * update the height of the command line scrollbar (it will already be
- * created).
- */
- if (gui.new_sb[SB_LEFT])
- {
- gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
- worst_update = SB_UPDATE_CREATE;
- gui.new_sb[SB_LEFT] = FALSE;
- }
- if (gui.new_sb[SB_RIGHT])
- {
- gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
- worst_update = SB_UPDATE_CREATE;
- gui.new_sb[SB_RIGHT] = FALSE;
- }
-
- if (worst_update != SB_UPDATE_NOTHING)
- {
- if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
- gui_mch_update_scrollbars(worst_update, SB_LEFT);
- if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
- gui_mch_update_scrollbars(worst_update, SB_RIGHT);
- }
- }
-
- /*
- * Scroll a window according to the values set in the globals current_scrollbar
- * and scrollbar_value. Return TRUE if the cursor in the current window moved
- * or FALSE otherwise.
- */
- int
- gui_do_scroll()
- {
- WIN *wp, *old_wp;
- int i;
- FPOS old_cursor;
-
- for (wp = firstwin, i = 0; i < current_scrollbar; i++)
- {
- if (wp == NULL)
- break;
- wp = wp->w_next;
- }
- if (wp != NULL)
- {
- old_cursor = curwin->w_cursor;
- old_wp = curwin;
- curwin = wp;
- curbuf = wp->w_buffer;
- i = (long)scrollbar_value - (long)wp->w_topline;
- if (i < 0)
- scrolldown(-i);
- else if (i > 0)
- scrollup(i);
- if (p_so)
- cursor_correct();
- coladvance(curwin->w_curswant);
-
- curwin = old_wp;
- curbuf = old_wp->w_buffer;
-
- if (wp == curwin)
- cursupdate(); /* fix window for 'so' */
- wp->w_redr_type = VALID;
- updateWindow(wp); /* update window, status line, and cmdline */
-
- return !equal(curwin->w_cursor, old_cursor);
- }
- else
- {
- /* Command-line scrollbar, unimplemented */
- return FALSE;
- }
- }
-
-
- /*
- * Horizontal scrollbar stuff:
- */
-
- static void
- gui_update_horiz_scrollbar()
- {
- int value, size, max;
-
- if (!gui.which_scrollbars[SB_BOTTOM])
- return;
-
- if (gui.dragged_sb == SB_BOTTOM)
- return;
-
- if (curwin->w_p_wrap && gui.prev_wrap)
- return;
-
- /*
- * It is possible for the cursor to be invalid if we're in the middle of
- * something (like changing files). If so, don't do anything for now.
- */
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
- return;
-
- size = Columns;
- if (curwin->w_p_wrap)
- {
- value = 0;
- #ifdef SCROLL_PAST_END
- max = 1;
- #else
- max = Columns;
- #endif
- }
- else
- {
- value = curwin->w_leftcol;
- #ifdef SCROLL_PAST_END
- max = gui_get_max_horiz_scroll() + 1;
- #else
- max = gui_get_max_horiz_scroll() + Columns;
- #endif
- }
-
- #ifndef SCROLL_PAST_END
- if (value + size > max)
- value = max - size; /* limit the value to allowable range */
- #endif
- gui_mch_update_horiz_scrollbar(value, size, max);
- gui.prev_wrap = curwin->w_p_wrap;
- }
-
- /*
- * Determine the maximum value for scrolling right.
- */
- int
- gui_get_max_horiz_scroll()
- {
- int max = 0;
- char_u *p;
-
- p = ml_get_curline();
- if (p[0] != NUL)
- while (p[1] != NUL) /* Don't count last character */
- max += chartabsize(*p++, (colnr_t)max);
- return max;
- }
-
- /*
- * Do a horizontal scroll. Return TRUE if the cursor moved, or FALSE otherwise
- */
- int
- gui_do_horiz_scroll()
- {
- char_u *p;
- int i;
- int vcol;
- int ret_val = FALSE;
- int width;
-
- /* no wrapping, no scrolling */
- if (curwin->w_p_wrap)
- return FALSE;
-
- curwin->w_leftcol = scrollbar_value;
-
- width = Columns;
- if (curwin->w_p_nu) /* 8 characters of window used by line number */
- width -= 8;
- i = 0;
- vcol = 0;
- p = ml_get_curline();
- while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
- vcol += chartabsize(p[i++], (colnr_t)vcol);
- if (vcol < curwin->w_leftcol)
- {
- /*
- * Cursor is on a character that is at least partly off the left hand
- * side of the screen.
- */
- while (p[i] && vcol < curwin->w_leftcol)
- vcol += chartabsize(p[i++], (colnr_t)vcol);
- curwin->w_cursor.col = i;
- curwin->w_set_curswant = TRUE;
- ret_val = TRUE;
- }
-
- while (p[i] && i <= curwin->w_cursor.col
- && vcol <= curwin->w_leftcol + width)
- vcol += chartabsize(p[i++], (colnr_t)vcol);
- if (vcol > curwin->w_leftcol + width)
- {
- /*
- * Cursor is on a character that is at least partly off the right hand
- * side of the screen.
- */
- if (i < 2)
- i = 0;
- else
- i -= 2;
- curwin->w_cursor.col = i;
- curwin->w_set_curswant = TRUE;
- ret_val = TRUE;
- }
- updateScreen(NOT_VALID);
- return ret_val;
- }
-