All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dirk Gouders <dirk@gouders.net>
To: Masahiro Yamada <yamada.masahiro@socionext.com>,
	Randy Dunlap <rdunlap@infradead.org>,
	Linux Kbuild mailing list <linux-kbuild@vger.kernel.org>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Segher Boessenkool <segher@kernel.crashing.org>,
	Sam Ravnborg <sam@ravnborg.org>
Cc: Dirk Gouders <dirk@gouders.net>
Subject: [RFC v5 1/1] i-search navigation for mconf
Date: Mon, 11 Jun 2018 14:51:55 +0200	[thread overview]
Message-ID: <20180611125155.21470-2-dirk@gouders.net> (raw)
In-Reply-To: <20180611125155.21470-1-dirk@gouders.net>

This patch implements an i-search navigation concept for mconf based
on an idea of Sam Ravnborg:

* mconf now distinguishes if the focus is on the menu items or the
  buttons below it.

* At startup focus is on the menu items and printable characters
  (except the keys below) entered are used to form a string that is
  searched for.

* Hotkeys were sacrificed for the i-search navigation concept.
  However, focus on the buttons offers most of mconf's known behavior,
  except the TAB key and a change in the highlighting of menu items.

Keys with special meaning:

* BACKSPACE is used to remove the last character of the search string

* DEL clears the search string

* ENTER can be used to enter a submenu; it also clears the search string

* Horizontal arrow keys still cycle between the buttons but also
  switch focus to the buttons.

* The TAB key is now exclusively used to toggle the focus.

* Vertical arrow keys work everywhere.

* When the focus is on the buttons, all keys work the
  same as before this change -- except TAB and hotkeys.

* <\> (backslash) can be used to search for other occurences of an
  already entered string.

* To use y|n|m|<space> on an item, focus has to be on the buttons.

An example to navigate into the USB support menu under Device Drivers:

   1) de		# Navigates to the item "Device Drivers"
   2) ENTER		# Enter the submenu
   3) USB		# Navigate to the item "USB support"
   4) ENTER		# Enter the submenu

Suggested-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Dirk Gouders <dirk@gouders.net>
---
 scripts/kconfig/lxdialog/dialog.h  |   3 +
 scripts/kconfig/lxdialog/menubox.c | 489 +++++++++++++++++++++++++++----------
 scripts/kconfig/lxdialog/util.c    | 106 +++++++-
 scripts/kconfig/mconf.c            |  68 ++++--
 4 files changed, 506 insertions(+), 160 deletions(-)

diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 0b00be5abaa6..28a3b42fdf71 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -215,6 +215,8 @@ void end_dialog(int x, int y);
 void attr_clear(WINDOW * win, int height, int width, chtype attr);
 void dialog_clear(void);
 void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
+void print_autowrap_fill(WINDOW * win, const char *prompt, int width,
+			int height, int y, int x);
 void print_button(WINDOW * win, const char *label, int y, int x, int selected);
 void print_title(WINDOW *dialog, const char *title, int width);
 void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
@@ -238,6 +240,7 @@ int dialog_checklist(const char *title, const char *prompt, int height,
 		     int width, int list_height);
 int dialog_inputbox(const char *title, const char *prompt, int height,
 		    int width, const char *init);
+int do_isearch(char *str, int choice, int scroll);
 
 /*
  * This is the base for fictitious keys, which activate
diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c
index d70cab36137e..7b22ec813d3c 100644
--- a/scripts/kconfig/lxdialog/menubox.c
+++ b/scripts/kconfig/lxdialog/menubox.c
@@ -56,22 +56,38 @@
  * fscanf would read in 'scroll', and eventually that value would get used.
  */
 
+#include <string.h>
 #include "dialog.h"
 
+#define ISEARCH_LEN 32
+static char isearch_str[ISEARCH_LEN] = "";
+
 static int menu_width, item_x;
 
