patch 8.1.1969: popup window filter is used in all modes

Problem:    Popup window filter is used in all modes.
Solution:   Add the "filtermode" property.
diff --git a/src/map.c b/src/map.c
index ac223a9..df8cbed 100644
--- a/src/map.c
+++ b/src/map.c
@@ -897,20 +897,10 @@
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
- * Recognize termcap codes in "str".
- * Also checks mappings local to the current buffer.
- */
     int
-map_to_exists(char_u *str, char_u *modechars, int abbr)
+mode_str2flags(char_u *modechars)
 {
     int		mode = 0;
-    char_u	*rhs;
-    char_u	*buf;
-    int		retval;
-
-    rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE);
 
     if (vim_strchr(modechars, 'n') != NULL)
 	mode |= NORMAL;
@@ -929,7 +919,24 @@
     if (vim_strchr(modechars, 'c') != NULL)
 	mode |= CMDLINE;
 
-    retval = map_to_exists_mode(rhs, mode, abbr);
+    return mode;
+}
+
+/*
+ * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
+ * Recognize termcap codes in "str".
+ * Also checks mappings local to the current buffer.
+ */
+    int
+map_to_exists(char_u *str, char_u *modechars, int abbr)
+{
+    char_u	*rhs;
+    char_u	*buf;
+    int		retval;
+
+    rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE);
+
+    retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
     vim_free(buf);
 
     return retval;
diff --git a/src/popupwin.c b/src/popupwin.c
index abf40e8..226232d 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -845,6 +845,15 @@
 	    wp->w_popup_flags &= ~POPF_MAPPING;
     }
 
+    str = dict_get_string(dict, (char_u *)"filtermode", FALSE);
+    if (str != NULL)
+    {
+	if (STRCMP(str, "a") == 0)
+	    wp->w_filter_mode = MODE_ALL;
+	else
+	    wp->w_filter_mode = mode_str2flags(str);
+    }
+
     di = dict_find(dict, (char_u *)"callback", -1);
     if (di != NULL)
     {
@@ -1851,6 +1860,7 @@
 	wp->w_border_char[i] = 0;
     wp->w_want_scrollbar = 1;
     wp->w_popup_fixed = 0;
+    wp->w_filter_mode = MODE_ALL;
 
     if (d != NULL)
 	// Deal with options.
@@ -2768,6 +2778,7 @@
     int		res = FALSE;
     win_T	*wp;
     int		save_KeyTyped = KeyTyped;
+    int		state;
 
     if (recursive)
 	return FALSE;
@@ -2785,8 +2796,10 @@
 	    res = TRUE;
     }
 
+    state = get_real_state();
     while (!res && (wp = find_next_popup(FALSE)) != NULL)
-	if (wp->w_filter_cb.cb_name != NULL)
+	if (wp->w_filter_cb.cb_name != NULL
+		&& (wp->w_filter_mode & state) != 0)
 	    res = invoke_popup_filter(wp, c);
 
     recursive = FALSE;
diff --git a/src/proto/map.pro b/src/proto/map.pro
index 05d66fb..cf1b31a 100644
--- a/src/proto/map.pro
+++ b/src/proto/map.pro
@@ -4,6 +4,7 @@
 int is_maphash_valid(void);
 int do_map(int maptype, char_u *arg, int mode, int abbrev);
 void map_clear_int(buf_T *buf, int mode, int local, int abbr);
+int mode_str2flags(char_u *modechars);
 int map_to_exists(char_u *str, char_u *modechars, int abbr);
 int map_to_exists_mode(char_u *rhs, int mode, int abbr);
 char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forceit, int isabbrev, int isunmap, cmdidx_T cmdidx);
diff --git a/src/structs.h b/src/structs.h
index 35a22b1..e599550 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -3054,6 +3054,7 @@
 				      // computed
     callback_T	w_close_cb;	    // popup close callback
     callback_T	w_filter_cb;	    // popup filter callback
+    int		w_filter_mode;	    // mode when filter callback is used
 
     win_T	*w_popup_curwin;    // close popup if curwin differs
     linenr_T	w_popup_lnum;	    // close popup if cursor not on this line
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 8fd4da1..36cf306 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -1892,6 +1892,72 @@
   delfunc MyPopupFilter
 endfunc
 
+func Test_popupwin_filter_mode()
+  func MyPopupFilter(winid, c)
+    let s:typed = a:c
+    if a:c == ':' || a:c == "\r" || a:c == 'v'
+      " can start cmdline mode, get out, and start/stop Visual mode
+      return 0
+    endif
+    return 1
+  endfunc
+
+  " Normal, Visual and Insert mode
+  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'nvi'})
+  redraw
+  call feedkeys('x', 'xt')
+  call assert_equal('x', s:typed)
+
+  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
+  call assert_equal(':', s:typed)
+  call assert_equal('foo', g:foo)
+
+  let @x = 'something'
+  call feedkeys('v$"xy', 'xt')
+  call assert_equal('y', s:typed)
+  call assert_equal('something', @x)  " yank command is filtered out
+  call feedkeys('v', 'xt')  " end Visual mode
+
+  call popup_close(winid)
+
+  " only Normal mode
+  let winid = popup_create('something', #{filter: 'MyPopupFilter', filtermode: 'n'})
+  redraw
+  call feedkeys('x', 'xt')
+  call assert_equal('x', s:typed)
+
+  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
+  call assert_equal(':', s:typed)
+  call assert_equal('foo', g:foo)
+
+  let @x = 'something'
+  call feedkeys('v$"xy', 'xt')
+  call assert_equal('v', s:typed)
+  call assert_notequal('something', @x)
+
+  call popup_close(winid)
+
+  " default: all modes
+  let winid = popup_create('something', #{filter: 'MyPopupFilter'})
+  redraw
+  call feedkeys('x', 'xt')
+  call assert_equal('x', s:typed)
+
+  let g:foo = 'bar'
+  call feedkeys(":let g:foo = 'foo'\<CR>", 'xt')
+  call assert_equal("\r", s:typed)
+  call assert_equal('bar', g:foo)
+
+  let @x = 'something'
+  call feedkeys('v$"xy', 'xt')
+  call assert_equal('y', s:typed)
+  call assert_equal('something', @x)  " yank command is filtered out
+  call feedkeys('v', 'xt')  " end Visual mode
+
+  call popup_close(winid)
+  delfunc MyPopupFilter
+endfunc
+
 func Test_popupwin_with_buffer()
   call writefile(['some text', 'in a buffer'], 'XsomeFile')
   let buf = bufadd('XsomeFile')
diff --git a/src/version.c b/src/version.c
index 5dbdc9f..655b68a 100644
--- a/src/version.c
+++ b/src/version.c
@@ -762,6 +762,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1969,
+/**/
     1968,
 /**/
     1967,
diff --git a/src/vim.h b/src/vim.h
index e9d586b..29441c8 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -680,6 +680,7 @@
 #define CONFIRM		0x800	// ":confirm" prompt
 #define SELECTMODE	0x1000	// Select mode, only for mappings
 #define TERMINAL        0x2000  // Terminal mode
+#define MODE_ALL	0xffff
 
 // all mode bits used for mapping
 #define MAP_ALL_MODES	(0x3f | SELECTMODE | TERMINAL)