This is gui.c in view mode; [Download] [Up]
/* 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_position_components __ARGS((int, int)); static void gui_outstr __ARGS((char_u *, int)); static void gui_outstr_nowrap __ARGS((char_u *, int, int)); static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *)); static void gui_update_menus_recurse __ARGS((GuiMenu *, 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((int)); static void gui_update_horiz_scrollbar __ARGS((int)); /* * 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; #ifdef UNIX pid_t pid; #endif old_term = vim_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(TMODE_COOK); /* 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(TMODE_RAW); /* restart RAW mode */ } else highlight_gui_started(); /* re-init colors and fonts */ full_screen = TRUE; vim_free(old_term); #ifdef UNIX /* * 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() { char_u *env_str; WIN *wp; int i; gui.window_created = FALSE; gui.dying = FALSE; gui.in_focus = FALSE; gui.dragged_sb = SBAR_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 = 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 do_source((char_u *)SYS_MENU_FILE, FALSE); #endif /* * Switch on the mouse by default. * This can then be changed in the .gvimrc. */ set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE); /* * Get system wide defaults for gvim, only when filename defined. */ #ifdef SYS_GVIMRC_FILE do_source((char_u *)SYS_GVIMRC_FILE, 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, NULL, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); 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_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 i = FAIL; if ( fullpathcmp((char_u *)USR_GVIMRC_FILE, (char_u *)GVIMRC_FILE) != FPC_SAME #ifdef SYS_GVIMRC_FILE && fullpathcmp((char_u *)SYS_GVIMRC_FILE, (char_u *)GVIMRC_FILE) != FPC_SAME #endif ) i = do_source((char_u *)GVIMRC_FILE, FALSE); if (secure == 2) need_wait_return = TRUE; secure = 0; } /* * 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) { gui.in_use = FALSE; return; } /* * 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 (gui_init_font() == FAIL) { gui.in_use = FALSE; return; } 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) { gui.in_use = FALSE; return; } maketitle(); } 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]; int ret = FAIL; 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) { ret = OK; break; } } if (ret != OK) { /* * Couldn't load any font in '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(FALSE); } 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) INVALIDATE_CURSOR(); } void gui_update_cursor(force) int force; /* when TRUE, update even when not moved */ { int cur_width = 0; int cur_height = 0; long_u old_hl_mask; gui_check_screen(); if (gui.row != gui.cursor_row || gui.col != gui.cursor_col || force) { gui_undraw_cursor(); gui.cursor_row = gui.row; gui.cursor_col = gui.col; /* Only write to the screen after LinePointers[] has been initialized */ if (screen_cleared && NextScreen != NULL) { /* Clear the selection if we are about to write over it */ if (clipboard.state == SELECT_DONE && gui.row >= clipboard.start.lnum && gui.row <= clipboard.end.lnum) clip_clear_selection(); /* * In Insert mode: Draw a vertical bar at the left of the cursor * char. In Overstrike mode: Draw a horizontal bar at the bottom. * Otherwise: Draw a box, filled when our window is in focus. */ if (gui.row < screen_Rows && gui.col < screen_Columns) { if (gui.in_focus) { if (State == INSERT) cur_width = 3; else if (State == REPLACE) cur_height = 3; else if (State == CMDLINE && !cmdline_at_end()) { if (cmdline_overstrike()) cur_height = 3; else cur_width = 3; } } if (!gui.in_focus) { gui_mch_draw_hollow_cursor(); } else if (cur_height || cur_width) { if (cur_height == 0) cur_height = gui.char_height; if (cur_width == 0) cur_width = gui.char_width; gui_mch_draw_part_cursor(cur_width, cur_height); } else { old_hl_mask = gui.highlight_mask; gui.highlight_mask = *(LinePointers[gui.row] + gui.col + Columns); gui_outstr_nowrap(LinePointers[gui.row] + gui.col, 1, GUI_MON_IS_CURSOR); gui.highlight_mask = old_hl_mask; } } } } } void gui_position_menu() { if (gui.menu_is_active && gui.in_use) gui_mch_set_menu_pos(0, 0, gui.menu_width, gui.menu_height); } /* * Position the various GUI components (text area, menu). The vertical * scrollbars are NOT handled here. See gui_update_scrollbars(). */ static void gui_position_components(total_width, total_height) int total_width; int total_height; { int text_area_x; int text_area_y; int text_area_width; int text_area_height; gui.menu_width = total_width; text_area_x = 0; if (gui.which_scrollbars[SBAR_LEFT]) text_area_x += gui.scrollbar_width; text_area_y = 0; if (gui.menu_is_active) text_area_y = gui.menu_height; 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); gui_position_menu(); 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; } 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; if (gui.menu_is_active) base_height += gui.menu_height; return base_height; } /* * 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; { if (!gui.window_created) return; gui.num_cols = (pixel_width - gui_get_base_width()) / gui.char_width; gui.num_rows = (pixel_height - gui_get_base_height()) / gui.char_height; gui_position_components(pixel_width, pixel_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_scrollbars(TRUE); gui_update_cursor(FALSE); } int gui_get_winsize() { Rows = gui.num_rows; Columns = gui.num_cols; return OK; } /* * Set the size of the window according to Rows and Columns. */ void gui_set_winsize(fit_to_display) int fit_to_display; { int base_width; int base_height; int width; int height; int min_width; int min_height; int screen_w; int screen_h; if (!gui.window_created) return; base_width = gui_get_base_width(); base_height = gui_get_base_height(); width = Columns * gui.char_width + base_width; height = Rows * gui.char_height + base_height; if (fit_to_display) { gui_mch_get_screen_dimensions(&screen_w, &screen_h); if (width > screen_w) { Columns = (screen_w - base_width) / gui.char_width; if (Columns < MIN_COLUMNS) Columns = MIN_COLUMNS; gui.num_cols = Columns; gui_reset_scroll_region(); width = Columns * gui.char_width + base_width; } if (height > screen_h) { Rows = (screen_h - base_height) / gui.char_height; if (Rows < MIN_ROWS) Rows = MIN_ROWS; gui.num_rows = Rows; gui_reset_scroll_region(); height = Rows * gui.char_height + base_height; } } min_width = base_width + MIN_COLUMNS * gui.char_width; min_height = base_height + MIN_ROWS * gui.char_height; gui_mch_set_winsize(width, height, min_width, min_height, base_width, base_height); gui_position_components(width, height); gui_update_scrollbars(TRUE); } /* * 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; { if (mask > HL_ALL) /* highlight code */ gui.highlight_mask = mask; else /* mask */ gui.highlight_mask |= mask; } void gui_stop_highlight(mask) long_u mask; { if (mask > HL_ALL) /* highlight code */ gui.highlight_mask = HL_NORMAL; else /* mask */ gui.highlight_mask &= ~mask; } /* * Clear a rectangular region of the screen from text pos (row1, col1) to * (row2, col2) inclusive. */ void gui_clear_block(row1, col1, row2, col2) int row1; int col1; int row2; int col2; { /* Clear the selection if we are about to write over it */ if (clipboard.state == SELECT_DONE && row2 >= clipboard.start.lnum && row1 <= clipboard.end.lnum) clip_clear_selection(); gui_mch_clear_block(row1, col1, row2, col2); /* Invalidate cursor if it was in this block */ if (gui.cursor_row >= row1 && gui.cursor_row <= row2 && gui.cursor_col >= col1 && gui.cursor_col <= col2) INVALIDATE_CURSOR(); } 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') /* NL */ { 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') /* CR */ { len--; s++; gui.col = 0; } else if (s[0] == '\b') /* Backspace */ { len--; s++; if (gui.col) --gui.col; } 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_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_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(FALSE); gui_update_scrollbars(FALSE); /* * We need to make sure this is cleared since Athena doesn't tell us when * he is done dragging. */ gui.dragged_sb = SBAR_NONE; if (vim_strchr(p_guioptions, GO_ASEL) != NULL) clip_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_outstr_nowrap(s, this_len, GUI_MON_WRAP_CURSOR); s += this_len; len -= this_len; } gui_outstr_nowrap(s, len, GUI_MON_WRAP_CURSOR); } /* * Output the given string at the current cursor position. If the string is * too long to fit on the line, then it is truncated. GUI_MON_WRAP_CURSOR may * be used if the cursor position should be wrapped when the end of the line * is reached, however the string will still be truncated and not continue on * the next line. GUI_MON_IS_CURSOR should only be used when this function is * being called to actually draw (an inverted) cursor. */ static void gui_outstr_nowrap(s, len, flags) char_u *s; int len; int flags; { long_u highlight_mask; GuiColor fg_color; GuiColor bg_color; GuiFont font; struct attr_entry *aep = NULL; int fake_bold; int underline; if (len == 0) return; if (len < 0) len = STRLEN(s); if (gui.highlight_mask > HL_ALL) { aep = syn_gui_attr2entry(gui.highlight_mask); if (aep == NULL) /* highlighting not set */ highlight_mask = 0; else highlight_mask = aep->ae_attr; } else highlight_mask = gui.highlight_mask; if ((flags & GUI_MON_IS_CURSOR) && gui.in_focus) highlight_mask |= HL_INVERSE; /* Set the font */ if (aep != NULL && aep->ae_u.gui.font != 0) font = aep->ae_u.gui.font; else { if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font != 0) if ((highlight_mask & HL_ITALIC) && gui.boldital_font != 0) font = gui.boldital_font; else font = gui.bold_font; else if ((highlight_mask & HL_ITALIC) && gui.ital_font != 0) font = gui.ital_font; else font = gui.norm_font; } gui_mch_set_font(font); /* Set the color */ bg_color = gui.back_pixel; if ((flags & GUI_MON_IS_CURSOR) && gui.in_focus) fg_color = gui.cursor_pixel; else if (aep != NULL) { fg_color = aep->ae_u.gui.fg_color; if (fg_color == 0) fg_color = gui.norm_pixel; else --fg_color; bg_color = aep->ae_u.gui.bg_color; if (bg_color == 0) bg_color = gui.back_pixel; else --bg_color; } else { if (highlight_mask & (HL_BOLD | HL_STANDOUT)) fg_color = gui.bold_pixel; else if (highlight_mask & HL_ITALIC) fg_color = gui.ital_pixel; else if (highlight_mask & HL_UNDERLINE) fg_color = gui.underline_pixel; else fg_color = gui.norm_pixel; } if (highlight_mask & (HL_INVERSE | HL_STANDOUT)) { gui_mch_set_fg_color(bg_color); gui_mch_set_bg_color(fg_color); } else { gui_mch_set_fg_color(fg_color); gui_mch_set_bg_color(bg_color); } /* Clear the selection if we are about to write over it */ if (clipboard.state == SELECT_DONE && gui.row >= clipboard.start.lnum && gui.row <= clipboard.end.lnum) clip_clear_selection(); fake_bold = FALSE; underline = FALSE; /* If there's no bold font, then fake it */ if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && (gui.bold_font == 0 || (aep != NULL && aep->ae_u.gui.font != 0))) fake_bold = TRUE; /* Do we underline the text? */ if ((highlight_mask & HL_UNDERLINE) || ((highlight_mask & HL_ITALIC) && gui.ital_font == 0)) underline = TRUE; /* Draw the text */ gui_mch_draw_string(gui.row, gui.col, s, len, fake_bold, underline); if (!(flags & GUI_MON_IS_CURSOR)) { /* Invalidate the old physical cursor position if we wrote over it */ if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col && gui.cursor_col < gui.col + len) INVALIDATE_CURSOR(); /* Update the cursor position */ gui.col += len; if ((flags & GUI_MON_WRAP_CURSOR) && gui.col >= Columns) { gui.col = 0; gui.row++; } } } /* * 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(FALSE); if (clipboard.state != SELECT_CLEARED) clip_redraw_selection(x, y, w, h); } 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) { first_attr = attrp[0]; for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++) --len; gui.highlight_mask = first_attr; gui_outstr_nowrap(screenp, idx, 0); 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; } /* * The main GUI input routine. Waits for a character from the keyboard. * wtime == -1 Wait forever. * wtime == 0 Don't wait. * wtime > 0 Wait wtime milliseconds for a character. * Returns OK if a character was found to be available within the given time, * or FAIL otherwise. */ int gui_wait_for_chars(wtime) int wtime; { /* * If we're going to wait a bit, update the menus for the current * State. */ if (wtime != 0) gui_update_menus(0); gui_mch_update(); if (!is_input_buf_empty()) /* Got char, return immediately */ return OK; else if (wtime == 0) /* Don't wait for char */ return FAIL; else if (wtime > 0) return gui_mch_wait_for_chars(wtime); else { if (gui_mch_wait_for_chars(p_ut)) return OK; else { /* * If no characters arrive within 'updatetime' milli-seconds, flush * all the swap files to disk. */ updatescript(0); return gui_mch_wait_for_chars(-1); } } } /* * 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; #ifdef USE_CLIPBOARD int checkfor; /* If a non-Visual mode selection is in progress, finish it */ if (clipboard.state == SELECT_IN_PROGRESS) { clip_process_selection(button, x, y, repeated_click, modifiers); return; } /* Determine which mouse settings to look for based on the current mode */ switch (State) { case NORMAL_BUSY: case NORMAL: checkfor = MOUSE_NORMAL; break; case VISUAL: checkfor = MOUSE_VISUAL; break; case REPLACE: case INSERT: checkfor = MOUSE_INSERT; break; case HITRETURN: checkfor = MOUSE_RETURN; break; /* * On the command line, use the non-Visual mode selection on all * lines but the command line. */ case CMDLINE: if (Y_2_ROW(y) < cmdline_row) checkfor = ' '; else checkfor = MOUSE_COMMAND; break; default: checkfor = ' '; break; }; /* * Allow selection of text in the command line in "normal" modes. */ if ((State == NORMAL || State == NORMAL_BUSY || State == INSERT || State == REPLACE) && Y_2_ROW(y) >= gui.num_rows - p_ch) checkfor = ' '; /* * If the mouse settings say to not use the mouse, use the non-Visual mode * selection. But if Visual is active, assume that only the Visual area * will be selected. */ if (!mouse_has(checkfor) && !VIsual_active) { /* If the selection is done, allow the right button to extend it */ if (clipboard.state == SELECT_DONE && button == MOUSE_RIGHT) { clip_process_selection(button, x, y, repeated_click, modifiers); return; } /* Allow the left button to start the selection */ else if (button == MOUSE_LEFT) { clip_start_selection(button, x, y, repeated_click, modifiers); return; } } if (clipboard.state != SELECT_CLEARED) clip_clear_selection(); #endif 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; } /* * 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); } } /* * Used recursively by gui_update_menus (see below) */ static void gui_update_menus_recurse(menu, mode) GuiMenu *menu; int mode; { while (menu) { if (menu->modes & mode) { if (vim_strchr(p_guioptions, GO_GREY) != NULL) gui_mch_menu_grey(menu, FALSE); else gui_mch_menu_hidden(menu, FALSE); gui_update_menus_recurse(menu->children, mode); } else { if (vim_strchr(p_guioptions, GO_GREY) != NULL) gui_mch_menu_grey(menu, TRUE); else gui_mch_menu_hidden(menu, TRUE); } menu = menu->next; } } /* * Make sure only the valid menu items appear for this mode. If * force_menu_update is not TRUE, then we only do this if the mode has changed * since last time. If "modes" is not 0, then we use these modes instead. */ void gui_update_menus(modes) int modes; { static int prev_mode = -1; int mode = 0; if (modes != 0x0) mode = modes; else if (VIsual_active) mode = MENU_VISUAL_MODE; else if (State & NORMAL) mode = MENU_NORMAL_MODE; else if (State & INSERT) mode = MENU_INSERT_MODE; else if (State & CMDLINE) mode = MENU_CMDLINE_MODE; if (force_menu_update || mode != prev_mode) { gui_update_menus_recurse(gui.root_menu, mode); gui_mch_draw_menubar(); prev_mode = mode; force_menu_update = FALSE; } } /* * 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 old_menu_height; int i; /* Make a copy so we can stuff around with it, since it could be const */ path_name = vim_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 = vim_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) */ old_menu_height = gui.menu_height; gui_mch_add_menu(menu, parent); if (gui.menu_height != old_menu_height) gui_set_winsize(FALSE); } } *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 : vim_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 = vim_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 */ /* Highlight title */ MSG_PUTS_TITLE("\n--- Menus ---"); 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_putchar('\n'); if (got_int) /* "q" hit for "--more--" */ return; for (i = 0; i < depth; i++) MSG_PUTS(" "); /* Same highlighting as for directories!? */ msg_puts_attr(menu->name, highlight_attr[HLF_D]); } if (menu != NULL && menu->children == NULL) { for (bit = 0; bit < 4; bit++) if ((menu->modes & modes & (1 << bit)) != 0) { msg_putchar('\n'); if (got_int) /* "q" hit for "--more--" */ return; for (i = 0; i < depth + 2; i++) MSG_PUTS(" "); msg_putchar("nvic"[bit]); if (menu->noremap[bit]) msg_putchar('*'); else msg_putchar(' '); MSG_PUTS(" "); 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) vim_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++] = vim_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; { static int prev_which_scrollbars[3] = {-1, -1, -1}; static int prev_menu_is_active = -1; char_u *p; int i; int grey_old, grey_new; char_u *temp; WIN *wp; int changed; 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_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[SBAR_LEFT] = TRUE; break; case GO_RIGHT: gui.which_scrollbars[SBAR_RIGHT] = TRUE; break; case GO_BOT: gui.which_scrollbars[SBAR_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) { changed = FALSE; for (i = 0; i < 3; i++) { if (gui.which_scrollbars[i] != prev_which_scrollbars[i]) { if (i == SBAR_BOTTOM) { gui_mch_enable_scrollbar(&gui.bottom_sbar, gui.which_scrollbars[i]); } else { for (wp = firstwin; wp != NULL; wp = wp->w_next) gui_mch_enable_scrollbar(&wp->w_scrollbars[i], gui.which_scrollbars[i]); } changed = TRUE; } prev_which_scrollbars[i] = gui.which_scrollbars[i]; } if (gui.menu_is_active != prev_menu_is_active) { gui_mch_enable_menu(gui.menu_is_active); prev_menu_is_active = gui.menu_is_active; changed = TRUE; } if (changed) { /* must_redraw = CLEAR; */ /* if (vimForm != (Widget)NULL && XtIsRealized(vimForm)) */ gui_set_winsize(FALSE); } } } /* * Scrollbar stuff: */ void gui_create_scrollbar(sb, wp) GuiScrollbar *sb; WIN *wp; { static int sbar_ident = 0; int which; sb->ident = sbar_ident++; /* No check for too big, but would it happen? */ sb->wp = wp; sb->value = -1; sb->size = -1; sb->max = -1; sb->top = -1; sb->height = -1; sb->status_height = -1; gui_mch_create_scrollbar(sb, (wp == NULL) ? SBAR_HORIZ : SBAR_VERT); if (wp != NULL) { which = (sb == &wp->w_scrollbars[SBAR_LEFT]) ? SBAR_LEFT : SBAR_RIGHT; gui_mch_enable_scrollbar(sb, gui.which_scrollbars[which]); } } /* * Find the scrollbar with the given index. */ GuiScrollbar * gui_find_scrollbar(ident) long ident; { WIN *wp; if (gui.bottom_sbar.ident == ident) return &gui.bottom_sbar; for (wp = firstwin; wp != NULL; wp = wp->w_next) { if (wp->w_scrollbars[SBAR_LEFT].ident == ident) return &wp->w_scrollbars[SBAR_LEFT]; if (wp->w_scrollbars[SBAR_RIGHT].ident == ident) return &wp->w_scrollbars[SBAR_RIGHT]; } return NULL; } void gui_drag_scrollbar(sb, value, still_dragging) GuiScrollbar *sb; int value; int still_dragging; { char_u bytes[4 + sizeof(long_u)]; WIN *wp; int sb_num; int byte_count; if (sb == NULL) return; if (still_dragging) { if (sb->wp == NULL) gui.dragged_sb = SBAR_BOTTOM; else if (sb == &sb->wp->w_scrollbars[SBAR_LEFT]) gui.dragged_sb = SBAR_LEFT; else gui.dragged_sb = SBAR_RIGHT; gui.dragged_wp = sb->wp; } else gui.dragged_sb = SBAR_NONE; if (sb->wp != NULL) { /* Vertical sbar info is kept in the first sbar (the left one) */ sb = &sb->wp->w_scrollbars[0]; } /* * Check validity of value */ if (value < 0) value = 0; #ifdef SCROLL_PAST_END else if (value > sb->max) value = sb->max; #else if (value > sb->max - sb->size + 1) value = sb->max - sb->size + 1; #endif sb->value = value; if (sb->wp != NULL) { sb_num = 0; for (wp = firstwin; wp != sb->wp && wp != NULL; wp = wp->w_next) sb_num++; if (wp == NULL) return; bytes[0] = CSI; bytes[1] = KS_SCROLLBAR; bytes[2] = K_FILLER; bytes[3] = (char_u)sb_num; byte_count = 4; } else { bytes[0] = CSI; bytes[1] = KS_HORIZ_SCROLLBAR; bytes[2] = K_FILLER; byte_count = 3; } add_long_to_buf(value, bytes + byte_count); add_to_input_buf(bytes, byte_count + sizeof(long_u)); } /* * Vertical scrollbar stuff: */ static void gui_update_scrollbars(force) int force; /* Force all scrollbars to get updated */ { WIN *wp; GuiScrollbar *sb; int val, size, max; int which_sb; int cmdline_height; int h, y, tmp; /* Update the horizontal scrollbar */ gui_update_horiz_scrollbar(force); /* Return straight away if there is neither a left nor right scrollbar */ if (!gui.which_scrollbars[SBAR_LEFT] && !gui.which_scrollbars[SBAR_RIGHT]) return; /* * 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 == SBAR_LEFT || gui.dragged_sb == SBAR_RIGHT) && (!gui.which_scrollbars[SBAR_LEFT] || !gui.which_scrollbars[SBAR_RIGHT]) && !force) return; if (!force && (gui.dragged_sb == SBAR_LEFT || gui.dragged_sb == SBAR_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 = SBAR_LEFT + SBAR_RIGHT - gui.dragged_sb; if (gui.dragged_wp != NULL) gui_mch_set_scrollbar_thumb( &gui.dragged_wp->w_scrollbars[which_sb], gui.dragged_wp->w_scrollbars[0].value, gui.dragged_wp->w_scrollbars[0].size, gui.dragged_wp->w_scrollbars[0].max); 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 - 1; #else max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 2; #endif if (max < 0) /* empty buffer */ max = 0; val = wp->w_topline - 1; size = wp->w_height; #ifdef SCROLL_PAST_END if (val > max) /* just in case */ val = max; #else if (size > max + 1) /* just in case */ size = max + 1; if (val > max - size + 1) val = max - size + 1; #endif if (val < 0) /* minimal value is 0 */ val = 0; /* * Scrollbar at index 0 (the left one) contains all the information. * It would be the same info for left and right so we just store it for * one of them. */ sb = &wp->w_scrollbars[0]; /* * Note: no check for valid w_botline. If it's not valid the * scrollbars will be updated later anyway. */ if (size < 1 || wp->w_botline - 2 > max) { /* * This can happen during changing files. Just don't update the * scrollbar for now. */ sb->height = 0; /* Force update next time */ continue; } if (force || sb->height != wp->w_height || sb->top != wp->w_winpos || sb->status_height != wp->w_status_height) { /* Height or position of scrollbar has changed */ sb->top = wp->w_winpos; sb->height = wp->w_height; sb->status_height = wp->w_status_height; /* Calculate height and position in pixels */ h = sb->height * gui.char_height + sb->status_height * gui.char_height / 2; y = sb->top * gui.char_height + gui.border_offset; if (gui.menu_is_active) y += gui.menu_height; if (wp == firstwin) { /* Height of top scrollbar includes width of top border */ h += gui.border_offset; y -= gui.border_offset; } else { /* * Height of other scrollbars includes half of status bar above */ tmp = wp->w_prev->w_status_height * (gui.char_height + 1) / 2; h += tmp; y -= tmp; } if (gui.which_scrollbars[SBAR_LEFT]) gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_LEFT], gui.left_sbar_x, y, gui.scrollbar_width, h); if (gui.which_scrollbars[SBAR_RIGHT]) gui_mch_set_scrollbar_pos(&wp->w_scrollbars[SBAR_RIGHT], gui.right_sbar_x, y, gui.scrollbar_width, h); } if (force || sb->value != val || sb->size != size || sb->max != max) { /* Thumb of scrollbar has moved */ sb->value = val; sb->size = size; sb->max = max; if (gui.which_scrollbars[SBAR_LEFT] && gui.dragged_sb != SBAR_LEFT) gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_LEFT], val, size, max); if (gui.which_scrollbars[SBAR_RIGHT] && gui.dragged_sb != SBAR_RIGHT) gui_mch_set_scrollbar_thumb(&wp->w_scrollbars[SBAR_RIGHT], val, size, max); } } } /* * 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; long nlines; FPOS old_cursor; linenr_t old_topline; for (wp = firstwin, i = 0; i < current_scrollbar; i++) { if (wp == NULL) break; wp = wp->w_next; } if (wp == NULL) { /* Couldn't find window */ return FALSE; } /* * Compute number of lines to scroll. If zero, nothing to do. */ nlines = (long)scrollbar_value + 1 - (long)wp->w_topline; if (nlines == 0) return FALSE; old_cursor = curwin->w_cursor; old_wp = curwin; old_topline = wp->w_topline; curwin = wp; curbuf = wp->w_buffer; if (nlines < 0) scrolldown(-nlines); else scrollup(nlines); if (old_topline != wp->w_topline) { if (p_so) { cursor_correct(); /* fix window for 'so' */ update_topline(); /* avoid up/down jump */ } coladvance(curwin->w_curswant); } curwin = old_wp; curbuf = old_wp->w_buffer; /* * Don't call updateWindow() when nothing has changed (it will overwrite * the status line!). */ if (old_topline != wp->w_topline) { wp->w_redr_type = VALID; updateWindow(wp); /* update window, status line, and cmdline */ } return !equal(curwin->w_cursor, old_cursor); } /* * Horizontal scrollbar stuff: */ static void gui_update_horiz_scrollbar(force) int force; { int value, size, max; char_u *p; if (!gui.which_scrollbars[SBAR_BOTTOM]) return; if (!force && gui.dragged_sb == SBAR_BOTTOM) return; if (!force && 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) { gui.bottom_sbar.value = -1; return; } size = Columns; if (curwin->w_p_wrap) { value = 0; #ifdef SCROLL_PAST_END max = 0; #else max = Columns - 1; #endif } else { value = curwin->w_leftcol; /* Calculate max for horizontal scrollbar */ p = ml_get_curline(); max = 0; if (p != NULL && p[0] != NUL) while (p[1] != NUL) /* Don't count last character */ max += chartabsize(*p++, (colnr_t)max); #ifndef SCROLL_PAST_END max += Columns - 1; #endif } #ifndef SCROLL_PAST_END if (value > max - size + 1) value = max - size + 1; /* limit the value to allowable range */ #endif if (!force && value == gui.bottom_sbar.value && size == gui.bottom_sbar.size && max == gui.bottom_sbar.max) return; gui.bottom_sbar.value = value; gui.bottom_sbar.size = size; gui.bottom_sbar.max = max; gui.prev_wrap = curwin->w_p_wrap; gui_mch_set_scrollbar_thumb(&gui.bottom_sbar, value, size, max); } /* * Do a horizontal scroll. Return TRUE if the cursor moved, or FALSE otherwise */ int gui_do_horiz_scroll() { /* no wrapping, no scrolling */ if (curwin->w_p_wrap) return FALSE; curwin->w_leftcol = scrollbar_value; return leftcol_changed(); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.