+static int focus_on_buttons;
+
+static const char isearch_instructions[] =
+	"I-search: Arrow keys navigate the menu.  "
+	"<Enter> selects submenus and/or clears i-search string.  "
+	"Type any character to search for menu items, "
+	"press <\\> to find further matches, <Esc><Esc> to exit. "
+	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable";
 /*
  * Print menu item
  */
 static void do_print_item(WINDOW * win, const char *item, int line_y,
-			  int selected, int hotkey)
+			  int selected)
 {
-	int j;
+	int i;
+	int isearch_match_pos;
+	char *isearch_match;
 	char *menu_item = malloc(menu_width + 1);
 
 	strncpy(menu_item, item, menu_width - item_x);
 	menu_item[menu_width - item_x] = '\0';
-	j = first_alpha(menu_item, "YyNnMmHh");
+
+	isearch_match = strcasestr(menu_item, isearch_str);
+	isearch_match_pos = isearch_match - menu_item;
 
 	/* Clear 'residue' of last item */
 	wattrset(win, dlg.menubox.atr);
@@ -85,14 +101,24 @@ static void do_print_item(WINDOW * win, const char *item, int line_y,
 #else
 	wclrtoeol(win);
 #endif
-	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+	if (focus_on_buttons)
+		wattrset(win, selected ? A_UNDERLINE : dlg.item.atr);
+	else
+		wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
 	mvwaddstr(win, line_y, item_x, menu_item);
-	if (hotkey) {
-		wattrset(win, selected ? dlg.tag_key_selected.atr
-			 : dlg.tag_key.atr);
-		mvwaddch(win, line_y, item_x + j, menu_item[j]);
-	}
+
 	if (selected) {
+		/*
+		 * Highlight i-search matching part of selected menu item
+		 */
+		if (isearch_match) {
+			for (i = 0; i < strlen(isearch_str); i++) {
+				wattrset(win, dlg.tag_key_selected.atr);
+				mvwaddch(win, line_y, item_x + isearch_match_pos + i,
+					 menu_item[isearch_match_pos + i]);
+			}
+		}
+
 		wmove(win, line_y, item_x + 1);
 	}
 	free(menu_item);
@@ -102,9 +128,37 @@ static void do_print_item(WINDOW * win, const char *item, int line_y,
 #define print_item(index, choice, selected)				\
 do {									\
 	item_set(index);						\
-	do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
+	do_print_item(menu, item_str(), choice, selected); \
 } while (0)
 
+
+/*
+* Print the isearch indicator.
+*/
+static void print_isearch(WINDOW * win, int y, int x, int height, bool isearch)
+{
+	unsigned char i = 0;
+	int text_size = ISEARCH_LEN - 1;
+	wmove(win, y, x);
+
+	y = y + height + 1;
+	wmove(win, y, x);
+
+	if (isearch) {
+		wattrset(win, dlg.button_key_inactive.atr);
+		waddstr(win, "isearch: ");
+		waddstr(win, isearch_str);
+		i = strlen(isearch_str);
+	} else {
+		text_size += 9; /* also overwrite "isearch: " */
+	}
+
+	wattrset(win, dlg.menubox_border.atr);
+
+	for ( ; i < text_size; i++ )
+		waddch(win, ACS_HLINE);
+}
+
 /*
  * Print the scroll indicators.
  */
@@ -156,12 +210,22 @@ static void print_buttons(WINDOW * win, int height, int width, int selected)
 {
 	int x = width / 2 - 28;
 	int y = height - 2;
+	int highlight;
+
+	/*
+	 * Don't highlight the selected button if the buttons don't have
+	 * the focus.
+	 */
+	if (!focus_on_buttons)
+		highlight = -1;
+	else
+		highlight = selected;
 
-	print_button(win, "Select", y, x, selected == 0);
-	print_button(win, " Exit ", y, x + 12, selected == 1);
-	print_button(win, " Help ", y, x + 24, selected == 2);
-	print_button(win, " Save ", y, x + 36, selected == 3);
-	print_button(win, " Load ", y, x + 48, selected == 4);
+	print_button(win, "Select", y, x, highlight == 0);
+	print_button(win, " Exit ", y, x + 12, highlight == 1);
+	print_button(win, " Help ", y, x + 24, highlight == 2);
+	print_button(win, " Save ", y, x + 36, highlight == 3);
+	print_button(win, " Load ", y, x + 48, highlight == 4);
 
 	wmove(win, y, x + 1 + 12 * selected);
 	wrefresh(win);
@@ -178,13 +242,40 @@ static void do_scroll(WINDOW *win, int *scroll, int n)
 	wrefresh(win);
 }
 
+/*
+ * Incremental search for text in dialog menu entries.
+ * The search operates as a ring search, continuing at the top after
+ * the last entry has been visited.
+ *
+ * Returned is -1 if no match was found, else the absolute index of
+ * the matching item.
+ */
+int do_isearch(char *str, int choice, int scroll)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < item_count(); i++) {
+		item_set((choice + scroll + i)%item_count());
+		if (strcasestr(item_str(), str)) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found)
+		return (choice + scroll + i)%item_count();
+	return -1;
+}
+
 /*
  * Display a menu for choosing among a number of options
  */
 int dialog_menu(const char *title, const char *prompt,
 		const void *selected, int *s_scroll)
 {
-	int i, j, x, y, box_x, box_y;
+	int i, x, y, box_x, box_y;
+	int key_match;		/* remember match in switch statement */
 	int height, width, menu_height;
 	int key = 0, button = 0, scroll = 0, choice = 0;
 	int first_item =  0, max_choice;
@@ -224,7 +315,9 @@ int dialog_menu(const char *title, const char *prompt,
 	print_title(dialog, title, width);
 
 	wattrset(dialog, dlg.dialog.atr);
-	print_autowrap(dialog, prompt, width - 2, 1, 3);
+	print_autowrap_fill(dialog,
+			   focus_on_buttons ? prompt : isearch_instructions,
+			   width - 2, 4, 1, 3);
 
 	menu_width = width - 6;
 	box_y = height - menu_height - 5;
@@ -275,6 +368,7 @@ int dialog_menu(const char *title, const char *prompt,
 	print_arrows(dialog, item_count(), scroll,
 		     box_y, box_x + item_x + 1, menu_height);
 
+	print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, !focus_on_buttons);
 	print_buttons(dialog, height, width, 0);
 	wmove(menu, choice, item_x + 1);
 	wrefresh(menu);
@@ -284,44 +378,129 @@ int dialog_menu(const char *title, const char *prompt,
 
 		if (key < 256 && isalpha(key))
 			key = tolower(key);
-
-		if (strchr("ynmh", key))
-			i = max_choice;
-		else {
-			for (i = choice + 1; i < max_choice; i++) {
-				item_set(scroll + i);
-				j = first_alpha(item_str(), "YyNnMmHh");
-				if (key == tolower(item_str()[j]))
-					break;
+		/*
+		 * These keys are handled for the focus on both,
+		 * menu and buttons.
+		 */
+		key_match = 0;
+		switch (key) {
+		case KEY_DC:	/* delete key clears i-search string */
+			key_match = 1;
+			isearch_str[0] = '\0';
+			break;
+		case TAB:
+			key_match = 1;
+			focus_on_buttons = 1 - focus_on_buttons;
+			wattrset(dialog, dlg.dialog.atr);
+			print_autowrap_fill(dialog,
+					   focus_on_buttons ? prompt : isearch_instructions,
+					   width - 2, 4, 1, 3);
+			break;
+		case KEY_LEFT:
+		case KEY_RIGHT:
+			key_match = 1;
+			if (!focus_on_buttons) {
+				focus_on_buttons = 1;
+				wattrset(dialog, dlg.dialog.atr);
+				print_autowrap_fill(dialog, prompt, width - 2, 4, 1, 3);
+				wnoutrefresh(dialog);
 			}
-			if (i == max_choice)
-				for (i = 0; i < max_choice; i++) {
-					item_set(scroll + i);
-					j = first_alpha(item_str(), "YyNnMmHh");
-					if (key == tolower(item_str()[j]))
-						break;
-				}
+			button = ((key == KEY_LEFT ? --button : ++button) < 0)
+			    ? 4 : (button > 4 ? 0 : button);
+			break;
+		case KEY_ESC:
+			key = on_key_esc(menu);
+			continue;
+		case KEY_RESIZE:
+			on_key_resize();
+			delwin(menu);
+			delwin(dialog);
+			goto do_resize;
+		}
+		if (key_match) {
+			print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, !focus_on_buttons);
+			print_item(scroll + choice, choice, TRUE);
+			print_buttons(dialog, height, width, button);
+			wrefresh(menu);
+			continue;	/* wait for another key press */
 		}
 
-		if (item_count() != 0 &&
-		    (i < max_choice ||
-		     key == KEY_UP || key == KEY_DOWN ||
-		     key == '-' || key == '+' ||
-		     key == KEY_PPAGE || key == KEY_NPAGE)) {
-			/* Remove highligt of current item */
+		key_match = 0;
+		switch (key) {
+		case KEY_UP:
+			key_match = 1;
+			/* Remove highlight of current item */
+			print_item(scroll + choice, choice, FALSE);
+			if (choice < 2 && scroll) {
+				/* Scroll menu down */
+				do_scroll(menu, &scroll, -1);
+				print_item(scroll, 0, FALSE);
+			} else
+				choice = MAX(choice - 1, 0);
+			break;
+		case KEY_DOWN:
+			key_match = 1;
+			/* Remove highlight of current item */
 			print_item(scroll + choice, choice, FALSE);
 
-			if (key == KEY_UP || key == '-') {
-				if (choice < 2 && scroll) {
-					/* Scroll menu down */
-					do_scroll(menu, &scroll, -1);
+			if ((choice > max_choice - 3) &&
+			    (scroll + max_choice < item_count())) {
+				/* Scroll menu up */
+				do_scroll(menu, &scroll, 1);
+				print_item(scroll+max_choice - 1,
+					   max_choice - 1, FALSE);
+			} else
+				choice = MIN(choice + 1, max_choice - 1);
+			break;
+		case KEY_PPAGE:
+			key_match = 1;
+			/* Remove highlight of current item */
+			print_item(scroll + choice, choice, FALSE);
 
+			scrollok(menu, TRUE);
+			for (i = 0; (i < max_choice); i++) {
+				if (scroll > 0) {
+					do_scroll(menu, &scroll, -1);
 					print_item(scroll, 0, FALSE);
-				} else
-					choice = MAX(choice - 1, 0);
+				} else {
+					if (choice > 0)
+						choice--;
+				}
+			}
+			break;
+		case KEY_NPAGE:
+			key_match = 1;
+			/* Remove highlight of current item */
+			print_item(scroll + choice, choice, FALSE);
+			for (i = 0; (i < max_choice); i++) {
+				if (scroll + max_choice < item_count()) {
+					do_scroll(menu, &scroll, 1);
+					print_item(scroll+max_choice-1,
+						   max_choice - 1, FALSE);
+				} else {
+					if (choice + 1 < max_choice)
+						choice++;
+				}
+			}
+		}
+
+		if (key_match) {
+			print_item(scroll + choice, choice, TRUE);
+			print_arrows(dialog, item_count(), scroll,
+				     box_y, box_x + item_x + 1, menu_height);
+			wnoutrefresh(dialog);
+			wrefresh(menu);
+			continue;	/* wait for another key press */
+		}
 
-			} else if (key == KEY_DOWN || key == '+') {
-				print_item(scroll+choice, choice, FALSE);
+		if (focus_on_buttons) {
+			/*
+			 * Focus is on buttons, handle appropriate keys.
+			 */
+			switch (key) {
+			case '+':
+				/* Remove highlight of current item */
+				print_item(scroll + choice, choice, FALSE);
 
 				if ((choice > max_choice - 3) &&
 				    (scroll + max_choice < item_count())) {
@@ -332,106 +511,150 @@ int dialog_menu(const char *title, const char *prompt,
 						   max_choice - 1, FALSE);
 				} else
 					choice = MIN(choice + 1, max_choice - 1);
-
-			} else if (key == KEY_PPAGE) {
-				scrollok(menu, TRUE);
-				for (i = 0; (i < max_choice); i++) {
-					if (scroll > 0) {
-						do_scroll(menu, &scroll, -1);
-						print_item(scroll, 0, FALSE);
-					} else {
-						if (choice > 0)
-							choice--;
-					}
+				print_item(scroll + choice, choice, TRUE);
+				print_arrows(dialog, item_count(), scroll,
+					     box_y, box_x + item_x + 1, menu_height);
+				wnoutrefresh(dialog);
+				wrefresh(menu);
+				continue;	/* wait for another key press */
+			case '-':
+				/* Remove highlight of current item */
+				print_item(scroll + choice, choice, FALSE);
+				if (choice < 2 && scroll) {
+					/* Scroll menu down */
+					do_scroll(menu, &scroll, -1);
+					print_item(scroll, 0, FALSE);
+				} else
+					choice = MAX(choice - 1, 0);
+				print_item(scroll + choice, choice, TRUE);
+				print_arrows(dialog, item_count(), scroll,
+					     box_y, box_x + item_x + 1, menu_height);
+				wnoutrefresh(dialog);
+				wrefresh(menu);
+				continue;	/* wait for another key press */
+			case '\n':
+				isearch_str[0] = '\0';
+				/* fallthrough */
+			case ' ':
+			case 's':
+			case 'y':
+			case 'n':
+			case 'm':
+			case '/':
+			case 'h':
+			case '?':
+			case 'z':
+				/* save scroll info */
+				*s_scroll = scroll;
+				delwin(menu);
+				delwin(dialog);
+				item_set(scroll + choice);
+				item_set_selected(1);
+				switch (key) {
+				case 'h':
+				case '?':
+					return 2;
+				case 's':
+				case 'y':
+					return 5;
+				case 'n':
+					return 6;
+				case 'm':
+					return 7;
+				case ' ':
+					return 8;
+				case '/':
+					return 9;
+				case 'z':
+					return 10;
+				case '\n':
+					return button;
 				}
-
-			} else if (key == KEY_NPAGE) {
-				for (i = 0; (i < max_choice); i++) {
-					if (scroll + max_choice < item_count()) {
+				return 0;
+			case 'e':
+			case 'x':
+				key = KEY_ESC;
+				break;
+			}
+			continue;
+		} else {	/* !focus_on_buttons */
+			if (key == '\n') {
+				/* save scroll info */
+				*s_scroll = scroll;
+				delwin(menu);
+				delwin(dialog);
+				item_set(scroll + choice);
+				item_set_selected(1);
+				isearch_str[0] = '\0';
+				return 0; /* 0 means first button "Select" */
+			} else if ( key == KEY_BACKSPACE ) {
+				if ( isearch_str[0] )
+					isearch_str[i = (strlen(isearch_str) - 1)] = '\0';
+				/* Remove highlight of current item */
+				print_item(scroll + choice, choice, FALSE);
+				i = do_isearch(isearch_str, choice + 1, scroll);
+			} else if (key == '\\') {
+				/*
+				 * Check \ before printable chars,
+				 * because it is reserved to search
+				 * further matches.
+				 */
+				/* Remove highlight of current item */
+				print_item(scroll + choice, choice, FALSE);
+				i = do_isearch(isearch_str, choice + 1, scroll);
+			} else if (key < 256 && (isprint(key) || key == ' ')) {
+				if (strlen(isearch_str) < ISEARCH_LEN - 1) {
+					isearch_str[i = strlen(isearch_str)] = key;
+					isearch_str[i+1] = '\0';
+					/* Remove highlight of current item */
+					print_item(scroll + choice, choice, FALSE);
+					i = do_isearch(isearch_str, choice, scroll);
+				} else
+					continue;
+			} else
+				continue;
+
+			/*
+			 * Handle matches
+			 */
+			if (i >= 0) {
+				i -= scroll;
+
+				if (i >= max_choice)
+					/*
+					 * Handle matches below the currently visible menu entries.
+					 */
+					while (i >= max_choice) {
 						do_scroll(menu, &scroll, 1);
-						print_item(scroll+max_choice-1,
-							   max_choice - 1, FALSE);
-					} else {
-						if (choice + 1 < max_choice)
-							choice++;
+						i--;
+						print_item(max_choice + scroll - 1, max_choice - 1, false);
+					}
+				else if (i < 0)
+					/*
+					 * Handle matches higher in the menu (ring search).
+					 */
+					while (i < 0) {
+						do_scroll(menu, &scroll, -1);
+						i++;
+						print_item(scroll, 0, false);
 					}
-				}
-			} else
 				choice = i;
+			} else {
+				i = choice;
+			}
 
 			print_item(scroll + choice, choice, TRUE);
-
+			print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, true);
 			print_arrows(dialog, item_count(), scroll,
 				     box_y, box_x + item_x + 1, menu_height);
 
 			wnoutrefresh(dialog);
 			wrefresh(menu);
-
-			continue;	/* wait for another key press */
-		}
-
-		switch (key) {
-		case KEY_LEFT:
-		case TAB:
-		case KEY_RIGHT:
-			button = ((key == KEY_LEFT ? --button : ++button) < 0)
-			    ? 4 : (button > 4 ? 0 : button);
-
-			print_buttons(dialog, height, width, button);
-			wrefresh(menu);
-			break;
-		case ' ':
-		case 's':
-		case 'y':
-		case 'n':
-		case 'm':
-		case '/':
-		case 'h':
-		case '?':
-		case 'z':
-		case '\n':
-			/* save scroll info */
-			*s_scroll = scroll;
-			delwin(menu);
-			delwin(dialog);
-			item_set(scroll + choice);
-			item_set_selected(1);
-			switch (key) {
-			case 'h':
-			case '?':
-				return 2;
-			case 's':
-			case 'y':
-				return 5;
-			case 'n':
-				return 6;
-			case 'm':
-				return 7;
-			case ' ':
-				return 8;
-			case '/':
-				return 9;
-			case 'z':
-				return 10;
-			case '\n':
-				return button;
-			}
-			return 0;
-		case 'e':
-		case 'x':
-			key = KEY_ESC;
-			break;
-		case KEY_ESC:
-			key = on_key_esc(menu);
-			break;
-		case KEY_RESIZE:
-			on_key_resize();
-			delwin(menu);
-			delwin(dialog);
-			goto do_resize;
+			continue;
 		}
 	}
 	delwin(menu);
 	delwin(dialog);
+	isearch_str[0] = '\0';
 	return key;		/* ESC pressed */
 }
diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c
index f7abdeb92af0..fde4865ff83a 100644
--- a/scripts/kconfig/lxdialog/util.c
+++ b/scripts/kconfig/lxdialog/util.c
@@ -373,13 +373,79 @@ void print_title(WINDOW *dialog, const char *title, int width)
 	}
 }
 
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+{
+	int newl, cur_x, cur_y;
+	int prompt_len, room, wlen;
+	char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0;
+
+	strcpy(tempstr, prompt);
+
+	prompt_len = strlen(tempstr);
+
+	if (prompt_len <= width - x * 2) {	/* If prompt is short */
+		wmove(win, y, (width - prompt_len) / 2);
+		waddstr(win, tempstr);
+	} else {
+		cur_x = x;
+		cur_y = y;
+		newl = 1;
+		word = tempstr;
+		while (word && *word) {
+			sp = strpbrk(word, "\n ");
+			if (sp && *sp == '\n')
+				newline_separator = sp;
+
+			if (sp)
+				*sp++ = 0;
+
+			/* Wrap to next line if either the word does not fit,
+			   or it is the first word of a new sentence, and it is
+			   short, and the next word does not fit. */
+			room = width - cur_x;
+			wlen = strlen(word);
+			if (wlen > room ||
+			    (newl && wlen < 4 && sp
+			     && wlen + 1 + strlen(sp) > room
+			     && (!(sp2 = strpbrk(sp, "\n "))
+				 || wlen + 1 + (sp2 - sp) > room))) {
+				cur_y++;
+				cur_x = x;
+			}
+			wmove(win, cur_y, cur_x);
+			waddstr(win, word);
+			getyx(win, cur_y, cur_x);
+
+			/* Move to the next line if the word separator was a newline */
+			if (newline_separator) {
+				cur_y++;
+				cur_x = x;
+				newline_separator = 0;
+			} else
+				cur_x++;
+
+			if (sp && *sp == ' ') {
+				cur_x++;	/* double space */
+				while (*++sp == ' ') ;
+				newl = 1;
+			} else
+				newl = 0;
+			word = sp;
+		}
+	}
+}
+
 /*
  * Print a string of text in a window, automatically wrap around to the
  * next line if the string is too long to fit on one line. Newline
  * characters '\n' are propperly processed.  We start on a new line
  * if there is no room for at least 4 nonblanks following a double-space.
+ *
+ * This function fills all of and at most the area width x height so
+ * that it can be used to overwrite previosly displayed text.
  */
-void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+void print_autowrap_fill(WINDOW * win, const char *prompt, int width,
+			int height, int y, int x)
 {
 	int newl, cur_x, cur_y;
 	int prompt_len, room, wlen;
@@ -415,7 +481,13 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
 			     && wlen + 1 + strlen(sp) > room
 			     && (!(sp2 = strpbrk(sp, "\n "))
 				 || wlen + 1 + (sp2 - sp) > room))) {
+				while (cur_x < width) {
+					waddch(win, ' ');
+					cur_x++;
+				}
 				cur_y++;
+				if (cur_y - y >= height)
+					break;
 				cur_x = x;
 			}
 			wmove(win, cur_y, cur_x);
@@ -424,13 +496,24 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
 
 			/* Move to the next line if the word separator was a newline */
 			if (newline_separator) {
+				while (cur_x < width) {
+					waddch(win, ' ');
+					cur_x++;
+				}
 				cur_y++;
+				if (cur_y - y >= height)
+					break;
 				cur_x = x;
 				newline_separator = 0;
-			} else
+			} else {
+				if (cur_x < width)
+					waddch(win, ' ');
 				cur_x++;
+			}
 
 			if (sp && *sp == ' ') {
+				if (cur_x < width)
+					waddch(win, ' ');
 				cur_x++;	/* double space */
 				while (*++sp == ' ') ;
 				newl = 1;
@@ -439,6 +522,25 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
 			word = sp;
 		}
 	}
