menu: Heirachical callback-based menu system
authorsjlongland <sjlongland@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 24 Sep 2015 08:12:19 +0000 (08:12 +0000)
committersjlongland <sjlongland@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 24 Sep 2015 08:12:19 +0000 (08:12 +0000)
This is an event-driven menu handler that allows arbirary depth and
event handling.

git-svn-id: https://svn.code.sf.net/p/freetel/code@2368 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/stm32/inc/menu.h [new file with mode: 0644]
codec2-dev/stm32/src/menu.c [new file with mode: 0644]

diff --git a/codec2-dev/stm32/inc/menu.h b/codec2-dev/stm32/inc/menu.h
new file mode 100644 (file)
index 0000000..f93601b
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef _MENU_H
+#define _MENU_H
+/*!
+ * Callback driven menu handler.
+ *
+ * The following is an implementation of a callback-driven menu system.
+ * It supports arbitrary levels of menus (limited by size of return stack)
+ * and supports arbitrary user events.
+ * 
+ * Author Stuart Longland <me@vk4msl.id.au>
+ * Copyright (C) 2015 FreeDV project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.  This program is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+#define MENU_STACK_SZ  8       /*!< Size of the menu return stack */
+
+#define MENU_EVT_ENTERED 0     /*!< Menu item has been entered */
+#define MENU_EVT_RETURNED 1    /*!< We have returned from a submenu */
+
+/*! Menu state structure */
+struct menu_t {
+       /*! The last seen menu item */
+       const struct menu_item_t*               last;
+       /*! Currently selected item index */
+       uint32_t                                current;
+       /*! Current menu item stack */
+       struct menu_stack_item_t {
+               const struct menu_item_t*       item;
+               uint32_t                        index;
+       }                                       stack[MENU_STACK_SZ];
+       /*! Present depth of the stack */
+       uint8_t                                 stack_depth;
+};
+
+/*! Menu item structure */
+struct menu_item_t {
+       /*! Morse-code label for the menu item */
+       const char*             label;
+       /*! Event callback pointer for menu item */
+       void                    (*event_cb)(
+                       struct menu_t* const menu,
+                       uint32_t event);
+       /*! Children of this menu item */
+       const struct menu_item_t** const        children;
+       uint32_t                                num_children;
+       /*! Arbitrary data */
+       union menu_item_data_t {
+               /*! Arbitrary pointer */
+               const void*                     p;
+               /*! Arbitrary unsigned integer */
+               uintptr_t                       ui;
+               /*! Arbitrary signed integer */
+               intptr_t                        si;
+       }                                       data;
+};
+
+/*!
+ * Return the Nth item on the stack.
+ */
+const struct menu_item_t* const menu_item(
+               const struct menu_t* const menu, uint8_t index);
+
+/*!
+ * Enter a (sub)-menu.
+ * @retval     -1      Stack is full
+ * @retval     0       Success
+ */
+int menu_enter(struct menu_t* const menu,
+               const struct menu_item_t* const item);
+
+/*! Return from a (sub)-menu */
+void menu_leave(struct menu_t* const menu);
+
+/*!
+ * Execute the callback for the current item with a user-supplied event.
+ */
+void menu_exec(struct menu_t* const menu, uint32_t event);
+
+#endif
diff --git a/codec2-dev/stm32/src/menu.c b/codec2-dev/stm32/src/menu.c
new file mode 100644 (file)
index 0000000..cb8ba97
--- /dev/null
@@ -0,0 +1,98 @@
+/*!
+ * Callback driven menu handler.
+ *
+ * The following is an implementation of a callback-driven menu system.
+ * It supports arbitrary levels of menus (limited by size of return stack)
+ * and supports arbitrary user events.
+ * 
+ * Author Stuart Longland <me@vk4msl.id.au>
+ * Copyright (C) 2015 FreeDV project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.  This program is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "menu.h"
+#include <stdlib.h>
+
+/*!
+ * Return the Nth item on the stack.
+ */
+static const struct menu_stack_item_t* const menu_stack(
+               const struct menu_t* const menu,
+               uint8_t index)
+{
+       if (menu->stack_depth <= index)
+               return NULL;
+
+       return &(menu->stack[menu->stack_depth - index - 1]);
+}
+
+/*!
+ * Return the Nth item on the stack.
+ */
+const struct menu_item_t* const menu_item(
+               const struct menu_t* const menu, uint8_t index)
+{
+       const struct menu_stack_item_t* const current
+               = menu_stack(menu, index);
+
+       if (!current)
+               return NULL;
+       return current->item;
+}
+
+/*!
+ * Enter a (sub)-menu.
+ */
+int menu_enter(struct menu_t* const menu,
+               const struct menu_item_t* const item)
+{
+       if (menu->stack_depth == MENU_STACK_SZ)
+               return -1;
+
+       menu->stack[menu->stack_depth].item = item;
+       menu->stack[menu->stack_depth].index = menu->current;
+       menu->stack_depth++;
+
+       (item->event_cb)(menu, MENU_EVT_ENTERED);
+
+       return 0;
+}
+
+/*!
+ * Return from a (sub)-menu.
+ */
+void menu_leave(struct menu_t* const menu)
+{
+       if (!menu->stack_depth)
+               return; /* Already out of the menu */
+
+       menu->last = menu_item(menu, 0);
+       menu->stack_depth--;
+
+       const struct menu_stack_item_t* current = menu_stack(menu, 0);
+       if (current && current->item) {
+               menu->current = current->index;
+               (current->item->event_cb)(menu, MENU_EVT_RETURNED);
+       }
+}
+
+/*!
+ * Execute the callback for the current item with a user-supplied event.
+ */
+void menu_exec(struct menu_t* const menu, uint32_t event)
+{
+       const struct menu_item_t* item = menu_item(menu, 0);
+       if (item && item->event_cb)
+               (item->event_cb)(menu, event);
+}