Annotation of src/usr.bin/vim/gui.c, Revision 1.1
1.1 ! downsj 1: /* $OpenBSD$ */
! 2: /* vi:set ts=4 sw=4:
! 3: *
! 4: * VIM - Vi IMproved by Bram Moolenaar
! 5: * GUI/Motif support by Robert Webb
! 6: *
! 7: * Do ":help uganda" in Vim to read copying and usage conditions.
! 8: * Do ":help credits" in Vim to see a list of people who contributed.
! 9: */
! 10:
! 11: #include "vim.h"
! 12: #include "globals.h"
! 13: #include "proto.h"
! 14: #include "option.h"
! 15:
! 16: /* Structure containing all the GUI information */
! 17: Gui gui;
! 18:
! 19: /* Set to TRUE after adding/removing menus to ensure they are updated */
! 20: int force_menu_update = FALSE;
! 21:
! 22:
! 23: static void gui_check_screen __ARGS((void));
! 24: static void gui_outstr __ARGS((char_u *, int));
! 25: static void gui_update_selection __ARGS((void));
! 26: static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
! 27: static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
! 28: static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
! 29: static void gui_free_menu __ARGS((GuiMenu *));
! 30: static void gui_free_menu_string __ARGS((GuiMenu *, int));
! 31: static int gui_show_menus __ARGS((char_u *, int));
! 32: static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
! 33: static char_u *gui_menu_name_skip __ARGS((char_u *name));
! 34: static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
! 35: static void gui_update_scrollbars __ARGS((void));
! 36: static void gui_update_horiz_scrollbar __ARGS((void));
! 37:
! 38: /*
! 39: * The Athena scrollbars can move the thumb to after the end of the scrollbar,
! 40: * this makes the thumb indicate the part of the text that is shown. Motif
! 41: * can't do this.
! 42: */
! 43: #ifdef USE_GUI_ATHENA
! 44: # define SCROLL_PAST_END
! 45: #endif
! 46:
! 47: /*
! 48: * gui_start -- Called when user wants to start the GUI.
! 49: */
! 50: void
! 51: gui_start()
! 52: {
! 53: char_u *old_term;
! 54:
! 55: old_term = strsave(term_strings[KS_NAME]);
! 56: mch_setmouse(FALSE); /* first switch mouse off */
! 57:
! 58: /* set_termname() will call gui_init() to start the GUI */
! 59: termcapinit((char_u *)"builtin_gui");
! 60:
! 61: if (!gui.in_use) /* failed to start GUI */
! 62: termcapinit(old_term);
! 63:
! 64: vim_free(old_term);
! 65:
! 66: /*
! 67: * Quit the current process and continue in the child.
! 68: * Makes "gvim file" disconnect from the shell it was started in.
! 69: * Don't do this when Vim was started with "-f" or the 'f' flag is present
! 70: * in 'guioptions'.
! 71: */
! 72: if (gui.in_use && gui.dofork &&
! 73: vim_strchr(p_guioptions, GO_FORG) == NULL && fork() > 0)
! 74: exit(0);
! 75: }
! 76:
! 77: /*
! 78: * Call this when vim starts up, whether or not the GUI is started
! 79: */
! 80: void
! 81: gui_prepare(argc, argv)
! 82: int *argc;
! 83: char **argv;
! 84: {
! 85: /* Menu items may be added before the GUI is started, so set this now */
! 86: gui.root_menu = NULL;
! 87: gui.in_use = FALSE; /* No GUI yet (maybe later) */
! 88: gui.starting = FALSE; /* No GUI yet (maybe later) */
! 89: gui.dofork = TRUE; /* default is to use fork() */
! 90: gui_mch_prepare(argc, argv);
! 91: }
! 92:
! 93: static struct default_menu
! 94: {
! 95: char *name; /* name of menu item */
! 96: int mode; /* mode where menu is valid */
! 97: char *command; /* resulting command */
! 98: } default_menus[] =
! 99: {
! 100: /* Help menu. Some reason Motif is happier if this is added first. */
! 101: {"Help.Overview <F1>", MENU_NORMAL_MODE, ":help\r"},
! 102: {"Help.Overview <F1>", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 103: "\033:help\r"},
! 104: {"Help.How to\\.\\.\\.",MENU_NORMAL_MODE, ":help how_to\r"},
! 105: {"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 106: "\033:help how_to\r"},
! 107: {"Help.GUI", MENU_NORMAL_MODE, ":help gui\r"},
! 108: {"Help.GUI", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 109: "\033:help gui\r"},
! 110: {"Help.Version", MENU_NORMAL_MODE, ":version\r"},
! 111: {"Help.Version", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 112: "\033:version\r"},
! 113: {"Help.Credits", MENU_NORMAL_MODE, ":help credits\r"},
! 114: {"Help.Credits", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 115: "\033:help credits\r"},
! 116: {"Help.Copying", MENU_NORMAL_MODE, ":help uganda\r"},
! 117: {"Help.Copying", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 118: "\033:help uganda\r"},
! 119:
! 120: /* File menu */
! 121: {"File.Save :w", MENU_NORMAL_MODE, ":w\r"},
! 122: {"File.Save :w", MENU_INSERT_MODE, "\017:w\r"},
! 123:
! 124: {"File.Close :q", MENU_NORMAL_MODE, ":q\r"},
! 125: {"File.Close :q", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 126: "\033:q\r"},
! 127:
! 128: {"File.Quit :qa", MENU_NORMAL_MODE, ":qa\r"},
! 129: {"File.Quit :qa", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 130: "\033:qa\r"},
! 131:
! 132: {"File.Save-Quit :wqa",MENU_NORMAL_MODE, ":wqa\r"},
! 133: {"File.Save-Quit :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 134: "\033:wqa\r"},
! 135:
! 136: /* Edit menu */
! 137: {"Edit.Undo", MENU_NORMAL_MODE, "u"},
! 138: {"Edit.Redo", MENU_NORMAL_MODE, "\022"},
! 139:
! 140: {"Edit.Cut", MENU_VISUAL_MODE, "x"},
! 141: {"Edit.Copy", MENU_VISUAL_MODE, "y"},
! 142: {"Edit.Put Before", MENU_NORMAL_MODE, "[p"},
! 143: {"Edit.Put Before", MENU_INSERT_MODE, "\017[p"},
! 144: {"Edit.Put After", MENU_NORMAL_MODE, "]p"},
! 145: {"Edit.Put After", MENU_INSERT_MODE, "\017]p"},
! 146: {"Edit.Paste", MENU_NORMAL_MODE, "i\022*\033"}, /* CTRL-R * */
! 147: {"Edit.Paste", MENU_INSERT_MODE|MENU_CMDLINE_MODE,
! 148: "\022*"}, /* CTRL-R * */
! 149: {NULL, 0, NULL}
! 150: };
! 151:
! 152: /*
! 153: * This is the call which starts the GUI.
! 154: */
! 155: void
! 156: gui_init()
! 157: {
! 158: char_u *env_str;
! 159: int i;
! 160:
! 161: gui.dying = FALSE;
! 162: gui.in_focus = FALSE;
! 163: gui.dragged_sb = SB_NONE;
! 164: gui.dragged_wp = NULL;
! 165: gui.col = gui.num_cols = 0;
! 166: gui.row = gui.num_rows = 0;
! 167:
! 168: /* Initialise gui.cursor_row: */
! 169: INVALIDATE_CURSOR();
! 170: gui.scroll_region_top = 0;
! 171: gui.scroll_region_bot = Rows - 1;
! 172: gui.highlight_mask = HL_NORMAL;
! 173: gui.char_width = 0;
! 174: gui.char_height = 0;
! 175: gui.char_ascent = 0;
! 176: gui.border_width = 0;
! 177:
! 178: gui.selection.owned = FALSE;
! 179: gui.selection.start.lnum = 0;
! 180: gui.selection.start.col = 0;
! 181: gui.selection.end.lnum = 0;
! 182: gui.selection.end.col = 0;
! 183: gui.selection.state = SELECT_CLEARED;
! 184:
! 185: gui.root_menu = NULL;
! 186: gui.menu_is_active = TRUE; /* default: include menu */
! 187:
! 188: gui.scrollbar_width = SB_DEFAULT_WIDTH;
! 189: gui.menu_height = MENU_DEFAULT_HEIGHT;
! 190: for (i = 0; i < 3; i++)
! 191: gui.new_sb[i] = FALSE;
! 192:
! 193: gui.prev_wrap = -1;
! 194:
! 195: for (i = 0; default_menus[i].name != NULL; ++i)
! 196: gui_add_menu_path((char_u *)default_menus[i].name,
! 197: default_menus[i].mode, gui_menu_cb,
! 198: (char_u *)default_menus[i].command, TRUE);
! 199:
! 200: /*
! 201: * Switch on the mouse by default.
! 202: * This can be changed in the .gvimrc.
! 203: */
! 204: set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);
! 205:
! 206: /*
! 207: * Get system wide defaults for gvim (Unix only)
! 208: */
! 209: #ifdef HAVE_CONFIG_H
! 210: do_source(sys_gvimrc_fname, FALSE);
! 211: #endif
! 212:
! 213: /*
! 214: * Try to read GUI initialization commands from the following places:
! 215: * - environment variable GVIMINIT
! 216: * - the user gvimrc file (~/.gvimrc for Unix)
! 217: * The first that exists is used, the rest is ignored.
! 218: */
! 219: if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
! 220: {
! 221: sourcing_name = (char_u *)"GVIMINIT";
! 222: do_cmdline(env_str, TRUE, TRUE);
! 223: sourcing_name = NULL;
! 224: }
! 225: else
! 226: do_source((char_u *)USR_GVIMRC_FILE, FALSE);
! 227:
! 228: /*
! 229: * Read initialization commands from ".gvimrc" in current directory. This
! 230: * is only done if the 'exrc' option is set. Because of security reasons
! 231: * we disallow shell and write commands now, except for unix if the file is
! 232: * owned by the user or 'secure' option has been reset in environment of
! 233: * global ".gvimrc". Only do this if GVIMRC_FILE is not the same as
! 234: * USR_GVIMRC_FILE or sys_gvimrc_fname.
! 235: */
! 236: if (p_exrc)
! 237: {
! 238: #ifdef UNIX
! 239: {
! 240: struct stat s;
! 241:
! 242: /* if ".gvimrc" file is not owned by user, set 'secure' mode */
! 243: if (stat(GVIMRC_FILE, &s) || s.st_uid != getuid())
! 244: secure = p_secure;
! 245: }
! 246: #else
! 247: secure = p_secure;
! 248: #endif
! 249:
! 250: i = FAIL;
! 251: if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
! 252: (char_u *)GVIMRC_FILE) != FPC_SAME
! 253: #ifdef HAVE_CONFIG_H
! 254: && fullpathcmp(sys_gvimrc_fname,
! 255: (char_u *)GVIMRC_FILE) != FPC_SAME
! 256: #endif
! 257: )
! 258: i = do_source((char_u *)GVIMRC_FILE, FALSE);
! 259: }
! 260:
! 261: /*
! 262: * Actually start the GUI itself.
! 263: */
! 264: gui.in_use = TRUE; /* Must be set after menus have been set up */
! 265: if (gui_mch_init() == FAIL)
! 266: {
! 267: gui.in_use = FALSE;
! 268: return;
! 269: }
! 270:
! 271: maketitle();
! 272:
! 273: gui_create_initial_menus(gui.root_menu, NULL);
! 274: }
! 275:
! 276: void
! 277: gui_exit()
! 278: {
! 279: gui.in_use = FALSE;
! 280: gui_mch_exit();
! 281: }
! 282:
! 283: /*
! 284: * Set the font. Uses the 'font' option. The first font name that works is
! 285: * used. If none is found, use the default font.
! 286: */
! 287: int
! 288: gui_init_font()
! 289: {
! 290: #define FONTLEN 100
! 291: char_u *font_list;
! 292: char_u font_name[FONTLEN];
! 293:
! 294: if (!gui.in_use)
! 295: return FAIL;
! 296:
! 297: for (font_list = p_guifont; *font_list != NUL; )
! 298: {
! 299: /* Isolate one font name */
! 300: (void)copy_option_part(&font_list, font_name, FONTLEN, ",");
! 301: if (gui_mch_init_font(font_name) == OK)
! 302: return OK;
! 303: }
! 304:
! 305: /*
! 306: * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
! 307: * find a font we can load.
! 308: */
! 309: return gui_mch_init_font(NULL);
! 310: }
! 311:
! 312: void
! 313: gui_set_cursor(row, col)
! 314: int row;
! 315: int col;
! 316: {
! 317: gui.row = row;
! 318: gui.col = col;
! 319: }
! 320:
! 321: /*
! 322: * gui_check_screen - check if the cursor is on the screen.
! 323: */
! 324: static void
! 325: gui_check_screen()
! 326: {
! 327: if (gui.row >= Rows)
! 328: gui.row = Rows - 1;
! 329: if (gui.col >= Columns)
! 330: gui.col = Columns - 1;
! 331: if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
! 332: INVALIDATE_CURSOR();
! 333: }
! 334:
! 335: void
! 336: gui_update_cursor()
! 337: {
! 338: gui_check_screen();
! 339: if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
! 340: {
! 341: gui_undraw_cursor();
! 342: gui.cursor_row = gui.row;
! 343: gui.cursor_col = gui.col;
! 344: gui_mch_draw_cursor();
! 345: }
! 346: }
! 347:
! 348: /*
! 349: * Should be called after the GUI window has been resized. Its arguments are
! 350: * the new width and height of the window in pixels.
! 351: */
! 352: void
! 353: gui_resize_window(pixel_width, pixel_height)
! 354: int pixel_width;
! 355: int pixel_height;
! 356: {
! 357: gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
! 358: gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;
! 359:
! 360: gui_reset_scroll_region();
! 361: /*
! 362: * At the "more" prompt there is no redraw, put the cursor at the last
! 363: * line here (why does it have to be one row too low???).
! 364: */
! 365: if (State == ASKMORE)
! 366: gui.row = gui.num_rows;
! 367:
! 368: if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
! 369: set_winsize(0, 0, FALSE);
! 370: gui_update_cursor();
! 371: }
! 372:
! 373: /*
! 374: * Make scroll region cover whole screen.
! 375: */
! 376: void
! 377: gui_reset_scroll_region()
! 378: {
! 379: gui.scroll_region_top = 0;
! 380: gui.scroll_region_bot = gui.num_rows - 1;
! 381: }
! 382:
! 383: void
! 384: gui_start_highlight(mask)
! 385: long_u mask;
! 386: {
! 387: gui.highlight_mask |= mask;
! 388: }
! 389:
! 390: void
! 391: gui_stop_highlight(mask)
! 392: long_u mask;
! 393: {
! 394: gui.highlight_mask &= ~mask;
! 395: }
! 396:
! 397: void
! 398: gui_write(s, len)
! 399: char_u *s;
! 400: int len;
! 401: {
! 402: char_u *p;
! 403: int arg1 = 0, arg2 = 0;
! 404:
! 405: /* #define DEBUG_GUI_WRITE */
! 406: #ifdef DEBUG_GUI_WRITE
! 407: {
! 408: int i;
! 409: char_u *str;
! 410:
! 411: printf("gui_write(%d):\n ", len);
! 412: for (i = 0; i < len; i++)
! 413: if (s[i] == ESC)
! 414: {
! 415: if (i != 0)
! 416: printf("\n ");
! 417: printf("<ESC>");
! 418: }
! 419: else
! 420: {
! 421: str = transchar(s[i]);
! 422: if (str[0] && str[1])
! 423: printf("<%s>", (char *)str);
! 424: else
! 425: printf("%s", (char *)str);
! 426: }
! 427: printf("\n");
! 428: }
! 429: #endif
! 430: while (len)
! 431: {
! 432: if (s[0] == '\n')
! 433: {
! 434: len--;
! 435: s++;
! 436: gui.col = 0;
! 437: if (gui.row < gui.scroll_region_bot)
! 438: gui.row++;
! 439: else
! 440: gui_mch_delete_lines(gui.scroll_region_top, 1);
! 441: }
! 442: else if (s[0] == '\r')
! 443: {
! 444: len--;
! 445: s++;
! 446: gui.col = 0;
! 447: }
! 448: else if (s[0] == Ctrl('G')) /* Beep */
! 449: {
! 450: gui_mch_beep();
! 451: len--;
! 452: s++;
! 453: }
! 454: else if (s[0] == ESC && s[1] == '|')
! 455: {
! 456: p = s + 2;
! 457: if (isdigit(*p))
! 458: {
! 459: arg1 = getdigits(&p);
! 460: if (p > s + len)
! 461: break;
! 462: if (*p == ';')
! 463: {
! 464: ++p;
! 465: arg2 = getdigits(&p);
! 466: if (p > s + len)
! 467: break;
! 468: }
! 469: }
! 470: switch (*p)
! 471: {
! 472: case 'C': /* Clear screen */
! 473: gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
! 474: break;
! 475: case 'M': /* Move cursor */
! 476: gui_set_cursor(arg1, arg2);
! 477: break;
! 478: case 'R': /* Set scroll region */
! 479: if (arg1 < arg2)
! 480: {
! 481: gui.scroll_region_top = arg1;
! 482: gui.scroll_region_bot = arg2;
! 483: }
! 484: else
! 485: {
! 486: gui.scroll_region_top = arg2;
! 487: gui.scroll_region_bot = arg1;
! 488: }
! 489: break;
! 490: case 'd': /* Delete line */
! 491: gui_mch_delete_lines(gui.row, 1);
! 492: break;
! 493: case 'D': /* Delete lines */
! 494: gui_mch_delete_lines(gui.row, arg1);
! 495: break;
! 496: case 'i': /* Insert line */
! 497: gui_mch_insert_lines(gui.row, 1);
! 498: break;
! 499: case 'I': /* Insert lines */
! 500: gui_mch_insert_lines(gui.row, arg1);
! 501: break;
! 502: case '$': /* Clear to end-of-line */
! 503: gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
! 504: break;
! 505: case 'h': /* Turn on highlighting */
! 506: gui_start_highlight(arg1);
! 507: break;
! 508: case 'H': /* Turn off highlighting */
! 509: gui_stop_highlight(arg1);
! 510: break;
! 511: case 'f': /* flash the window (visual bell) */
! 512: gui_mch_flash();
! 513: break;
! 514: default:
! 515: p = s + 1; /* Skip the ESC */
! 516: break;
! 517: }
! 518: len -= ++p - s;
! 519: s = p;
! 520: }
! 521: else if (s[0] < 0x20) /* Ctrl character, shouldn't happen */
! 522: {
! 523: /*
! 524: * For some reason vim sends me a ^M after hitting return on the
! 525: * ':' line. Make sure we ignore this here.
! 526: */
! 527: len--; /* Skip this char */
! 528: s++;
! 529: }
! 530: else
! 531: {
! 532: p = s;
! 533: while (len && *p >= 0x20)
! 534: {
! 535: len--;
! 536: p++;
! 537: }
! 538: gui_outstr(s, p - s);
! 539: s = p;
! 540: }
! 541: }
! 542: gui_update_cursor();
! 543: gui_update_scrollbars();
! 544: gui_update_horiz_scrollbar();
! 545:
! 546: /*
! 547: * We need to make sure this is cleared since Athena doesn't tell us when
! 548: * he is done dragging.
! 549: */
! 550: gui.dragged_sb = SB_NONE;
! 551:
! 552: if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
! 553: gui_update_selection();
! 554: gui_mch_flush(); /* In case vim decides to take a nap */
! 555: }
! 556:
! 557: static void
! 558: gui_outstr(s, len)
! 559: char_u *s;
! 560: int len;
! 561: {
! 562: int this_len;
! 563:
! 564: if (len == 0)
! 565: return;
! 566:
! 567: if (len < 0)
! 568: len = STRLEN(s);
! 569:
! 570: while (gui.col + len > Columns)
! 571: {
! 572: this_len = Columns - gui.col;
! 573: gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
! 574: s += this_len;
! 575: len -= this_len;
! 576: }
! 577: gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
! 578: }
! 579:
! 580: /*
! 581: * Un-draw the cursor. Actually this just redraws the character at the given
! 582: * position.
! 583: */
! 584: void
! 585: gui_undraw_cursor()
! 586: {
! 587: if (IS_CURSOR_VALID())
! 588: gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
! 589: gui.cursor_col);
! 590: }
! 591:
! 592: void
! 593: gui_redraw(x, y, w, h)
! 594: int x;
! 595: int y;
! 596: int w;
! 597: int h;
! 598: {
! 599: int row1, col1, row2, col2;
! 600:
! 601: row1 = Y_2_ROW(y);
! 602: col1 = X_2_COL(x);
! 603: row2 = Y_2_ROW(y + h - 1);
! 604: col2 = X_2_COL(x + w - 1);
! 605:
! 606: gui_redraw_block(row1, col1, row2, col2);
! 607:
! 608: /* We may need to redraw the cursor */
! 609: gui_update_cursor();
! 610: }
! 611:
! 612: void
! 613: gui_redraw_block(row1, col1, row2, col2)
! 614: int row1;
! 615: int col1;
! 616: int row2;
! 617: int col2;
! 618: {
! 619: int old_row, old_col;
! 620: long_u old_hl_mask;
! 621: char_u *screenp, *attrp, first_attr;
! 622: int idx, len;
! 623:
! 624: /* Don't try to draw outside the window! */
! 625: /* Check everything, strange values may be caused by big border width */
! 626: col1 = check_col(col1);
! 627: col2 = check_col(col2);
! 628: row1 = check_row(row1);
! 629: row2 = check_row(row2);
! 630:
! 631: /* Don't try to update when NextScreen is not valid */
! 632: if (!screen_cleared || NextScreen == NULL)
! 633: return;
! 634:
! 635: /* Remember where our cursor was */
! 636: old_row = gui.row;
! 637: old_col = gui.col;
! 638: old_hl_mask = gui.highlight_mask;
! 639:
! 640: for (gui.row = row1; gui.row <= row2; gui.row++)
! 641: {
! 642: gui.col = col1;
! 643: screenp = LinePointers[gui.row] + gui.col;
! 644: attrp = screenp + Columns;
! 645: len = col2 - col1 + 1;
! 646: while (len > 0)
! 647: {
! 648: switch (attrp[0])
! 649: {
! 650: case CHAR_INVERT:
! 651: gui.highlight_mask = HL_INVERSE;
! 652: break;
! 653: case CHAR_UNDERL:
! 654: gui.highlight_mask = HL_UNDERLINE;
! 655: break;
! 656: case CHAR_BOLD:
! 657: gui.highlight_mask = HL_BOLD;
! 658: break;
! 659: case CHAR_STDOUT:
! 660: gui.highlight_mask = HL_STANDOUT;
! 661: break;
! 662: case CHAR_ITALIC:
! 663: gui.highlight_mask = HL_ITAL;
! 664: break;
! 665: case CHAR_NORMAL:
! 666: default:
! 667: gui.highlight_mask = HL_NORMAL;
! 668: break;
! 669: }
! 670: first_attr = attrp[0];
! 671: for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
! 672: --len;
! 673: gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
! 674: screenp += idx;
! 675: attrp += idx;
! 676: }
! 677: }
! 678:
! 679: /* Put the cursor back where it was */
! 680: gui.row = old_row;
! 681: gui.col = old_col;
! 682: gui.highlight_mask = old_hl_mask;
! 683: }
! 684:
! 685: /*
! 686: * Check bounds for column number
! 687: */
! 688: int
! 689: check_col(col)
! 690: int col;
! 691: {
! 692: if (col < 0)
! 693: return 0;
! 694: if (col >= (int)Columns)
! 695: return (int)Columns - 1;
! 696: return col;
! 697: }
! 698:
! 699: /*
! 700: * Check bounds for row number
! 701: */
! 702: int
! 703: check_row(row)
! 704: int row;
! 705: {
! 706: if (row < 0)
! 707: return 0;
! 708: if (row >= (int)Rows)
! 709: return (int)Rows - 1;
! 710: return row;
! 711: }
! 712:
! 713: /*
! 714: * Generic mouse support function. Add a mouse event to the input buffer with
! 715: * the given properties.
! 716: * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
! 717: * MOUSE_DRAG, or MOUSE_RELEASE.
! 718: * x, y --- Coordinates of mouse in pixels.
! 719: * repeated_click --- TRUE if this click comes only a short time after a
! 720: * previous click.
! 721: * modifiers --- Bit field which may be any of the following modifiers
! 722: * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
! 723: * This function will ignore drag events where the mouse has not moved to a new
! 724: * character.
! 725: */
! 726: void
! 727: gui_send_mouse_event(button, x, y, repeated_click, modifiers)
! 728: int button;
! 729: int x;
! 730: int y;
! 731: int repeated_click;
! 732: int_u modifiers;
! 733: {
! 734: static int prev_row = 0, prev_col = 0;
! 735: static int prev_button = -1;
! 736: static linenr_t prev_topline = 0;
! 737: static int num_clicks = 1;
! 738: char_u string[6];
! 739: int row, col;
! 740:
! 741: row = Y_2_ROW(y);
! 742: col = X_2_COL(x);
! 743:
! 744: /*
! 745: * If we are dragging and the mouse hasn't moved far enough to be on a
! 746: * different character, then don't send an event to vim.
! 747: */
! 748: if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
! 749: return;
! 750:
! 751: /*
! 752: * If topline has changed (window scrolled) since the last click, reset
! 753: * repeated_click, because we don't want starting Visual mode when
! 754: * clicking on a different character in the text.
! 755: */
! 756: if (curwin->w_topline != prev_topline)
! 757: repeated_click = FALSE;
! 758:
! 759: string[0] = CSI; /* this sequence is recognized by check_termcode() */
! 760: string[1] = KS_MOUSE;
! 761: string[2] = K_FILLER;
! 762: if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
! 763: {
! 764: if (repeated_click)
! 765: {
! 766: /*
! 767: * Handle multiple clicks. They only count if the mouse is still
! 768: * pointing at the same character.
! 769: */
! 770: if (button != prev_button || row != prev_row || col != prev_col)
! 771: num_clicks = 1;
! 772: else if (++num_clicks > 4)
! 773: num_clicks = 1;
! 774: }
! 775: else
! 776: num_clicks = 1;
! 777: prev_button = button;
! 778: prev_topline = curwin->w_topline;
! 779:
! 780: string[3] = (char_u)(button | 0x20);
! 781: SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
! 782: }
! 783: else
! 784: string[3] = (char_u)button;
! 785:
! 786: string[3] |= modifiers;
! 787: string[4] = (char_u)(col + ' ' + 1);
! 788: string[5] = (char_u)(row + ' ' + 1);
! 789: add_to_input_buf(string, 6);
! 790:
! 791: prev_row = row;
! 792: prev_col = col;
! 793: }
! 794:
! 795: /*
! 796: * Selection stuff, for cutting and pasting text to other windows.
! 797: */
! 798:
! 799: /*
! 800: * Check whether the VIsual area has changed, and if so try to become the owner
! 801: * of the selection, and free any old converted selection we may still have
! 802: * lying around. If the VIsual mode has ended, make a copy of what was
! 803: * selected so we can still give it to others. Will probably have to make sure
! 804: * this is called whenever VIsual mode is ended.
! 805: */
! 806: static void
! 807: gui_update_selection()
! 808: {
! 809: /* If visual mode is only due to a redo command ("."), then ignore it */
! 810: if (redo_VIsual_busy)
! 811: return;
! 812: if (!VIsual_active)
! 813: {
! 814: gui_mch_clear_selection();
! 815: gui.selection.start = gui.selection.end = VIsual;
! 816: }
! 817: else if (lt(VIsual, curwin->w_cursor))
! 818: {
! 819: if (!equal(gui.selection.start, VIsual) ||
! 820: !equal(gui.selection.end, curwin->w_cursor))
! 821: {
! 822: gui_mch_clear_selection();
! 823: gui.selection.start = VIsual;
! 824: gui.selection.end = curwin->w_cursor;
! 825: gui_free_selection();
! 826: gui_own_selection();
! 827: }
! 828: }
! 829: else
! 830: {
! 831: if (!equal(gui.selection.start, curwin->w_cursor) ||
! 832: !equal(gui.selection.end, VIsual))
! 833: {
! 834: gui_mch_clear_selection();
! 835: gui.selection.start = curwin->w_cursor;
! 836: gui.selection.end = VIsual;
! 837: gui_free_selection();
! 838: gui_own_selection();
! 839: }
! 840: }
! 841: }
! 842:
! 843: void
! 844: gui_own_selection()
! 845: {
! 846: /*
! 847: * Also want to check somehow that we are reading from the keyboard rather
! 848: * than a mapping etc.
! 849: */
! 850: if (!gui.selection.owned && gui_mch_own_selection())
! 851: {
! 852: gui_free_selection();
! 853: gui.selection.owned = TRUE;
! 854: }
! 855: }
! 856:
! 857: void
! 858: gui_lose_selection()
! 859: {
! 860: gui_free_selection();
! 861: gui.selection.owned = FALSE;
! 862: gui_mch_lose_selection();
! 863: }
! 864:
! 865: void
! 866: gui_copy_selection()
! 867: {
! 868: if (VIsual_active)
! 869: {
! 870: if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
! 871: gui_update_selection();
! 872: gui_own_selection();
! 873: if (gui.selection.owned)
! 874: gui_get_selection();
! 875: }
! 876: }
! 877:
! 878: void
! 879: gui_auto_select()
! 880: {
! 881: if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
! 882: gui_copy_selection();
! 883: }
! 884:
! 885: /*
! 886: * Menu stuff.
! 887: */
! 888:
! 889: void
! 890: gui_menu_cb(menu)
! 891: GuiMenu *menu;
! 892: {
! 893: char_u bytes[3 + sizeof(long_u)];
! 894:
! 895: bytes[0] = CSI;
! 896: bytes[1] = KS_MENU;
! 897: bytes[2] = K_FILLER;
! 898: add_long_to_buf((long_u)menu, bytes + 3);
! 899: add_to_input_buf(bytes, 3 + sizeof(long_u));
! 900: }
! 901:
! 902: /*
! 903: * Return the index into the menu->strings or menu->noremap arrays for the
! 904: * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
! 905: * given menu in the current mode.
! 906: */
! 907: int
! 908: gui_get_menu_index(menu, state)
! 909: GuiMenu *menu;
! 910: int state;
! 911: {
! 912: int idx;
! 913:
! 914: if (VIsual_active)
! 915: idx = MENU_INDEX_VISUAL;
! 916: else if ((state & NORMAL))
! 917: idx = MENU_INDEX_NORMAL;
! 918: else if ((state & INSERT))
! 919: idx = MENU_INDEX_INSERT;
! 920: else if ((state & CMDLINE))
! 921: idx = MENU_INDEX_CMDLINE;
! 922: else
! 923: idx = MENU_INDEX_INVALID;
! 924:
! 925: if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
! 926: idx = MENU_INDEX_INVALID;
! 927: return idx;
! 928: }
! 929:
! 930: /*
! 931: * Return the modes specified by the given menu command (eg :menu! returns
! 932: * MENU_CMDLINE_MODE | MENU_INSERT_MODE). If noremap is not NULL, then the
! 933: * flag it points to is set according to whether the command is a "nore"
! 934: * command. If unmenu is not NULL, then the flag it points to is set
! 935: * according to whether the command is an "unmenu" command.
! 936: */
! 937: static int
! 938: gui_get_menu_cmd_modes(cmd, force, noremap, unmenu)
! 939: char_u *cmd;
! 940: int force; /* Was there a "!" after the command? */
! 941: int *noremap;
! 942: int *unmenu;
! 943: {
! 944: int modes = 0x0;
! 945:
! 946: if (*cmd == 'n' && cmd[1] != 'o') /* nmenu, nnoremenu */
! 947: {
! 948: modes |= MENU_NORMAL_MODE;
! 949: cmd++;
! 950: }
! 951: else if (*cmd == 'v') /* vmenu, vnoremenu */
! 952: {
! 953: modes |= MENU_VISUAL_MODE;
! 954: cmd++;
! 955: }
! 956: else if (*cmd == 'i') /* imenu, inoremenu */
! 957: {
! 958: modes |= MENU_INSERT_MODE;
! 959: cmd++;
! 960: }
! 961: else if (*cmd == 'c') /* cmenu, cnoremenu */
! 962: {
! 963: modes |= MENU_CMDLINE_MODE;
! 964: cmd++;
! 965: }
! 966: else if (force) /* menu!, noremenu! */
! 967: modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
! 968: else /* menu, noremenu */
! 969: modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;
! 970:
! 971: if (noremap != NULL)
! 972: *noremap = (*cmd == 'n');
! 973: if (unmenu != NULL)
! 974: *unmenu = (*cmd == 'u');
! 975: return modes;
! 976: }
! 977:
! 978: /*
! 979: * Do the :menu commands.
! 980: */
! 981: void
! 982: gui_do_menu(cmd, arg, force)
! 983: char_u *cmd;
! 984: char_u *arg;
! 985: int force;
! 986: {
! 987: char_u *menu_path;
! 988: int modes;
! 989: char_u *map_to;
! 990: int noremap;
! 991: int unmenu;
! 992: char_u *map_buf;
! 993:
! 994: modes = gui_get_menu_cmd_modes(cmd, force, &noremap, &unmenu);
! 995: menu_path = arg;
! 996: if (*menu_path == NUL)
! 997: {
! 998: gui_show_menus(menu_path, modes);
! 999: return;
! 1000: }
! 1001: while (*arg && !vim_iswhite(*arg))
! 1002: {
! 1003: if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
! 1004: arg++;
! 1005: arg++;
! 1006: }
! 1007: if (*arg != NUL)
! 1008: *arg++ = NUL;
! 1009: arg = skipwhite(arg);
! 1010: map_to = arg;
! 1011: if (*map_to == NUL && !unmenu)
! 1012: {
! 1013: gui_show_menus(menu_path, modes);
! 1014: return;
! 1015: }
! 1016: else if (*map_to != NUL && unmenu)
! 1017: {
! 1018: EMSG("Trailing characters");
! 1019: return;
! 1020: }
! 1021: if (unmenu)
! 1022: {
! 1023: if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
! 1024: menu_path = (char_u *)"";
! 1025: gui_remove_menu(&gui.root_menu, menu_path, modes);
! 1026: }
! 1027: else
! 1028: {
! 1029: /* Replace special key codes */
! 1030: map_to = replace_termcodes(map_to, &map_buf, FALSE);
! 1031: gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
! 1032: vim_free(map_buf);
! 1033: }
! 1034: }
! 1035:
! 1036: /*
! 1037: * Add the menu with the given name to the menu hierarchy
! 1038: */
! 1039: static int
! 1040: gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
! 1041: char_u *path_name;
! 1042: int modes;
! 1043: void (*call_back)();
! 1044: char_u *call_data;
! 1045: int noremap;
! 1046: {
! 1047: GuiMenu **menup;
! 1048: GuiMenu *menu = NULL;
! 1049: GuiMenu *parent;
! 1050: char_u *p;
! 1051: char_u *name;
! 1052: int i;
! 1053:
! 1054: /* Make a copy so we can stuff around with it, since it could be const */
! 1055: path_name = strsave(path_name);
! 1056: if (path_name == NULL)
! 1057: return FAIL;
! 1058: menup = &gui.root_menu;
! 1059: parent = NULL;
! 1060: name = path_name;
! 1061: while (*name)
! 1062: {
! 1063: /* Get name of this element in the menu hierarchy */
! 1064: p = gui_menu_name_skip(name);
! 1065:
! 1066: /* See if it's already there */
! 1067: menu = *menup;
! 1068: while (menu != NULL)
! 1069: {
! 1070: if (STRCMP(name, menu->name) == 0)
! 1071: {
! 1072: if (*p == NUL && menu->children != NULL)
! 1073: {
! 1074: EMSG("Menu path must not lead to a sub-menu");
! 1075: vim_free(path_name);
! 1076: return FAIL;
! 1077: }
! 1078: else if (*p != NUL && menu->children == NULL)
! 1079: {
! 1080: EMSG("Part of menu-item path is not sub-menu");
! 1081: vim_free(path_name);
! 1082: return FAIL;
! 1083: }
! 1084: break;
! 1085: }
! 1086: menup = &menu->next;
! 1087: menu = menu->next;
! 1088: }
! 1089: if (menu == NULL)
! 1090: {
! 1091: if (*p == NUL && parent == NULL)
! 1092: {
! 1093: EMSG("Must not add menu items directly to menu bar");
! 1094: vim_free(path_name);
! 1095: return FAIL;
! 1096: }
! 1097:
! 1098: /* Not already there, so lets add it */
! 1099: menu = (GuiMenu *)alloc(sizeof(GuiMenu));
! 1100: if (menu == NULL)
! 1101: {
! 1102: vim_free(path_name);
! 1103: return FAIL;
! 1104: }
! 1105: menu->modes = modes;
! 1106: menu->name = strsave(name);
! 1107: menu->cb = NULL;
! 1108: for (i = 0; i < 4; i++)
! 1109: {
! 1110: menu->strings[i] = NULL;
! 1111: menu->noremap[i] = FALSE;
! 1112: }
! 1113: menu->children = NULL;
! 1114: menu->next = NULL;
! 1115: if (gui.in_use) /* Otherwise it will be added when GUI starts */
! 1116: {
! 1117: if (*p == NUL)
! 1118: {
! 1119: /* Real menu item, not sub-menu */
! 1120: gui_mch_add_menu_item(menu, parent);
! 1121:
! 1122: /* Want to update menus now even if mode not changed */
! 1123: force_menu_update = TRUE;
! 1124: }
! 1125: else
! 1126: {
! 1127: /* Sub-menu (not at end of path yet) */
! 1128: gui_mch_add_menu(menu, parent);
! 1129: }
! 1130: }
! 1131: *menup = menu;
! 1132: }
! 1133: else
! 1134: {
! 1135: /*
! 1136: * If this menu option was previously only available in other
! 1137: * modes, then make sure it's available for this one now
! 1138: */
! 1139: menu->modes |= modes;
! 1140: }
! 1141:
! 1142: menup = &menu->children;
! 1143: parent = menu;
! 1144: name = p;
! 1145: }
! 1146: vim_free(path_name);
! 1147:
! 1148: if (menu != NULL)
! 1149: {
! 1150: menu->cb = call_back;
! 1151: p = (call_data == NULL) ? NULL : strsave(call_data);
! 1152:
! 1153: /* May match more than one of these */
! 1154: if (modes & MENU_NORMAL_MODE)
! 1155: {
! 1156: gui_free_menu_string(menu, MENU_INDEX_NORMAL);
! 1157: menu->strings[MENU_INDEX_NORMAL] = p;
! 1158: menu->noremap[MENU_INDEX_NORMAL] = noremap;
! 1159: }
! 1160: if (modes & MENU_VISUAL_MODE)
! 1161: {
! 1162: gui_free_menu_string(menu, MENU_INDEX_VISUAL);
! 1163: menu->strings[MENU_INDEX_VISUAL] = p;
! 1164: menu->noremap[MENU_INDEX_VISUAL] = noremap;
! 1165: }
! 1166: if (modes & MENU_INSERT_MODE)
! 1167: {
! 1168: gui_free_menu_string(menu, MENU_INDEX_INSERT);
! 1169: menu->strings[MENU_INDEX_INSERT] = p;
! 1170: menu->noremap[MENU_INDEX_INSERT] = noremap;
! 1171: }
! 1172: if (modes & MENU_CMDLINE_MODE)
! 1173: {
! 1174: gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
! 1175: menu->strings[MENU_INDEX_CMDLINE] = p;
! 1176: menu->noremap[MENU_INDEX_CMDLINE] = noremap;
! 1177: }
! 1178: }
! 1179: return OK;
! 1180: }
! 1181:
! 1182: /*
! 1183: * Remove the (sub)menu with the given name from the menu hierarchy
! 1184: * Called recursively.
! 1185: */
! 1186: static int
! 1187: gui_remove_menu(menup, name, modes)
! 1188: GuiMenu **menup;
! 1189: char_u *name;
! 1190: int modes;
! 1191: {
! 1192: GuiMenu *menu;
! 1193: GuiMenu *child;
! 1194: char_u *p;
! 1195:
! 1196: if (*menup == NULL)
! 1197: return OK; /* Got to bottom of hierarchy */
! 1198:
! 1199: /* Get name of this element in the menu hierarchy */
! 1200: p = gui_menu_name_skip(name);
! 1201:
! 1202: /* Find the menu */
! 1203: menu = *menup;
! 1204: while (menu != NULL)
! 1205: {
! 1206: if (*name == NUL || STRCMP(name, menu->name) == 0)
! 1207: {
! 1208: if (*p != NUL && menu->children == NULL)
! 1209: {
! 1210: EMSG("Part of menu-item path is not sub-menu");
! 1211: return FAIL;
! 1212: }
! 1213: if ((menu->modes & modes) != 0x0)
! 1214: {
! 1215: if (gui_remove_menu(&menu->children, p, modes) == FAIL)
! 1216: return FAIL;
! 1217: }
! 1218: else if (*name != NUL)
! 1219: {
! 1220: EMSG("Menu only exists in another mode");
! 1221: return FAIL;
! 1222: }
! 1223:
! 1224: /*
! 1225: * When name is empty, we are removing all menu items for the given
! 1226: * modes, so keep looping, otherwise we are just removing the named
! 1227: * menu item (which has been found) so break here.
! 1228: */
! 1229: if (*name != NUL)
! 1230: break;
! 1231:
! 1232: /* Remove the menu item for the given mode[s] */
! 1233: menu->modes &= ~modes;
! 1234:
! 1235: if (menu->modes == 0x0)
! 1236: {
! 1237: /* The menu item is no longer valid in ANY mode, so delete it */
! 1238: *menup = menu->next;
! 1239: gui_free_menu(menu);
! 1240: }
! 1241: else
! 1242: menup = &menu->next;
! 1243: }
! 1244: else
! 1245: menup = &menu->next;
! 1246: menu = *menup;
! 1247: }
! 1248: if (*name != NUL)
! 1249: {
! 1250: if (menu == NULL)
! 1251: {
! 1252: EMSG("No menu of that name");
! 1253: return FAIL;
! 1254: }
! 1255:
! 1256: /* Recalculate modes for menu based on the new updated children */
! 1257: menu->modes = 0x0;
! 1258: for (child = menu->children; child != NULL; child = child->next)
! 1259: menu->modes |= child->modes;
! 1260: if (menu->modes == 0x0)
! 1261: {
! 1262: /* The menu item is no longer valid in ANY mode, so delete it */
! 1263: *menup = menu->next;
! 1264: gui_free_menu(menu);
! 1265: }
! 1266: }
! 1267:
! 1268: return OK;
! 1269: }
! 1270:
! 1271: /*
! 1272: * Free the given menu structure
! 1273: */
! 1274: static void
! 1275: gui_free_menu(menu)
! 1276: GuiMenu *menu;
! 1277: {
! 1278: int i;
! 1279:
! 1280: gui_mch_destroy_menu(menu); /* Free machine specific menu structures */
! 1281: vim_free(menu->name);
! 1282: for (i = 0; i < 4; i++)
! 1283: gui_free_menu_string(menu, i);
! 1284: vim_free(menu);
! 1285:
! 1286: /* Want to update menus now even if mode not changed */
! 1287: force_menu_update = TRUE;
! 1288: }
! 1289:
! 1290: /*
! 1291: * Free the menu->string with the given index.
! 1292: */
! 1293: static void
! 1294: gui_free_menu_string(menu, idx)
! 1295: GuiMenu *menu;
! 1296: int idx;
! 1297: {
! 1298: int count = 0;
! 1299: int i;
! 1300:
! 1301: for (i = 0; i < 4; i++)
! 1302: if (menu->strings[i] == menu->strings[idx])
! 1303: count++;
! 1304: if (count == 1)
! 1305: vim_free(menu->strings[idx]);
! 1306: menu->strings[idx] = NULL;
! 1307: }
! 1308:
! 1309: /*
! 1310: * Show the mapping associated with a menu item or hierarchy in a sub-menu.
! 1311: */
! 1312: static int
! 1313: gui_show_menus(path_name, modes)
! 1314: char_u *path_name;
! 1315: int modes;
! 1316: {
! 1317: char_u *p;
! 1318: char_u *name;
! 1319: GuiMenu *menu;
! 1320: GuiMenu *parent = NULL;
! 1321:
! 1322: menu = gui.root_menu;
! 1323: name = path_name = strsave(path_name);
! 1324: if (path_name == NULL)
! 1325: return FAIL;
! 1326:
! 1327: /* First, find the (sub)menu with the given name */
! 1328: while (*name)
! 1329: {
! 1330: p = gui_menu_name_skip(name);
! 1331: while (menu != NULL)
! 1332: {
! 1333: if (STRCMP(name, menu->name) == 0)
! 1334: {
! 1335: /* Found menu */
! 1336: if (*p != NUL && menu->children == NULL)
! 1337: {
! 1338: EMSG("Part of menu-item path is not sub-menu");
! 1339: vim_free(path_name);
! 1340: return FAIL;
! 1341: }
! 1342: else if ((menu->modes & modes) == 0x0)
! 1343: {
! 1344: EMSG("Menu only exists in another mode");
! 1345: vim_free(path_name);
! 1346: return FAIL;
! 1347: }
! 1348: break;
! 1349: }
! 1350: menu = menu->next;
! 1351: }
! 1352: if (menu == NULL)
! 1353: {
! 1354: EMSG("No menu of that name");
! 1355: vim_free(path_name);
! 1356: return FAIL;
! 1357: }
! 1358: name = p;
! 1359: parent = menu;
! 1360: menu = menu->children;
! 1361: }
! 1362:
! 1363: /* Now we have found the matching menu, and we list the mappings */
! 1364: set_highlight('t'); /* Highlight title */
! 1365: start_highlight();
! 1366: MSG_OUTSTR("\n--- Menus ---");
! 1367: stop_highlight();
! 1368:
! 1369: gui_show_menus_recursive(parent, modes, 0);
! 1370: return OK;
! 1371: }
! 1372:
! 1373: /*
! 1374: * Recursively show the mappings associated with the menus under the given one
! 1375: */
! 1376: static void
! 1377: gui_show_menus_recursive(menu, modes, depth)
! 1378: GuiMenu *menu;
! 1379: int modes;
! 1380: int depth;
! 1381: {
! 1382: int i;
! 1383: int bit;
! 1384:
! 1385: if (menu != NULL && (menu->modes & modes) == 0x0)
! 1386: return;
! 1387:
! 1388: if (menu != NULL)
! 1389: {
! 1390: msg_outchar('\n');
! 1391: if (got_int) /* "q" hit for "--more--" */
! 1392: return;
! 1393: for (i = 0; i < depth; i++)
! 1394: MSG_OUTSTR(" ");
! 1395: set_highlight('d'); /* Same as for directories!? */
! 1396: start_highlight();
! 1397: msg_outstr(menu->name);
! 1398: stop_highlight();
! 1399: }
! 1400:
! 1401: if (menu != NULL && menu->children == NULL)
! 1402: {
! 1403: for (bit = 0; bit < 4; bit++)
! 1404: if ((menu->modes & modes & (1 << bit)) != 0)
! 1405: {
! 1406: msg_outchar('\n');
! 1407: if (got_int) /* "q" hit for "--more--" */
! 1408: return;
! 1409: for (i = 0; i < depth + 2; i++)
! 1410: MSG_OUTSTR(" ");
! 1411: msg_outchar("nvic"[bit]);
! 1412: if (menu->noremap[bit])
! 1413: msg_outchar('*');
! 1414: else
! 1415: msg_outchar(' ');
! 1416: MSG_OUTSTR(" ");
! 1417: msg_outtrans_special(menu->strings[bit], TRUE);
! 1418: }
! 1419: }
! 1420: else
! 1421: {
! 1422: if (menu == NULL)
! 1423: {
! 1424: menu = gui.root_menu;
! 1425: depth--;
! 1426: }
! 1427: else
! 1428: menu = menu->children;
! 1429: for (; menu != NULL; menu = menu->next)
! 1430: gui_show_menus_recursive(menu, modes, depth + 1);
! 1431: }
! 1432: }
! 1433:
! 1434: /*
! 1435: * Used when expanding menu names.
! 1436: */
! 1437: static GuiMenu *expand_menu = NULL;
! 1438: static int expand_modes = 0x0;
! 1439:
! 1440: /*
! 1441: * Work out what to complete when doing command line completion of menu names.
! 1442: */
! 1443: char_u *
! 1444: gui_set_context_in_menu_cmd(cmd, arg, force)
! 1445: char_u *cmd;
! 1446: char_u *arg;
! 1447: int force;
! 1448: {
! 1449: char_u *after_dot;
! 1450: char_u *p;
! 1451: char_u *path_name = NULL;
! 1452: char_u *name;
! 1453: int unmenu;
! 1454: GuiMenu *menu;
! 1455:
! 1456: expand_context = EXPAND_UNSUCCESSFUL;
! 1457:
! 1458: after_dot = arg;
! 1459: for (p = arg; *p && !vim_iswhite(*p); ++p)
! 1460: {
! 1461: if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
! 1462: p++;
! 1463: else if (*p == '.')
! 1464: after_dot = p + 1;
! 1465: }
! 1466: if (*p == NUL) /* Complete the menu name */
! 1467: {
! 1468: /*
! 1469: * With :unmenu, you only want to match menus for the appropriate mode.
! 1470: * With :menu though you might want to add a menu with the same name as
! 1471: * one in another mode, so match menus fom other modes too.
! 1472: */
! 1473: expand_modes = gui_get_menu_cmd_modes(cmd, force, NULL, &unmenu);
! 1474: if (!unmenu)
! 1475: expand_modes = MENU_ALL_MODES;
! 1476:
! 1477: menu = gui.root_menu;
! 1478: if (after_dot != arg)
! 1479: {
! 1480: path_name = alloc(after_dot - arg);
! 1481: if (path_name == NULL)
! 1482: return NULL;
! 1483: STRNCPY(path_name, arg, after_dot - arg - 1);
! 1484: path_name[after_dot - arg - 1] = NUL;
! 1485: }
! 1486: name = path_name;
! 1487: while (name != NULL && *name)
! 1488: {
! 1489: p = gui_menu_name_skip(name);
! 1490: while (menu != NULL)
! 1491: {
! 1492: if (STRCMP(name, menu->name) == 0)
! 1493: {
! 1494: /* Found menu */
! 1495: if ((*p != NUL && menu->children == NULL)
! 1496: || ((menu->modes & expand_modes) == 0x0))
! 1497: {
! 1498: /*
! 1499: * Menu path continues, but we have reached a leaf.
! 1500: * Or menu exists only in another mode.
! 1501: */
! 1502: vim_free(path_name);
! 1503: return NULL;
! 1504: }
! 1505: break;
! 1506: }
! 1507: menu = menu->next;
! 1508: }
! 1509: if (menu == NULL)
! 1510: {
! 1511: /* No menu found with the name we were looking for */
! 1512: vim_free(path_name);
! 1513: return NULL;
! 1514: }
! 1515: name = p;
! 1516: menu = menu->children;
! 1517: }
! 1518:
! 1519: expand_context = EXPAND_MENUS;
! 1520: expand_pattern = after_dot;
! 1521: expand_menu = menu;
! 1522: }
! 1523: else /* We're in the mapping part */
! 1524: expand_context = EXPAND_NOTHING;
! 1525: return NULL;
! 1526: }
! 1527:
! 1528: /*
! 1529: * Expand the menu names.
! 1530: */
! 1531: int
! 1532: gui_ExpandMenuNames(prog, num_file, file)
! 1533: regexp *prog;
! 1534: int *num_file;
! 1535: char_u ***file;
! 1536: {
! 1537: GuiMenu *menu;
! 1538: int round;
! 1539: int count;
! 1540:
! 1541: /*
! 1542: * round == 1: Count the matches.
! 1543: * round == 2: Save the matches into the array.
! 1544: */
! 1545: for (round = 1; round <= 2; ++round)
! 1546: {
! 1547: count = 0;
! 1548: for (menu = expand_menu; menu != NULL; menu = menu->next)
! 1549: if ((menu->modes & expand_modes) != 0x0
! 1550: && vim_regexec(prog, menu->name, TRUE))
! 1551: {
! 1552: if (round == 1)
! 1553: count++;
! 1554: else
! 1555: (*file)[count++] = strsave_escaped(menu->name,
! 1556: (char_u *)" \t\\.");
! 1557: }
! 1558: if (round == 1)
! 1559: {
! 1560: *num_file = count;
! 1561: if (count == 0 || (*file = (char_u **)
! 1562: alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
! 1563: return FAIL;
! 1564: }
! 1565: }
! 1566: return OK;
! 1567: }
! 1568:
! 1569: /*
! 1570: * Skip over this element of the menu path and return the start of the next
! 1571: * element. Any \ and ^Vs are removed from the current element.
! 1572: */
! 1573: static char_u *
! 1574: gui_menu_name_skip(name)
! 1575: char_u *name;
! 1576: {
! 1577: char_u *p;
! 1578:
! 1579: for (p = name; *p && *p != '.'; p++)
! 1580: if (*p == '\\' || *p == Ctrl('V'))
! 1581: {
! 1582: STRCPY(p, p + 1);
! 1583: if (*p == NUL)
! 1584: break;
! 1585: }
! 1586: if (*p)
! 1587: *p++ = NUL;
! 1588: return p;
! 1589: }
! 1590:
! 1591: /*
! 1592: * After we have started the GUI, then we can create any menus that have been
! 1593: * defined. This is done once here. gui_add_menu_path() may have already been
! 1594: * called to define these menus, and may be called again. This function calls
! 1595: * itself recursively. Should be called at the top level with:
! 1596: * gui_create_initial_menus(gui.root_menu, NULL);
! 1597: */
! 1598: static void
! 1599: gui_create_initial_menus(menu, parent)
! 1600: GuiMenu *menu;
! 1601: GuiMenu *parent;
! 1602: {
! 1603: while (menu)
! 1604: {
! 1605: if (menu->children != NULL)
! 1606: {
! 1607: gui_mch_add_menu(menu, parent);
! 1608: gui_create_initial_menus(menu->children, menu);
! 1609: }
! 1610: else
! 1611: gui_mch_add_menu_item(menu, parent);
! 1612: menu = menu->next;
! 1613: }
! 1614: }
! 1615:
! 1616:
! 1617: /*
! 1618: * Set which components are present.
! 1619: * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
! 1620: * in p_guioptions.
! 1621: */
! 1622: void
! 1623: gui_init_which_components(oldval)
! 1624: char_u *oldval;
! 1625: {
! 1626: char_u *p;
! 1627: int i;
! 1628: int grey_old, grey_new;
! 1629: char_u *temp;
! 1630:
! 1631: if (oldval != NULL)
! 1632: {
! 1633: /*
! 1634: * Check if the menu's go from grey to non-grey or vise versa.
! 1635: */
! 1636: grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
! 1637: grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
! 1638: if (grey_old != grey_new)
! 1639: {
! 1640: temp = p_guioptions;
! 1641: p_guioptions = oldval;
! 1642: gui_x11_update_menus(MENU_ALL_MODES);
! 1643: p_guioptions = temp;
! 1644: }
! 1645: }
! 1646:
! 1647: gui.menu_is_active = FALSE;
! 1648: for (i = 0; i < 3; i++)
! 1649: gui.which_scrollbars[i] = FALSE;
! 1650: for (p = p_guioptions; *p; p++)
! 1651: switch (*p)
! 1652: {
! 1653: case GO_LEFT:
! 1654: gui.which_scrollbars[SB_LEFT] = TRUE;
! 1655: break;
! 1656: case GO_RIGHT:
! 1657: gui.which_scrollbars[SB_RIGHT] = TRUE;
! 1658: break;
! 1659: case GO_BOT:
! 1660: gui.which_scrollbars[SB_BOTTOM] = TRUE;
! 1661: break;
! 1662: case GO_MENUS:
! 1663: gui.menu_is_active = TRUE;
! 1664: break;
! 1665: case GO_GREY:
! 1666: /* make menu's have grey items, ignored here */
! 1667: break;
! 1668: default:
! 1669: /* Should give error message for internal error */
! 1670: break;
! 1671: }
! 1672: if (gui.in_use)
! 1673: gui_mch_create_which_components();
! 1674: }
! 1675:
! 1676:
! 1677: /*
! 1678: * Vertical scrollbar stuff:
! 1679: */
! 1680:
! 1681: static void
! 1682: gui_update_scrollbars()
! 1683: {
! 1684: WIN *wp;
! 1685: int worst_update = SB_UPDATE_NOTHING;
! 1686: int val, size, max;
! 1687: int which_sb;
! 1688: int cmdline_height;
! 1689:
! 1690: /*
! 1691: * Don't want to update a scrollbar while we're dragging it. But if we
! 1692: * have both a left and right scrollbar, and we drag one of them, we still
! 1693: * need to update the other one.
! 1694: */
! 1695: if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
! 1696: (!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
! 1697: return;
! 1698:
! 1699: if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
! 1700: {
! 1701: /*
! 1702: * If we have two scrollbars and one of them is being dragged, just
! 1703: * copy the scrollbar position from the dragged one to the other one.
! 1704: */
! 1705: which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
! 1706: if (gui.dragged_wp != NULL)
! 1707: gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
! 1708: else
! 1709: gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;
! 1710:
! 1711: gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
! 1712: return;
! 1713: }
! 1714:
! 1715: /* Return straight away if there is neither a left nor right scrollbar */
! 1716: if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
! 1717: return;
! 1718:
! 1719: cmdline_height = Rows;
! 1720: for (wp = firstwin; wp; wp = wp->w_next)
! 1721: {
! 1722: cmdline_height -= wp->w_height + wp->w_status_height;
! 1723: if (wp->w_buffer == NULL) /* just in case */
! 1724: continue;
! 1725: #ifdef SCROLL_PAST_END
! 1726: max = wp->w_buffer->b_ml.ml_line_count;
! 1727: #else
! 1728: max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
! 1729: #endif
! 1730: if (max < 1) /* empty buffer */
! 1731: max = 1;
! 1732: val = wp->w_topline;
! 1733: size = wp->w_height;
! 1734: #ifdef SCROLL_PAST_END
! 1735: if (val > max) /* just in case */
! 1736: val = max;
! 1737: #else
! 1738: if (size > max) /* just in case */
! 1739: size = max;
! 1740: if (val > max - size + 1)
! 1741: {
! 1742: val = max - size + 1;
! 1743: if (val < 1) /* minimal value is 1 */
! 1744: val = 1;
! 1745: }
! 1746: #endif
! 1747: if (size < 1 || wp->w_botline - 1 > max)
! 1748: {
! 1749: /*
! 1750: * This can happen during changing files. Just don't update the
! 1751: * scrollbar for now.
! 1752: */
! 1753: }
! 1754: else if (wp->w_scrollbar.height == 0)
! 1755: {
! 1756: /* Must be a new window */
! 1757: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
! 1758: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
! 1759: wp->w_scrollbar.value = val;
! 1760: wp->w_scrollbar.size = size;
! 1761: wp->w_scrollbar.max = max;
! 1762: wp->w_scrollbar.top = wp->w_winpos;
! 1763: wp->w_scrollbar.height = wp->w_height;
! 1764: wp->w_scrollbar.status_height = wp->w_status_height;
! 1765: gui.num_scrollbars++;
! 1766: worst_update = SB_UPDATE_CREATE;
! 1767: }
! 1768: else if (wp->w_scrollbar.top != wp->w_winpos
! 1769: || wp->w_scrollbar.height != wp->w_height
! 1770: || wp->w_scrollbar.status_height != wp->w_status_height)
! 1771: {
! 1772: /* Height of scrollbar has changed */
! 1773: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
! 1774: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
! 1775: wp->w_scrollbar.value = val;
! 1776: wp->w_scrollbar.size = size;
! 1777: wp->w_scrollbar.max = max;
! 1778: wp->w_scrollbar.top = wp->w_winpos;
! 1779: wp->w_scrollbar.height = wp->w_height;
! 1780: wp->w_scrollbar.status_height = wp->w_status_height;
! 1781: if (worst_update < SB_UPDATE_HEIGHT)
! 1782: worst_update = SB_UPDATE_HEIGHT;
! 1783: }
! 1784: else if (wp->w_scrollbar.value != val
! 1785: || wp->w_scrollbar.size != size
! 1786: || wp->w_scrollbar.max != max)
! 1787: {
! 1788: /* Thumb of scrollbar has moved */
! 1789: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
! 1790: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
! 1791: wp->w_scrollbar.value = val;
! 1792: wp->w_scrollbar.size = size;
! 1793: wp->w_scrollbar.max = max;
! 1794: if (worst_update < SB_UPDATE_VALUE)
! 1795: worst_update = SB_UPDATE_VALUE;
! 1796: }
! 1797:
! 1798: /*
! 1799: * We may have just created the left scrollbar say, when we already had
! 1800: * the right one.
! 1801: */
! 1802: if (gui.new_sb[SB_LEFT])
! 1803: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
! 1804: if (gui.new_sb[SB_RIGHT])
! 1805: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
! 1806: }
! 1807: if (cmdline_height < 1)
! 1808: cmdline_height = 1; /* Shouldn't happen, but just in case */
! 1809:
! 1810: /* Check the command line scrollbar */
! 1811: if (gui.cmdline_sb.height != cmdline_height
! 1812: || gui.cmdline_sb.status_height != lastwin->w_status_height)
! 1813: {
! 1814: /* Height of scrollbar has changed */
! 1815: gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
! 1816: gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
! 1817: gui.cmdline_sb.value = 0;
! 1818: gui.cmdline_sb.size = 1; /* No thumb */
! 1819: gui.cmdline_sb.max = 0;
! 1820: gui.cmdline_sb.top = Rows - cmdline_height;
! 1821: gui.cmdline_sb.height = cmdline_height;
! 1822: gui.cmdline_sb.status_height = lastwin->w_status_height;
! 1823: if (worst_update < SB_UPDATE_HEIGHT)
! 1824: worst_update = SB_UPDATE_HEIGHT;
! 1825: }
! 1826:
! 1827: /*
! 1828: * If we have just created the left or right scrollbar-box, then we need to
! 1829: * update the height of the command line scrollbar (it will already be
! 1830: * created).
! 1831: */
! 1832: if (gui.new_sb[SB_LEFT])
! 1833: {
! 1834: gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
! 1835: worst_update = SB_UPDATE_CREATE;
! 1836: gui.new_sb[SB_LEFT] = FALSE;
! 1837: }
! 1838: if (gui.new_sb[SB_RIGHT])
! 1839: {
! 1840: gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
! 1841: worst_update = SB_UPDATE_CREATE;
! 1842: gui.new_sb[SB_RIGHT] = FALSE;
! 1843: }
! 1844:
! 1845: if (worst_update != SB_UPDATE_NOTHING)
! 1846: {
! 1847: if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
! 1848: gui_mch_update_scrollbars(worst_update, SB_LEFT);
! 1849: if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
! 1850: gui_mch_update_scrollbars(worst_update, SB_RIGHT);
! 1851: }
! 1852: }
! 1853:
! 1854: /*
! 1855: * Scroll a window according to the values set in the globals current_scrollbar
! 1856: * and scrollbar_value. Return TRUE if the cursor in the current window moved
! 1857: * or FALSE otherwise.
! 1858: */
! 1859: int
! 1860: gui_do_scroll()
! 1861: {
! 1862: WIN *wp, *old_wp;
! 1863: int i;
! 1864: FPOS old_cursor;
! 1865:
! 1866: for (wp = firstwin, i = 0; i < current_scrollbar; i++)
! 1867: {
! 1868: if (wp == NULL)
! 1869: break;
! 1870: wp = wp->w_next;
! 1871: }
! 1872: if (wp != NULL)
! 1873: {
! 1874: old_cursor = curwin->w_cursor;
! 1875: old_wp = curwin;
! 1876: curwin = wp;
! 1877: curbuf = wp->w_buffer;
! 1878: i = (long)scrollbar_value - (long)wp->w_topline;
! 1879: if (i < 0)
! 1880: scrolldown(-i);
! 1881: else if (i > 0)
! 1882: scrollup(i);
! 1883: if (p_so)
! 1884: cursor_correct();
! 1885: coladvance(curwin->w_curswant);
! 1886:
! 1887: curwin = old_wp;
! 1888: curbuf = old_wp->w_buffer;
! 1889:
! 1890: if (wp == curwin)
! 1891: cursupdate(); /* fix window for 'so' */
! 1892: wp->w_redr_type = VALID;
! 1893: updateWindow(wp); /* update window, status line, and cmdline */
! 1894:
! 1895: return !equal(curwin->w_cursor, old_cursor);
! 1896: }
! 1897: else
! 1898: {
! 1899: /* Command-line scrollbar, unimplemented */
! 1900: return FALSE;
! 1901: }
! 1902: }
! 1903:
! 1904:
! 1905: /*
! 1906: * Horizontal scrollbar stuff:
! 1907: */
! 1908:
! 1909: static void
! 1910: gui_update_horiz_scrollbar()
! 1911: {
! 1912: int value, size, max;
! 1913:
! 1914: if (!gui.which_scrollbars[SB_BOTTOM])
! 1915: return;
! 1916:
! 1917: if (gui.dragged_sb == SB_BOTTOM)
! 1918: return;
! 1919:
! 1920: if (curwin->w_p_wrap && gui.prev_wrap)
! 1921: return;
! 1922:
! 1923: /*
! 1924: * It is possible for the cursor to be invalid if we're in the middle of
! 1925: * something (like changing files). If so, don't do anything for now.
! 1926: */
! 1927: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
! 1928: return;
! 1929:
! 1930: if (curwin->w_p_wrap)
! 1931: {
! 1932: value = 0;
! 1933: size = Columns;
! 1934: #ifdef SCROLL_PAST_END
! 1935: max = 0;
! 1936: #else
! 1937: max = Columns - 1;
! 1938: #endif
! 1939: }
! 1940: else
! 1941: {
! 1942: value = curwin->w_leftcol;
! 1943: size = Columns;
! 1944: #ifdef SCROLL_PAST_END
! 1945: max = gui_get_max_horiz_scroll();
! 1946: #else
! 1947: max = gui_get_max_horiz_scroll() + Columns - 1;
! 1948: #endif
! 1949: }
! 1950: gui_mch_update_horiz_scrollbar(value, size, max + 1);
! 1951: gui.prev_wrap = curwin->w_p_wrap;
! 1952: }
! 1953:
! 1954: /*
! 1955: * Determine the maximum value for scrolling right.
! 1956: */
! 1957: int
! 1958: gui_get_max_horiz_scroll()
! 1959: {
! 1960: int max = 0;
! 1961: char_u *p;
! 1962:
! 1963: p = ml_get_curline();
! 1964: if (p[0] != NUL)
! 1965: while (p[1] != NUL) /* Don't count last character */
! 1966: max += chartabsize(*p++, (colnr_t)max);
! 1967: return max;
! 1968: }
! 1969:
! 1970: /*
! 1971: * Do a horizontal scroll. Return TRUE if the cursor moved, or FALSE otherwise
! 1972: */
! 1973: int
! 1974: gui_do_horiz_scroll()
! 1975: {
! 1976: char_u *p;
! 1977: int i;
! 1978: int vcol;
! 1979: int ret_val = FALSE;
! 1980:
! 1981: /* no wrapping, no scrolling */
! 1982: if (curwin->w_p_wrap)
! 1983: return FALSE;
! 1984:
! 1985: curwin->w_leftcol = scrollbar_value;
! 1986:
! 1987: i = 0;
! 1988: vcol = 0;
! 1989: p = ml_get_curline();
! 1990: while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
! 1991: vcol += chartabsize(p[i++], (colnr_t)vcol);
! 1992: if (vcol < curwin->w_leftcol)
! 1993: {
! 1994: /*
! 1995: * Cursor is on a character that is at least partly off the left hand
! 1996: * side of the screen.
! 1997: */
! 1998: while (p[i] && vcol < curwin->w_leftcol)
! 1999: vcol += chartabsize(p[i++], (colnr_t)vcol);
! 2000: curwin->w_cursor.col = i;
! 2001: curwin->w_set_curswant = TRUE;
! 2002: ret_val = TRUE;
! 2003: }
! 2004:
! 2005: while (p[i] && i <= curwin->w_cursor.col
! 2006: && vcol <= curwin->w_leftcol + Columns)
! 2007: vcol += chartabsize(p[i++], (colnr_t)vcol);
! 2008: if (vcol > curwin->w_leftcol + Columns)
! 2009: {
! 2010: /*
! 2011: * Cursor is on a character that is at least partly off the right hand
! 2012: * side of the screen.
! 2013: */
! 2014: if (i < 2)
! 2015: i = 0;
! 2016: else
! 2017: i -= 2;
! 2018: curwin->w_cursor.col = i;
! 2019: curwin->w_set_curswant = TRUE;
! 2020: ret_val = TRUE;
! 2021: }
! 2022: updateScreen(NOT_VALID);
! 2023: return ret_val;
! 2024: }