changeset 12487:3f16cf18386c v8.0.1123

patch 8.0.1123: cannot define a toolbar for a window commit https://github.com/vim/vim/commit/1b9645de3c05f37b5c30e78f999351b0cf486ade Author: Bram Moolenaar <Bram@vim.org> Date: Sun Sep 17 23:03:31 2017 +0200 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.
author Christian Brabandt <cb@256bit.org>
date Sun, 17 Sep 2017 23:15:04 +0200
parents be9971c7a226
children 6867d1bb5d9c
files runtime/doc/gui.txt runtime/doc/terminal.txt runtime/pack/dist/opt/termdebug/plugin/termdebug.vim src/Makefile src/eval.c src/evalfunc.c src/if_perl.xs src/menu.c src/normal.c src/proto/menu.pro src/proto/syntax.pro src/screen.c src/structs.h src/syntax.c src/terminal.c src/testdir/Make_all.mak src/testdir/test_winbar.vim src/ui.c src/version.c src/window.c
diffstat 20 files changed, 679 insertions(+), 238 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -1,4 +1,4 @@
-*gui.txt*       For Vim version 8.0.  Last change: 2017 Aug 27
+*gui.txt*       For Vim version 8.0.  Last change: 2017 Sep 16
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -784,10 +784,31 @@ In the Win32 and GTK+ GUI, starting a me
 from the main menu bar.  You must then use the |:popup| or |:tearoff| command
 to display it.
 
+							*window-toolbar*
+Each window can have a local toolbar.  This uses the first line of the window,
+thus reduces the space for the text by one line.
+
+Only text can be used.  When using Unicode special characters can be used to
+make the items look like icons.
+
+If the items do not fit then the last ones cannot be used.  The toolbar does
+not wrap.
+
+Example for debugger tools: >
+	amenu 1.10 WinBar.Step :Step<CR>
+	amenu 1.20 WinBar.Next :Next<CR>
+	amenu 1.30 WinBar.Finish :Finish<CR>
+	amenu 1.40 WinBar.Cont :Continue<CR>
+<
+The window toolbar uses the ToolbarLine and ToolbarButton highlight groups.
+
 							*popup-menu*
 In the Win32, GTK+, Motif, Athena and Photon GUI, you can define the
 special menu "PopUp".  This is the menu that is displayed when the right mouse
 button is pressed, if 'mousemodel' is set to popup or popup_setpos.
+Example: >
+    nnoremenu 1.40 PopUp.&Paste	"+gP
+    menu PopUp
 
 
 5.3 Showing What Menus Are Mapped To			*showing-menus*
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -1,4 +1,4 @@
-*terminal.txt*	For Vim version 8.0.  Last change: 2017 Sep 14
+*terminal.txt*	For Vim version 8.0.  Last change: 2017 Sep 17
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -226,7 +226,7 @@ Use CTRL-W N (or 'termkey' N) to switch 
 contents of the terminal window is under control of Vim, the job output is
 suspended.  CTRL-\ CTRL-N does the same.
 
-Terminal-Job mode is where |tmap| mappings are applied. Keys sent by
+Terminal-Job mode is where |:tmap| mappings are applied. Keys sent by
 |term_sendkeys()| are not subject to tmap, but keys from |feedkeys()| are.
 
 							*E946*
@@ -234,7 +234,7 @@ In Terminal-Normal mode you can move the
 commands, Visually mark text, yank text, etc.  But you cannot change the
 contents of the buffer.  The commands that would start insert mode, such as
 'i' and 'a', return to Terminal-Job mode.  The window will be updated to show
-the contents of the terminal.
+the contents of the terminal. |:startinsert| is ineffective.
 
 In Terminal-Normal mode the statusline and window title show "(Terminal)".  If
 the job ends while in Terminal-Normal mode this changes to
@@ -372,6 +372,14 @@ In the window showing the source code so
  :Finish    execute the gdb "finish" command
  :Continue  execute the gdb "continue" command
 
+The plugin adds a window toolbar with these entries:
+  Step	    :Step
+  Next	    :Over
+  Finish    :Finish
+  Cont	    :Continue
+  Eval	    :Evaluate
+This way you can use the mouse to perform the most common commands.
+
 
 Inspecting variables ~
 
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -104,6 +104,11 @@ func s:StartDebug(cmd)
   call win_gotoid(s:gdbwin)
 
   let s:breakpoints = {}
+
+  augroup TermDebug
+    au BufRead * call s:BufRead()
+    au BufUnload * call s:BufUnloaded()
+  augroup END
 endfunc
 
 func s:EndDebug(job, status)
@@ -120,6 +125,8 @@ func s:EndDebug(job, status)
   if s:save_columns > 0
     let &columns = s:save_columns
   endif
+
+  au! TermDebug
 endfunc
 
 " Handle a message received from gdb on the GDB/MI interface.
@@ -132,7 +139,7 @@ func s:CommOutput(chan, msg)
       let msg = msg[1:]
     endif
     if msg != ''
-      if msg =~ '^\*\(stopped\|running\)'
+      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
 	call s:HandleCursor(msg)
       elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
 	call s:HandleNewBreakpoint(msg)
@@ -161,6 +168,14 @@ func s:InstallCommands()
 
   " TODO: can the K mapping be restored?
   nnoremap K :Evaluate<CR>
+
+  if has('menu')
+    amenu WinBar.Step :Step<CR>
+    amenu WinBar.Next :Over<CR>
+    amenu WinBar.Finish :Finish<CR>
+    amenu WinBar.Cont :Continue<CR>
+    amenu WinBar.Eval :Evaluate<CR>
+  endif
 endfunc
 
 " Delete installed debugger commands in the current window.