+
+	/*
+	 * Fill remaining space to overwrite possibly existing text.
+	 */
+	wmove(win, cur_y, cur_x);
+	while (cur_x < width) {
+		waddch(win, ' ');
+		cur_x++;
+	}
+	wmove(win, cur_y + 1, x);
+	getyx(win, cur_y, cur_x);
+	while (cur_y - y < height) {
+		while (cur_x < width) {
+			waddch(win, ' ');
+			cur_x++;
+		}
+		wmove(win, cur_y + 1, x);
+		getyx(win, cur_y, cur_x);
+	}
 }
 
 /*
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 5294ed159b98..b25d4738136b 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -36,43 +36,62 @@ static const char mconf_readme[] =
 "while *, M or whitespace inside braces means to build in, build as\n"
 "a module or to exclude the feature respectively.\n"
 "\n"
-"To change any of these features, highlight it with the cursor\n"
-"keys and press <Y> to build it in, <M> to make it a module or\n"
+"Operation modes\n"
+"---------------\n"
+"Menuconfig operates in two modes, depending on the focus that can be\n"
+"either on the menu or the buttons below it.\n"
+"\n"
+"To change any of the above features, it has to be navigated to (see\n"
+"below) so that it is highlited, focus then has to be on the buttons\n"
+"before you press <Y> to build it in, <M> to make it a module or\n"
 "<N> to remove it.  You may also press the <Space Bar> to cycle\n"
 "through the available options (i.e. Y->N->M->Y).\n"
 "\n"
-"Some additional keyboard hints:\n"
-"\n"
-"Menus\n"
+"Navigation\n"
 "----------\n"
-"o  Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
-"   wish to change or the submenu you wish to select and press <Enter>.\n"
-"   Submenus are designated by \"--->\", empty ones by \"----\".\n"
+"The following keys work independent of the current focus:\n"
 "\n"
-"   Shortcut: Press the option's highlighted letter (hotkey).\n"
-"             Pressing a hotkey more than once will sequence\n"
-"             through all visible items which use that hotkey.\n"
+"o vertical arrow keys are used to navigate to menu items\n"
 "\n"
-"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
-"   unseen options into view.\n"
+"o horizontal arrow keys cycle between the buttons\n"
+"  If used with focus on the menu, the focus also changes to the buttons\n"
 "\n"
-"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
-"   and press <ENTER>.\n"
+"o <PAGE UP> and <PAGE DOWN> scroll invisible items into view\n"
 "\n"
-"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
-"             using those letters.  You may press a single <ESC>, but\n"
-"             there is a delayed response which you may find annoying.\n"
+"o <ENTER> visits a submenu\n"
+"  Submenus are designated by \"--->\", empty ones by \"----\".\n"
 "\n"
-"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
-"   <Exit>, <Help>, <Save>, and <Load>.\n"
+"o <ESC><ESC> leaves a submenu or (in the main menu) exits menuconfig\n"
 "\n"
-"o  To get help with an item, use the cursor keys to highlight <Help>\n"
-"   and press <ENTER>.\n"
+"o <TAB> is reserved to toggle the focus between menu and buttons\n"
 "\n"
-"   Shortcut: Press <H> or <?>.\n"
+"When menuconfig starts, the focus is on the menu and i-search mode\n"
+"is active.  You can enter subsequent characters to build a string the\n"
+"menu items are searched for.  Keys with a special meaning are:\n"
+"\n"
+"o <BACKSPACE> removes the last character of the current search string\n"
+"\n"
+"o <DEL> clears the complete search string\n"
+"\n"
+"o <ENTER> also clears the whole string, but also visits a submenu\n"
+"  if the selected menu item is one\n"
 "\n"
-"o  To toggle the display of hidden options, press <Z>.\n"
+"o <\\> (backslash) can be used to find further matches of a string\n"
 "\n"
+"When the focus is on the buttons the following keys can be used:\n"
+"\n"
+"o <x> can be used for exit identical to <ESC><ESC>\n"
+"\n"
+"o <y>, <n>, <m> or <SPACE> change the selected item\n"
+"\n"
+"o <+> and <-> keys navigate menu items identical to vertical arrow\n"
+"  keys\n"
+"\n"
+"o <h> or <?> display help messages\n"
+"\n"
+"o <z> toggles the display of hidden options\n"
+"\n"
+"Some additional keyboard hints:\n"
 "\n"
 "Radiolists  (Choice lists)\n"
 "-----------\n"
@@ -174,7 +193,6 @@ static const char mconf_readme[] =
 menu_instructions[] =
 	"Arrow keys navigate the menu.  "
 	"<Enter> selects submenus ---> (or empty submenus ----).  "
-	"Highlighted letters are hotkeys.  "
 	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
 	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
 	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable",
-- 
2.16.4

      reply	other threads:[~2018-06-11 12:59 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-11 12:51 [RFC v5 0/1] i-search navigation for mconf Dirk Gouders
2018-06-11 12:51 ` Dirk Gouders [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180611125155.21470-2-dirk@gouders.net \
    --to=dirk@gouders.net \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=sam@ravnborg.org \
    --cc=segher@kernel.crashing.org \
    --cc=yamada.masahiro@socionext.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.