[BACK]Return to gui.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

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: }