menu: Add support for user defined item choice function

Selecting menu items is currently done in menu_interactive_choice()
by reading the user input strings from standard input.

Extend menu_interactive_choice() to support user defined function
for selecting menu items. This function and its argument can be
specified when creating the menu.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Anatolij Gustschin <agust@denx.de>
diff --git a/board/ait/cam_enc_4xx/cam_enc_4xx.c b/board/ait/cam_enc_4xx/cam_enc_4xx.c
index 32b28f9..644c445 100644
--- a/board/ait/cam_enc_4xx/cam_enc_4xx.c
+++ b/board/ait/cam_enc_4xx/cam_enc_4xx.c
@@ -561,7 +561,8 @@
 	char *s;
 	char temp[6][200];
 
-	m = menu_create(display->title, display->timeout, 1, ait_menu_print);
+	m = menu_create(display->title, display->timeout, 1, ait_menu_print,
+			NULL, NULL);
 
 	for (i = 0; display->menulist[i]; i++) {
 		sprintf(key, "%d", i + 1);
diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c
index ee75db9..2dbd49c 100644
--- a/common/cmd_pxe.c
+++ b/common/cmd_pxe.c
@@ -1280,7 +1280,8 @@
 	/*
 	 * Create a menu and add items for all the labels.
 	 */
-	m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print);
+	m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print,
+			NULL, NULL);
 
 	if (!m)
 		return NULL;
diff --git a/common/menu.c b/common/menu.c
index 6b2a2db..322b75e 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -47,6 +47,8 @@
 	char *title;
 	int prompt;
 	void (*item_data_print)(void *);
+	char *(*item_choice)(void *);
+	void *item_choice_data;
 	struct list_head items;
 };
 
@@ -204,18 +206,26 @@
 
 		menu_display(m);
 
-		readret = readline_into_buffer("Enter choice: ", cbuf,
-				m->timeout / 10);
+		if (!m->item_choice) {
+			readret = readline_into_buffer("Enter choice: ", cbuf,
+					m->timeout / 10);
 
-		if (readret >= 0) {
-			choice_item = menu_item_by_key(m, cbuf);
-
-			if (!choice_item) {
-				printf("%s not found\n", cbuf);
-				m->timeout = 0;
+			if (readret >= 0) {
+				choice_item = menu_item_by_key(m, cbuf);
+				if (!choice_item)
+					printf("%s not found\n", cbuf);
+			} else {
+				return menu_default_choice(m, choice);
 			}
-		} else
-			return menu_default_choice(m, choice);
+		} else {
+			char *key = m->item_choice(m->item_choice_data);
+
+			if (key)
+				choice_item = menu_item_by_key(m, key);
+		}
+
+		if (!choice_item)
+			m->timeout = 0;
 	}
 
 	*choice = choice_item->data;
@@ -348,11 +358,19 @@
  * what must be entered to select an item, the item_data_print function should
  * make it obvious what the key for each entry is.
  *
+ * item_choice - If not NULL, will be called when asking the user to choose an
+ * item. Returns a key string corresponding to the choosen item or NULL if
+ * no item has been selected.
+ *
+ * item_choice_data - Will be passed as the argument to the item_choice function
+ *
  * Returns a pointer to the menu if successful, or NULL if there is
  * insufficient memory available to create the menu.
  */
 struct menu *menu_create(char *title, int timeout, int prompt,
-				void (*item_data_print)(void *))
+				void (*item_data_print)(void *),
+				char *(*item_choice)(void *),
+				void *item_choice_data)
 {
 	struct menu *m;
 
@@ -365,6 +383,8 @@
 	m->prompt = prompt;
 	m->timeout = timeout;
 	m->item_data_print = item_data_print;
+	m->item_choice = item_choice;
+	m->item_choice_data = item_choice_data;
 
 	if (title) {
 		m->title = strdup(title);
diff --git a/doc/README.menu b/doc/README.menu
index 6ce6bba..c949398 100644
--- a/doc/README.menu
+++ b/doc/README.menu
@@ -51,7 +51,9 @@
  * menu_create() - Creates a menu handle with default settings
  */
 struct menu *menu_create(char *title, int timeout, int prompt,
-				void (*item_data_print)(void *));
+				void (*item_data_print)(void *),
+				char *(*item_choice)(void *),
+				void *item_choice_data);
 
 /*
  * menu_item_add() - Adds or replaces a menu item
diff --git a/include/menu.h b/include/menu.h
index 7af5fdb..f4dd5af 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -21,7 +21,9 @@
 struct menu;
 
 struct menu *menu_create(char *title, int timeout, int prompt,
-				void (*item_data_print)(void *));
+				void (*item_data_print)(void *),
+				char *(*item_choice)(void *),
+				void *item_choice_data);
 int menu_default_set(struct menu *m, char *item_key);
 int menu_get_choice(struct menu *m, void **choice);
 int menu_item_add(struct menu *m, char *item_key, void *item_data);