home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / amiga / vim46src.lha / vim-4.6 / src / gui.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-06  |  46.2 KB  |  2,069 lines

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