From 1b9645de3c05f37b5c30e78f999351b0cf486ade Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Sep 2017 23:03:31 +0200 Subject: patch 8.0.1123: cannot define a toolbar for a window Problem: Cannot define a toolbar for a window. Solution: Add a window-local toolbar. --- src/Makefile | 1 + src/eval.c | 8 +- src/evalfunc.c | 3 + src/if_perl.xs | 13 +- src/menu.c | 290 +++++++++++++++++++++++++++-------- src/normal.c | 4 +- src/proto/menu.pro | 3 + src/proto/syntax.pro | 1 + src/screen.c | 362 ++++++++++++++++++++++++++++---------------- src/structs.h | 22 ++- src/syntax.c | 26 ++++ src/terminal.c | 7 +- src/testdir/Make_all.mak | 5 +- src/testdir/test_winbar.vim | 23 +++ src/ui.c | 20 ++- src/version.c | 2 + src/window.c | 5 + 17 files changed, 571 insertions(+), 224 deletions(-) create mode 100644 src/testdir/test_winbar.vim (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 247fee6100..ad96784a6d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2278,6 +2278,7 @@ test_arglist \ test_vimscript \ test_virtualedit \ test_visual \ + test_winbar \ test_window_cmd \ test_window_id \ test_windows_home \ diff --git a/src/eval.c b/src/eval.c index 0d954a0ee1..8b1ceaea80 100644 --- a/src/eval.c +++ b/src/eval.c @@ -8252,13 +8252,7 @@ ex_echo(exarg_T *eap) void ex_echohl(exarg_T *eap) { - int id; - - id = syn_name2id(eap->arg); - if (id == 0) - echo_attr = 0; - else - echo_attr = syn_id2attr(id); + echo_attr = syn_name2attr(eap->arg); } /* diff --git a/src/evalfunc.c b/src/evalfunc.c index 8d370d49a2..6c2a73e800 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5229,6 +5229,9 @@ get_win_info(win_T *wp, short tpnr, short winnr) dict_add_nr_str(dict, "winnr", winnr, NULL); dict_add_nr_str(dict, "winid", wp->w_id, NULL); dict_add_nr_str(dict, "height", wp->w_height, NULL); +#ifdef FEAT_MENU + dict_add_nr_str(dict, "winbar", wp->w_winbar_height, NULL); +#endif dict_add_nr_str(dict, "width", wp->w_width, NULL); dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL); diff --git a/src/if_perl.xs b/src/if_perl.xs index 8936838137..d67645cd57 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -1387,11 +1387,8 @@ PerlIOVim_pushed(pTHX_ PerlIO *f, const char *mode, { PerlIOVim *s = PerlIOSelf(f, PerlIOVim); s->attr = 0; - if (arg && SvPOK(arg)) { - int id = syn_name2id((char_u *)SvPV_nolen(arg)); - if (id != 0) - s->attr = syn_id2attr(id); - } + if (arg && SvPOK(arg)) + s->attr = syn_name2attr((char_u *)SvPV_nolen(arg)); return PerlIOBase_pushed(aTHX_ f, mode, (SV *)NULL, tab); } @@ -1482,11 +1479,7 @@ Msg(text, hl=NULL) { attr = 0; if (hl != NULL) - { - id = syn_name2id((char_u *)hl); - if (id != 0) - attr = syn_id2attr(id); - } + attr = syn_name2attr((char_u *)hl); msg_split((char_u *)text, attr); } diff --git a/src/menu.c b/src/menu.c index 343a1b8ec5..1ad8a5cb90 100644 --- a/src/menu.c +++ b/src/menu.c @@ -82,6 +82,31 @@ static const char *toolbar_names[] = # define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *)) #endif +/* + * Return TRUE if "name" is a window toolbar menu name. + */ + static int +menu_is_winbar(char_u *name) +{ + return (STRNCMP(name, "WinBar", 5) == 0); +} + + int +winbar_height(win_T *wp) +{ + if (wp->w_winbar != NULL && wp->w_winbar->children != NULL) + return 1; + return 0; +} + + static vimmenu_T ** +get_root_menu(char_u *name) +{ + if (menu_is_winbar(name)) + return &curwin->w_winbar; + return &root_menu; +} + /* * Do the :menu command and relatives. */ @@ -113,6 +138,7 @@ ex_menu( char_u *icon = NULL; #endif vimmenu_T menuarg; + vimmenu_T **root_menu_ptr; modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu); arg = eap->arg; @@ -279,6 +305,11 @@ ex_menu( # endif #endif + root_menu_ptr = get_root_menu(menu_path); + if (root_menu_ptr == &curwin->w_winbar) + /* Assume the window toolbar menu will change. */ + redraw_later(NOT_VALID); + if (enable != MAYBE) { /* @@ -297,13 +328,13 @@ ex_menu( p = popup_mode_name(menu_path, i); if (p != NULL) { - menu_nable_recurse(root_menu, p, MENU_ALL_MODES, + menu_nable_recurse(*root_menu_ptr, p, MENU_ALL_MODES, enable); vim_free(p); } } } - menu_nable_recurse(root_menu, menu_path, modes, enable); + menu_nable_recurse(*root_menu_ptr, menu_path, modes, enable); } else if (unmenu) { @@ -324,14 +355,14 @@ ex_menu( p = popup_mode_name(menu_path, i); if (p != NULL) { - remove_menu(&root_menu, p, MENU_ALL_MODES, TRUE); + remove_menu(root_menu_ptr, p, MENU_ALL_MODES, TRUE); vim_free(p); } } } /* Careful: remove_menu() changes menu_path */ - remove_menu(&root_menu, menu_path, modes, FALSE); + remove_menu(root_menu_ptr, menu_path, modes, FALSE); } else { @@ -401,6 +432,19 @@ ex_menu( )) gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); #endif + if (root_menu_ptr == &curwin->w_winbar) + { + int h = winbar_height(curwin); + + if (h != curwin->w_winbar_height) + { + if (h == 0) + ++curwin->w_height; + else if (curwin->w_height > 0) + --curwin->w_height; + curwin->w_winbar_height = h; + } + } theend: ; @@ -445,12 +489,14 @@ add_menu_path( char_u *en_name; char_u *map_to = NULL; #endif + vimmenu_T **root_menu_ptr; /* Make a copy so we can stuff around with it, since it could be const */ path_name = vim_strsave(menu_path); if (path_name == NULL) return FAIL; - menup = &root_menu; + root_menu_ptr = get_root_menu(menu_path); + menup = root_menu_ptr; parent = NULL; name = path_name; while (*name) @@ -786,7 +832,7 @@ erret: while (parent != NULL && parent->children == NULL) { if (parent->parent == NULL) - menup = &root_menu; + menup = root_menu_ptr; else menup = &parent->parent->children; for ( ; *menup != NULL && *menup != parent; menup = &((*menup)->next)) @@ -985,6 +1031,16 @@ remove_menu( return OK; } +/* + * Remove the WinBar menu from window "wp". + */ + void +remove_winbar(win_T *wp) +{ + remove_menu(&wp->w_winbar, (char_u *)"", MENU_ALL_MODES, TRUE); + vim_free(wp->w_winbar_items); +} + /* * Free the given menu structure and remove it from the linked list. */ @@ -1057,10 +1113,10 @@ show_menus(char_u *path_name, int modes) vimmenu_T *menu; vimmenu_T *parent = NULL; - menu = root_menu; name = path_name = vim_strsave(path_name); if (path_name == NULL) return FAIL; + menu = *get_root_menu(path_name); /* First, find the (sub)menu with the given name */ while (*name) @@ -1190,6 +1246,7 @@ show_menus_recursive(vimmenu_T *menu, int modes, int depth) * Used when expanding menu names. */ static vimmenu_T *expand_menu = NULL; +static vimmenu_T *expand_menu_alt = NULL; static int expand_modes = 0x0; static int expand_emenu; /* TRUE for ":emenu" command */ @@ -1251,6 +1308,8 @@ set_context_in_menu_cmd( return NULL; /* TODO: check for next command? */ if (*p == NUL) /* Complete the menu name */ { + int try_alt_menu = TRUE; + /* * With :unmenu, you only want to match menus for the appropriate mode. * With :menu though you might want to add a menu with the same name as @@ -1290,6 +1349,11 @@ set_context_in_menu_cmd( break; } menu = menu->next; + if (menu == NULL && try_alt_menu) + { + menu = curwin->w_winbar; + try_alt_menu = FALSE; + } } if (menu == NULL) { @@ -1299,12 +1363,17 @@ set_context_in_menu_cmd( } name = p; menu = menu->children; + try_alt_menu = FALSE; } vim_free(path_name); xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS; xp->xp_pattern = after_dot; expand_menu = menu; + if (expand_menu == root_menu) + expand_menu_alt = curwin->w_winbar; + else + expand_menu_alt = NULL; } else /* We're in the mapping part */ xp->xp_context = EXPAND_NOTHING; @@ -1319,6 +1388,7 @@ set_context_in_menu_cmd( get_menu_name(expand_T *xp UNUSED, int idx) { static vimmenu_T *menu = NULL; + static int did_alt_menu = FALSE; char_u *str; #ifdef FEAT_MULTI_LANG static int should_advance = FALSE; @@ -1327,6 +1397,7 @@ get_menu_name(expand_T *xp UNUSED, int idx) if (idx == 0) /* first call: start at first item */ { menu = expand_menu; + did_alt_menu = FALSE; #ifdef FEAT_MULTI_LANG should_advance = FALSE; #endif @@ -1337,7 +1408,14 @@ get_menu_name(expand_T *xp UNUSED, int idx) || menu_is_separator(menu->dname) || menu_is_tearoff(menu->dname) || menu->children == NULL)) + { menu = menu->next; + if (menu == NULL && !did_alt_menu) + { + menu = expand_menu_alt; + did_alt_menu = TRUE; + } + } if (menu == NULL) /* at end of linked list */ return NULL; @@ -1361,8 +1439,15 @@ get_menu_name(expand_T *xp UNUSED, int idx) #ifdef FEAT_MULTI_LANG if (should_advance) #endif + { /* Advance to next menu entry. */ menu = menu->next; + if (menu == NULL && !did_alt_menu) + { + menu = expand_menu_alt; + did_alt_menu = TRUE; + } + } #ifdef FEAT_MULTI_LANG should_advance = !should_advance; @@ -1379,6 +1464,7 @@ get_menu_name(expand_T *xp UNUSED, int idx) get_menu_names(expand_T *xp UNUSED, int idx) { static vimmenu_T *menu = NULL; + static int did_alt_menu = FALSE; #define TBUFFER_LEN 256 static char_u tbuffer[TBUFFER_LEN]; /*hack*/ char_u *str; @@ -1389,6 +1475,7 @@ get_menu_names(expand_T *xp UNUSED, int idx) if (idx == 0) /* first call: start at first item */ { menu = expand_menu; + did_alt_menu = FALSE; #ifdef FEAT_MULTI_LANG should_advance = FALSE; #endif @@ -1403,7 +1490,14 @@ get_menu_names(expand_T *xp UNUSED, int idx) || menu->dname[STRLEN(menu->dname) - 1] == '.' #endif )) + { menu = menu->next; + if (menu == NULL && !did_alt_menu) + { + menu = expand_menu_alt; + did_alt_menu = TRUE; + } + } if (menu == NULL) /* at end of linked list */ return NULL; @@ -1451,8 +1545,15 @@ get_menu_names(expand_T *xp UNUSED, int idx) #ifdef FEAT_MULTI_LANG if (should_advance) #endif + { /* Advance to next menu entry. */ menu = menu->next; + if (menu == NULL && !did_alt_menu) + { + menu = expand_menu_alt; + did_alt_menu = TRUE; + } + } #ifdef FEAT_MULTI_LANG should_advance = !should_advance; @@ -2134,62 +2235,16 @@ gui_destroy_tearoffs_recurse(vimmenu_T *menu) #endif /* FEAT_GUI_W32 && FEAT_TEAROFF */ /* - * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and - * execute it. + * Execute "menu". Use by ":emenu" and the window toolbar. + * "eap" is NULL for the window toolbar. */ - void -ex_emenu(exarg_T *eap) + static void +execute_menu(exarg_T *eap, vimmenu_T *menu) { - vimmenu_T *menu; - char_u *name; - char_u *saved_name; - char_u *p; - int idx; char_u *mode; + int idx; - saved_name = vim_strsave(eap->arg); - if (saved_name == NULL) - return; - - menu = root_menu; - name = saved_name; - while (*name) - { - /* Find in the menu hierarchy */ - p = menu_name_skip(name); - - while (menu != NULL) - { - if (menu_name_equal(name, menu)) - { - if (*p == NUL && menu->children != NULL) - { - EMSG(_("E333: Menu path must lead to a menu item")); - menu = NULL; - } - else if (*p != NUL && menu->children == NULL) - { - EMSG(_(e_notsubmenu)); - menu = NULL; - } - break; - } - menu = menu->next; - } - if (menu == NULL || *p == NUL) - break; - menu = menu->children; - name = p; - } - vim_free(saved_name); - if (menu == NULL) - { - EMSG2(_("E334: Menu not found: %s"), eap->arg); - return; - } - - /* Found the menu, so execute. - * Use the Insert mode entry when returning to Insert mode. */ + /* Use the Insert mode entry when returning to Insert mode. */ if (restart_edit #ifdef FEAT_EVAL && !current_SID @@ -2199,7 +2254,12 @@ ex_emenu(exarg_T *eap) mode = (char_u *)"Insert"; idx = MENU_INDEX_INSERT; } - else if (eap->addr_count) + else if (VIsual_active) + { + mode = (char_u *)"Visual"; + idx = MENU_INDEX_VISUAL; + } + else if (eap != NULL && eap->addr_count) { pos_T tpos; @@ -2255,22 +2315,120 @@ ex_emenu(exarg_T *eap) if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL) { /* When executing a script or function execute the commands right now. + * Also for the window toolbar. * Otherwise put them in the typeahead buffer. */ + if (eap == NULL #ifdef FEAT_EVAL - if (current_SID != 0) + || current_SID != 0 +#endif + ) exec_normal_cmd(menu->strings[idx], menu->noremap[idx], menu->silent[idx]); else -#endif ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, TRUE, menu->silent[idx]); } - else + else if (eap != NULL) EMSG2(_("E335: Menu not defined for %s mode"), mode); } -#if defined(FEAT_GUI_MSWIN) \ - || (defined(FEAT_GUI_GTK) && defined(FEAT_MENU)) \ +/* + * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and + * execute it. + */ + void +ex_emenu(exarg_T *eap) +{ + vimmenu_T *menu; + char_u *name; + char_u *saved_name; + char_u *p; + + saved_name = vim_strsave(eap->arg); + if (saved_name == NULL) + return; + + menu = *get_root_menu(saved_name); + name = saved_name; + while (*name) + { + /* Find in the menu hierarchy */ + p = menu_name_skip(name); + + while (menu != NULL) + { + if (menu_name_equal(name, menu)) + { + if (*p == NUL && menu->children != NULL) + { + EMSG(_("E333: Menu path must lead to a menu item")); + menu = NULL; + } + else if (*p != NUL && menu->children == NULL) + { + EMSG(_(e_notsubmenu)); + menu = NULL; + } + break; + } + menu = menu->next; + } + if (menu == NULL || *p == NUL) + break; + menu = menu->children; + name = p; + } + vim_free(saved_name); + if (menu == NULL) + { + EMSG2(_("E334: Menu not found: %s"), eap->arg); + return; + } + + /* Found the menu, so execute. */ + execute_menu(eap, menu); +} + +/* + * Handle a click in the window toolbar of "wp" at column "col". + */ + void +winbar_click(win_T *wp, int col) +{ + int idx; + + if (wp->w_winbar_items == NULL) + return; + for (idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; ++idx) + { + winbar_item_T *item = &wp->w_winbar_items[idx]; + + if (col >= item->wb_startcol && col <= item->wb_endcol) + { + win_T *save_curwin = NULL; + + if (wp != curwin) + { + /* Clicking in the window toolbar of a not-current window. + * Make that window the current one and go to Normal mode. */ + save_curwin = curwin; + curwin = wp; + curbuf = curwin->w_buffer; + check_cursor(); + } + + execute_menu(NULL, item->wb_menu); + + if (save_curwin != NULL) + { + curwin = save_curwin; + curbuf = curwin->w_buffer; + } + } + } +} + +#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \ || defined(FEAT_BEVAL_TIP) || defined(PROTO) /* * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy. @@ -2283,7 +2441,7 @@ gui_find_menu(char_u *path_name) char_u *saved_name; char_u *p; - menu = root_menu; + menu = *get_root_menu(path_name); saved_name = vim_strsave(path_name); if (saved_name == NULL) diff --git a/src/normal.c b/src/normal.c index a8b6ffa16d..6fffbcbc17 100644 --- a/src/normal.c +++ b/src/normal.c @@ -2679,9 +2679,9 @@ do_mouse( * selection or the current window (might have false * negative here) */ - if (mouse_row < W_WINROW(curwin) + if (mouse_row < curwin->w_winrow || mouse_row - > (W_WINROW(curwin) + curwin->w_height)) + > (curwin->w_winrow + curwin->w_height)) jump_flags = MOUSE_MAY_STOP_VIS; else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) jump_flags = MOUSE_MAY_STOP_VIS; diff --git a/src/proto/menu.pro b/src/proto/menu.pro index e370835845..bd3d26b92c 100644 --- a/src/proto/menu.pro +++ b/src/proto/menu.pro @@ -1,5 +1,7 @@ /* menu.c */ +int winbar_height(win_T *wp); void ex_menu(exarg_T *eap); +void remove_winbar(win_T *wp); char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forceit); char_u *get_menu_name(expand_T *xp, int idx); char_u *get_menu_names(expand_T *xp, int idx); @@ -17,6 +19,7 @@ int gui_is_menu_shortcut(int key); void gui_show_popupmenu(void); void gui_mch_toggle_tearoffs(int enable); void ex_emenu(exarg_T *eap); +void winbar_click(win_T *wp, int col); vimmenu_T *gui_find_menu(char_u *path_name); void ex_menutranslate(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/proto/syntax.pro b/src/proto/syntax.pro index 0f64ceaf8c..f6ecb8c491 100644 --- a/src/proto/syntax.pro +++ b/src/proto/syntax.pro @@ -46,6 +46,7 @@ char_u *highlight_has_attr(int id, int flag, int modec); char_u *highlight_color(int id, char_u *what, int modec); long_u highlight_gui_color_rgb(int id, int fg); int syn_name2id(char_u *name); +int syn_name2attr(char_u *name); int highlight_exists(char_u *name); char_u *syn_id2name(int id); int syn_namen2id(char_u *linep, int len); diff --git a/src/screen.c b/src/screen.c index d93ce50f3c..9f39edf04f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -107,6 +107,9 @@ static int screen_cur_row, screen_cur_col; /* last known cursor position */ static match_T search_hl; /* used for 'hlsearch' highlight matching */ #endif +#if defined(FEAT_MENU) || defined(FEAT_FOLDING) +static int text_to_screenline(win_T *wp, char_u *text, int col); +#endif #ifdef FEAT_FOLDING static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ static int compute_foldcolumn(win_T *wp, int col); @@ -160,6 +163,9 @@ static void recording_mode(int attr); static void draw_tabline(void); static int fillchar_status(int *attr, win_T *wp); static int fillchar_vsep(int *attr); +#ifdef FEAT_MENU +static void redraw_win_toolbar(win_T *wp); +#endif #ifdef FEAT_STL_OPT static void win_redr_custom(win_T *wp, int draw_ruler); #endif @@ -455,7 +461,7 @@ redraw_after_callback(int call_update_screen) * editing the command. */ redrawcmdline_ex(FALSE); } - else if (State & (NORMAL | INSERT)) + else if (State & (NORMAL | INSERT | TERMINAL)) { /* keep the command line if possible */ update_screen(VALID_NO_UPDATE); @@ -1804,6 +1810,15 @@ win_update(win_T *wp) win_foldinfo.fi_level = 0; #endif +#ifdef FEAT_MENU + /* + * Draw the window toolbar, if there is one. + * TODO: only when needed. + */ + if (winbar_height(wp) > 0) + redraw_win_toolbar(wp); +#endif + /* * Update all the window rows. */ @@ -2433,6 +2448,143 @@ advance_color_col(int vcol, int **color_cols) } #endif +#if defined(FEAT_MENU) || defined(FEAT_FOLDING) +/* + * Copy "text" to ScreenLines using "attr". + * Returns the next screen column. + */ + static int +text_to_screenline(win_T *wp, char_u *text, int col) +{ + int off = (int)(current_ScreenLine - ScreenLines); + +#ifdef FEAT_MBYTE + if (has_mbyte) + { + int cells; + int u8c, u8cc[MAX_MCO]; + int i; + int idx; + int c_len; + char_u *p; +# ifdef FEAT_ARABIC + int prev_c = 0; /* previous Arabic character */ + int prev_c1 = 0; /* first composing char for prev_c */ +# endif + +# ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) + idx = off; + else +# endif + idx = off + col; + + /* Store multibyte characters in ScreenLines[] et al. correctly. */ + for (p = text; *p != NUL; ) + { + cells = (*mb_ptr2cells)(p); + c_len = (*mb_ptr2len)(p); + if (col + cells > W_WIDTH(wp) +# ifdef FEAT_RIGHTLEFT + - (wp->w_p_rl ? col : 0) +# endif + ) + break; + ScreenLines[idx] = *p; + if (enc_utf8) + { + u8c = utfc_ptr2char(p, u8cc); + if (*p < 0x80 && u8cc[0] == 0) + { + ScreenLinesUC[idx] = 0; +#ifdef FEAT_ARABIC + prev_c = u8c; +#endif + } + else + { +#ifdef FEAT_ARABIC + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) + { + /* Do Arabic shaping. */ + int pc, pc1, nc; + int pcc[MAX_MCO]; + int firstbyte = *p; + + /* The idea of what is the previous and next + * character depends on 'rightleft'. */ + if (wp->w_p_rl) + { + pc = prev_c; + pc1 = prev_c1; + nc = utf_ptr2char(p + c_len); + prev_c1 = u8cc[0]; + } + else + { + pc = utfc_ptr2char(p + c_len, pcc); + nc = prev_c; + pc1 = pcc[0]; + } + prev_c = u8c; + + u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], + pc, pc1, nc); + ScreenLines[idx] = firstbyte; + } + else + prev_c = u8c; +#endif + /* Non-BMP character: display as ? or fullwidth ?. */ +#ifdef UNICODE16 + if (u8c >= 0x10000) + ScreenLinesUC[idx] = (cells == 2) ? 0xff1f : (int)'?'; + else +#endif + ScreenLinesUC[idx] = u8c; + for (i = 0; i < Screen_mco; ++i) + { + ScreenLinesC[i][idx] = u8cc[i]; + if (u8cc[i] == 0) + break; + } + } + if (cells > 1) + ScreenLines[idx + 1] = 0; + } + else if (enc_dbcs == DBCS_JPNU && *p == 0x8e) + /* double-byte single width character */ + ScreenLines2[idx] = p[1]; + else if (cells > 1) + /* double-width character */ + ScreenLines[idx + 1] = p[1]; + col += cells; + idx += cells; + p += c_len; + } + } + else +#endif + { + int len = (int)STRLEN(text); + + if (len > W_WIDTH(wp) - col) + len = W_WIDTH(wp) - col; + if (len > 0) + { +#ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) + STRNCPY(current_ScreenLine, text, len); + else +#endif + STRNCPY(current_ScreenLine + col, text, len); + col += len; + } + } + return col; +} +#endif + #ifdef FEAT_FOLDING /* * Compute the width of the foldcolumn. Based on 'foldcolumn' and how much @@ -2618,128 +2770,7 @@ fold_line( * Right-left text is put in columns 0 - number-col, normal text is put * in columns number-col - window-width. */ -#ifdef FEAT_MBYTE - if (has_mbyte) - { - int cells; - int u8c, u8cc[MAX_MCO]; - int i; - int idx; - int c_len; - char_u *p; -# ifdef FEAT_ARABIC - int prev_c = 0; /* previous Arabic character */ - int prev_c1 = 0; /* first composing char for prev_c */ -# endif - -# ifdef FEAT_RIGHTLEFT - if (wp->w_p_rl) - idx = off; - else -# endif - idx = off + col; - - /* Store multibyte characters in ScreenLines[] et al. correctly. */ - for (p = text; *p != NUL; ) - { - cells = (*mb_ptr2cells)(p); - c_len = (*mb_ptr2len)(p); - if (col + cells > W_WIDTH(wp) -# ifdef FEAT_RIGHTLEFT - - (wp->w_p_rl ? col : 0) -# endif - ) - break; - ScreenLines[idx] = *p; - if (enc_utf8) - { - u8c = utfc_ptr2char(p, u8cc); - if (*p < 0x80 && u8cc[0] == 0) - { - ScreenLinesUC[idx] = 0; -#ifdef FEAT_ARABIC - prev_c = u8c; -#endif - } - else - { -#ifdef FEAT_ARABIC - if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) - { - /* Do Arabic shaping. */ - int pc, pc1, nc; - int pcc[MAX_MCO]; - int firstbyte = *p; - - /* The idea of what is the previous and next - * character depends on 'rightleft'. */ - if (wp->w_p_rl) - { - pc = prev_c; - pc1 = prev_c1; - nc = utf_ptr2char(p + c_len); - prev_c1 = u8cc[0]; - } - else - { - pc = utfc_ptr2char(p + c_len, pcc); - nc = prev_c; - pc1 = pcc[0]; - } - prev_c = u8c; - - u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], - pc, pc1, nc); - ScreenLines[idx] = firstbyte; - } - else - prev_c = u8c; -#endif - /* Non-BMP character: display as ? or fullwidth ?. */ -#ifdef UNICODE16 - if (u8c >= 0x10000) - ScreenLinesUC[idx] = (cells == 2) ? 0xff1f : (int)'?'; - else -#endif - ScreenLinesUC[idx] = u8c; - for (i = 0; i < Screen_mco; ++i) - { - ScreenLinesC[i][idx] = u8cc[i]; - if (u8cc[i] == 0) - break; - } - } - if (cells > 1) - ScreenLines[idx + 1] = 0; - } - else if (enc_dbcs == DBCS_JPNU && *p == 0x8e) - /* double-byte single width character */ - ScreenLines2[idx] = p[1]; - else if (cells > 1) - /* double-width character */ - ScreenLines[idx + 1] = p[1]; - col += cells; - idx += cells; - p += c_len; - } - } - else -#endif - { - len = (int)STRLEN(text); - if (len > W_WIDTH(wp) - col) - len = W_WIDTH(wp) - col; - if (len > 0) - { -#ifdef FEAT_RIGHTLEFT - if (wp->w_p_rl) - STRNCPY(current_ScreenLine, text, len); - else -#endif - STRNCPY(current_ScreenLine + col, text, len); - col += len; - } - } + col = text_to_screenline(wp, text, col); /* Fill the rest of the line with the fold filler */ #ifdef FEAT_RIGHTLEFT @@ -8397,6 +8428,17 @@ redraw_block(int row, int end, win_T *wp) screen_draw_rectangle(row, col, end - row, width, FALSE); } + static void +space_to_screenline(int off, int attr) +{ + ScreenLines[off] = ' '; + ScreenAttrs[off] = attr; +# ifdef FEAT_MBYTE + if (enc_utf8) + ScreenLinesUC[off] = 0; +# endif +} + /* * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' * with character 'c1' in first column followed by 'c2' in the other columns. @@ -8502,12 +8544,7 @@ screen_fill( col = end_col - col; while (col--) /* clear chars in ScreenLines */ { - ScreenLines[off] = ' '; -#ifdef FEAT_MBYTE - if (enc_utf8) - ScreenLinesUC[off] = 0; -#endif - ScreenAttrs[off] = 0; + space_to_screenline(off, 0); ++off; } } @@ -10671,6 +10708,73 @@ messaging(void) return (!(p_lz && char_avail() && !KeyTyped)); } +#ifdef FEAT_MENU +/* + * Draw the window toolbar. + */ + static void +redraw_win_toolbar(win_T *wp) +{ + vimmenu_T *menu; + int item_idx = 0; + int item_count = 0; + int col = 0; + int next_col; + int off = (int)(current_ScreenLine - ScreenLines); + int fill_attr = syn_name2attr((char_u *)"ToolbarLine"); + int button_attr = syn_name2attr((char_u *)"ToolbarButton"); + + vim_free(wp->w_winbar_items); + for (menu = wp->w_winbar->children; menu != NULL; menu = menu->next) + ++item_count; + wp->w_winbar_items = (winbar_item_T *)alloc_clear( + (unsigned)sizeof(winbar_item_T) * (item_count + 1)); + + /* TODO: use fewer spaces if there is not enough room */ + for (menu = wp->w_winbar->children; + menu != NULL && col < W_WIDTH(wp); menu = menu->next) + { + space_to_screenline(off + col, fill_attr); + if (++col >= W_WIDTH(wp)) + break; + if (col > 1) + { + space_to_screenline(off + col, fill_attr); + if (++col >= W_WIDTH(wp)) + break; + } + + wp->w_winbar_items[item_idx].wb_startcol = col; + space_to_screenline(off + col, button_attr); + if (++col >= W_WIDTH(wp)) + break; + + next_col = text_to_screenline(wp, menu->name, col); + while (col < next_col) + { + ScreenAttrs[off + col] = button_attr; + ++col; + } + wp->w_winbar_items[item_idx].wb_endcol = col; + wp->w_winbar_items[item_idx].wb_menu = menu; + ++item_idx; + + if (col >= W_WIDTH(wp)) + break; + space_to_screenline(off + col, button_attr); + ++col; + } + while (col < W_WIDTH(wp)) + { + space_to_screenline(off + col, fill_attr); + ++col; + } + wp->w_winbar_items[item_idx].wb_menu = NULL; /* end marker */ + + screen_line(wp->w_winrow, W_WINCOL(wp), (int)W_WIDTH(wp), + (int)W_WIDTH(wp), FALSE); +} +#endif /* * Show current status info in ruler and various other places * If always is FALSE, only show ruler if position has changed. diff --git a/src/structs.h b/src/structs.h index 18a047c519..94d7391919 100644 --- a/src/structs.h +++ b/src/structs.h @@ -70,6 +70,10 @@ typedef int scid_T; /* script ID */ typedef struct file_buffer buf_T; /* forward declaration */ typedef struct terminal_S term_T; +#ifdef FEAT_MENU +typedef struct VimMenu vimmenu_T; +#endif + /* * Reference to a buffer that stores the value of buf_free_count. * bufref_valid() only needs to check "buf" when the count differs. @@ -2611,6 +2615,14 @@ struct matchitem #endif }; +#ifdef FEAT_MENU +typedef struct { + int wb_startcol; + int wb_endcol; + vimmenu_T *wb_menu; +} winbar_item_T; +#endif + /* * Structure which contains all information that belongs to a window * @@ -2686,7 +2698,7 @@ struct window_S */ int w_winrow; /* first row of window in screen */ int w_height; /* number of rows in window, excluding - status/command line(s) */ + status/command/winbar line(s) */ int w_status_height; /* number of status lines (0 or 1) */ int w_wincol; /* Leftmost column of window in screen. use W_WINCOL() */ @@ -2798,6 +2810,12 @@ struct window_S char_u *w_localdir; /* absolute path of local directory or NULL */ +#ifdef FEAT_MENU + vimmenu_T *w_winbar; /* The root of the WinBar menu hierarchy. */ + winbar_item_T *w_winbar_items; /* list of items in the WinBar */ + int w_winbar_height; /* 1 if there is a window toolbar */ +#endif + /* * Options local to a window. * They are local because they influence the layout of the window or @@ -3064,8 +3082,6 @@ typedef struct cursor_entry /* Start a menu name with this to not include it on the main menu bar */ #define MNU_HIDDEN_CHAR ']' -typedef struct VimMenu vimmenu_T; - struct VimMenu { int modes; /* Which modes is this menu visible for? */ diff --git a/src/syntax.c b/src/syntax.c index c6e01562e4..6361bd7aaf 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -7001,6 +7001,12 @@ static char *(highlight_init_light[]) = { "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"), CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen", "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"), +#endif +#ifdef FEAT_MENU + CENT("ToolbarLine term=underline ctermbg=LightGrey", + "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"), + CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey", + "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=DarkGrey"), #endif NULL }; @@ -7093,6 +7099,12 @@ static char *(highlight_init_dark[]) = { "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"), CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen", "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"), +#endif +#ifdef FEAT_MENU + CENT("ToolbarLine term=underline ctermbg=DarkGrey", + "ToolbarLine term=underline ctermbg=DarkGrey guibg=DarkGrey"), + CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey", + "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"), #endif NULL }; @@ -9525,6 +9537,20 @@ syn_name2id(char_u *name) return i + 1; } +/* + * Lookup a highlight group name and return its attributes. + * Return zero if not found. + */ + int +syn_name2attr(char_u *name) +{ + int id = syn_name2id(name); + + if (id != 0) + return syn_id2attr(syn_get_final_id(id)); + return 0; +} + #if defined(FEAT_EVAL) || defined(PROTO) /* * Return TRUE if highlight group "name" exists. diff --git a/src/terminal.c b/src/terminal.c index db1741703b..7dc21d6127 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -38,7 +38,8 @@ * in tl_scrollback are no longer used. * * TODO: - * - test_terminal_no_cmd hangs (Christian) + * - Shift-Tab does not work. + * - click in Window toolbar of other window: save/restore Insert and Visual * - Redirecting output does not work on MS-Windows, Test_terminal_redir_file() * is disabled. * - implement term_setsize() @@ -703,7 +704,7 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) update_screen(0); update_cursor(term, TRUE); } - else if (buffer->b_nwindows > 0) + else redraw_after_callback(TRUE); } } @@ -1545,7 +1546,7 @@ terminal_loop(int blocking) { /* TODO: skip screen update when handling a sequence of keys. */ /* Repeat redrawing in case a message is received while redrawing. */ - while (curwin->w_redr_type != 0) + while (must_redraw != 0) if (update_screen(0) == FAIL) break; update_cursor(curbuf->b_term, FALSE); diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index b979c975b7..169d84fd5d 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -201,12 +201,13 @@ NEW_TESTS = test_arabic.res \ test_viminfo.res \ test_vimscript.res \ test_visual.res \ + test_winbar.res \ test_window_id.res \ + test_windows_home.res \ test_writefile.res \ test_alot_latin.res \ test_alot_utf8.res \ - test_alot.res \ - test_windows_home.res + test_alot.res # Explicit dependencies. diff --git a/src/testdir/test_winbar.vim b/src/testdir/test_winbar.vim new file mode 100644 index 0000000000..19616073c0 --- /dev/null +++ b/src/testdir/test_winbar.vim @@ -0,0 +1,23 @@ +" Test WinBar + +if !has('menu') + finish +endif + +func Test_add_remove_menu() + new + amenu 1.10 WinBar.Next :let g:did_next = 11 + amenu 1.20 WinBar.Cont :let g:did_cont = 12 + emenu WinBar.Next + call assert_equal(11, g:did_next) + emenu WinBar.Cont + call assert_equal(12, g:did_cont) + + wincmd w + call assert_fails('emenu WinBar.Next', 'E334') + wincmd p + + aunmenu WinBar.Next + aunmenu WinBar.Cont + close +endfunc diff --git a/src/ui.c b/src/ui.c index 49782ae627..c26cb378ff 100644 --- a/src/ui.c +++ b/src/ui.c @@ -2657,7 +2657,7 @@ retnomove: } #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD) /* Continue a modeless selection in another window. */ - if (cmdwin_type != 0 && row < W_WINROW(curwin)) + if (cmdwin_type != 0 && row < curwin->w_winrow) return IN_OTHER_WIN; #endif return IN_BUFFER; @@ -2692,6 +2692,17 @@ retnomove: if (wp == NULL) return IN_UNKNOWN; dragwin = NULL; + +#ifdef FEAT_MENU + if (row == -1) + { + /* A click in the window toolbar does not enter another window or + * change Visual highlighting. */ + winbar_click(wp, col); + return IN_OTHER_WIN; + } +#endif + /* * winpos and height may change in win_enter()! */ @@ -2829,7 +2840,7 @@ retnomove: #if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD) /* Continue a modeless selection in another window. */ - if (cmdwin_type != 0 && row < W_WINROW(curwin)) + if (cmdwin_type != 0 && row < curwin->w_winrow) return IN_OTHER_WIN; #endif @@ -3117,7 +3128,12 @@ mouse_find_win(int *rowp, int *colp UNUSED) * exist. */ FOR_ALL_WINDOWS(wp) if (wp == fp->fr_win) + { +#ifdef FEAT_MENU + *rowp -= wp->w_winbar_height; +#endif return wp; + } return NULL; } diff --git a/src/version.c b/src/version.c index a241166283..03431c79ea 100644 --- a/src/version.c +++ b/src/version.c @@ -761,6 +761,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1123, /**/ 1122, /**/ diff --git a/src/window.c b/src/window.c index 9b470f6c3b..be8e20ade9 100644 --- a/src/window.c +++ b/src/window.c @@ -4692,6 +4692,10 @@ win_free( } #endif /* FEAT_GUI */ +#ifdef FEAT_MENU + remove_winbar(wp); +#endif + #ifdef FEAT_SYN_HL vim_free(wp->w_p_cc_cols); #endif @@ -5667,6 +5671,7 @@ set_fraction(win_T *wp) /* * Set the height of a window. + * "height" excludes any window toolbar. * This takes care of the things inside the window, not what happens to the * window position, the frame or to other windows. */ -- cgit v1.2.3