@@ -176,6 +191,15 @@ func s:DeleteCommands()
   delcommand Program
 
   nunmap K
+
+  if has('menu')
+    aunmenu WinBar.Step
+    aunmenu WinBar.Next
+    aunmenu WinBar.Finish
+    aunmenu WinBar.Cont
+    aunmenu WinBar.Eval
+  endif
+
   exe 'sign unplace ' . s:pc_id
   for key in keys(s:breakpoints)
     exe 'sign unplace ' . (s:break_id + key)
@@ -232,7 +256,15 @@ endfunc
 
 " Handle the result of data-evaluate-expression
 func s:HandleEvaluate(msg)
-  echomsg '"' . s:evalexpr . '": ' . substitute(a:msg, '.*value="\(.*\)"', '\1', '')
+  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
+  let value = substitute(value, '\\"', '"', 'g')
+  echomsg '"' . s:evalexpr . '": ' . value
+
+  if s:evalexpr[0] != '*' && value =~ '^0x' && value !~ '"$'
+    " Looks like a pointer, also display what it points to.
+    let s:evalexpr = '*' . s:evalexpr
+    call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . s:evalexpr . "\"\r")
+  endif
 endfunc
 
 " Handle an error.
@@ -247,10 +279,10 @@ func s:HandleCursor(msg)
 
   if win_gotoid(s:startwin)
     let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
-    if a:msg =~ '^\*stopped' && filereadable(fname)
+    if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
       let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
       if lnum =~ '^[0-9]*$'
-	if expand('%:h') != fname
+	if expand('%:p') != fnamemodify(fname, ':p')
 	  if &modified
 	    " TODO: find existing window
 	    exe 'split ' . fnameescape(fname)
@@ -260,7 +292,7 @@ func s:HandleCursor(msg)
 	  endif
 	endif
 	exe lnum
-	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
+	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
 	setlocal signcolumn=yes
       endif
     else
@@ -288,11 +320,17 @@ func s:HandleNewBreakpoint(msg)
 
   let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
   let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
-
-  exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname)
-
   let entry['fname'] = fname
   let entry['lnum'] = lnum
+
+  if bufloaded(fname)
+    call s:PlaceSign(nr, entry)
+  endif
+endfunc
+
+func s:PlaceSign(nr, entry)
+  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
+  let a:entry['placed'] = 1
 endfunc
 
 " Handle deleting a breakpoint
@@ -302,6 +340,33 @@ func s:HandleBreakpointDelete(msg)
   if nr == 0
     return
   endif
-  exe 'sign unplace ' . (s:break_id + nr)
-  unlet s:breakpoints[nr]
+  if has_key(s:breakpoints, nr)
+    let entry = s:breakpoints[nr]
+    if has_key(entry, 'placed')
+      exe 'sign unplace ' . (s:break_id + nr)
+      unlet entry['placed']
+    endif
+    unlet s:breakpoints[nr]
+  endif
 endfunc
+
+" Handle a BufRead autocommand event: place any signs.
+func s:BufRead()
+  let fname = expand('<afile>:p')
+  for [nr, entry] in items(s:breakpoints)
+    if entry['fname'] == fname
+      call s:PlaceSign(nr, entry)
+    endif
+  endfor
+endfunc
+
+" Handle a BufUnloaded autocommand event: unplace any signs.
+func s:BufUnloaded()
+  let fname = expand('<afile>:p')
+  for [nr, entry] in items(s:breakpoints)
+    if entry['fname'] == fname
+      let entry['placed'] = 0
+    endif
+  endfor
+endfunc
+
--- 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 \
--- 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);
 }
 
 /*
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5229,6 +5229,9 @@ get_win_info(win_T *wp, short tpnr, shor
     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);
 
--- a/src/if_perl.xs
+++ b/src/if_perl.xs
@@ -1387,11 +1387,8 @@ PerlIOVim_pushed(pTHX_ PerlIO *f, const 
 {
     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);
     }
 
--- a/src/menu.c
+++ b/src/menu.c
@@ -83,6 +83,31 @@ static const char *toolbar_names[] =
 #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.
  */
     void
@@ -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))
@@ -986,6 +1032,16 @@ remove_menu(
 }
 
 /*
+ * 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.
  */
     static void
@@ -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, in
  * 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 i
     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 i
 	    || 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 i
 #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 i
 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 
     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 
 		|| 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 
 #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 *
 #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;
-
-    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);
+    int		idx;
 
-	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)
--- 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;
--- 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 : */
--- a/src/proto/syntax.pro
+++ b/src/proto/syntax.pro
@@ -46,6 +46,7 @@ char_u *highlight_has_attr(int id, int f
 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);
--- a/src/screen.c
+++ b/src/screen.c
@@ -107,6 +107,9 @@ static int	screen_cur_row, screen_cur_co
 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_sc
 	 * 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_
 }
 #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.
--- 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? */
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -7002,6 +7002,12 @@ static char *(highlight_init_light[]) = 
     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
 };
 
@@ -7094,6 +7100,12 @@ static char *(highlight_init_dark[]) = {
     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.
--- 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
 	    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);
--- 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.
new file mode 100644
--- /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<CR>
+  amenu 1.20 WinBar.Cont :let g:did_cont = 12<CR>
+  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
--- 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 UNUS
      * exist. */
     FOR_ALL_WINDOWS(wp)
 	if (wp == fp->fr_win)
+	{
+#ifdef FEAT_MENU
+	    *rowp -= wp->w_winbar_height;
+#endif
 	    return wp;
+	}
     return NULL;
 }
 
--- a/src/version.c
+++ b/src/version.c
@@ -762,6 +762,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1123,
+/**/
     1122,
 /**/
     1121,
--- 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.
  */