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.
diff --git a/src/Makefile b/src/Makefile
index 247fee6..ad96784 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2278,6 +2278,7 @@
 	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 0d954a0..8b1ceae 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -8252,13 +8252,7 @@
     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 8d370d4..6c2a73e 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5229,6 +5229,9 @@
     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 8936838..d67645c 100644
--- a/src/if_perl.xs
+++ b/src/if_perl.xs
@@ -1387,11 +1387,8 @@
 {
     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 @@
     {
 	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 343a1b8..1ad8a5c 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -83,6 +83,31 @@
 #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 @@
     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 @@
 # 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 @@
 		    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 @@
 		    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 @@
 	    ))
 	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 @@
     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 @@
     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 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 @@
     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 @@
  * 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 @@
 	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 @@
 		    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 @@
 	    }
 	    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 @@
 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 @@
     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 @@
 	    || 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 @@
 #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_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 @@
     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 @@
 		|| 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 @@
 #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 @@
 #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 @@
 	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 @@
     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 @@
     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 a8b6ffa..6fffbcb 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -2679,9 +2679,9 @@
 			 * 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 e370835..bd3d26b 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 @@
 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 0f64cea..f6ecb8c 100644
--- a/src/proto/syntax.pro
+++ b/src/proto/syntax.pro
@@ -46,6 +46,7 @@
 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 d93ce50..9f39edf 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -107,6 +107,9 @@
 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 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 @@
 	 * 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_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 @@
 }
 #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 @@
      *    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 @@
     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 @@
 		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 @@
     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 18a047c..94d7391 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -70,6 +70,10 @@
 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 @@
 #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 @@
      */
     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 @@
 
     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 @@
 /* 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 c6e0156..6361bd7 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -7002,6 +7002,12 @@
     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 @@
     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 @@
     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 db17417..7dc21d6 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 @@
 	    update_screen(0);
 	    update_cursor(term, TRUE);
 	}
-	else if (buffer->b_nwindows > 0)
+	else
 	    redraw_after_callback(TRUE);
     }
 }
@@ -1545,7 +1546,7 @@
     {
 	/* 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 b979c97..169d84f 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -201,12 +201,13 @@
 	    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 0000000..1961607
--- /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
diff --git a/src/ui.c b/src/ui.c
index 49782ae..c26cb37 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -2657,7 +2657,7 @@
 	}
 #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 @@
 	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 @@
 
 #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 @@
      * 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 a241166..03431c7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -762,6 +762,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1123,
+/**/
     1122,
 /**/
     1121,
diff --git a/src/window.c b/src/window.c
index 9b470f6..be8e20a 100644
--- a/src/window.c
+++ b/src/window.c
@@ -4692,6 +4692,10 @@
     }
 #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 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.
  */