653 lines
13 KiB
C++
653 lines
13 KiB
C++
/* $Id: MenuBackend.h 1231 2011-08-25 10:57:49Z abrevig $
|
|
||
|
|
|| @author Alexander Brevig <abrevig@wiring.org.co>
|
|
|| @url http://wiring.org.co/
|
|
|| @contribution Adrian Brzezinski
|
|
|| @contribution Bernhard Benum
|
|
|| @contribution Brett Hagman <bhagman@wiring.org.co>
|
|
|| @contribution Ryan Michael <kerinin@gmail.com>
|
|
||
|
|
|| @description
|
|
|| | Provides an easy way of making menus.
|
|
|| |
|
|
|| | Wiring Cross-platform Library
|
|
|| #
|
|
||
|
|
|| @license Please see cores/Common/License.txt.
|
|
||
|
|
*/
|
|
|
|
#ifndef MENUBACKEND_H
|
|
#define MENUBACKEND_H
|
|
#if defined(ARDUINO) && ARDUINO >= 100
|
|
#include "Arduino.h"
|
|
#else
|
|
#include "WProgram.h"
|
|
#endif
|
|
|
|
class MenuBackend; //forward declaration of the menu backend
|
|
class MenuItem; //forward declaration of the menu item
|
|
|
|
struct MenuChangeEvent
|
|
{
|
|
//const MenuItem &from;
|
|
const MenuItem &to;
|
|
};
|
|
|
|
struct MenuUseEvent
|
|
{
|
|
MenuItem &item;
|
|
};
|
|
|
|
struct MenuItemChangeEvent
|
|
{
|
|
const MenuItem &item;
|
|
};
|
|
|
|
struct MenuMoveEvent
|
|
{
|
|
const MenuItem &item;
|
|
};
|
|
|
|
typedef void (*cb_change)(MenuChangeEvent);
|
|
typedef void (*cb_use)(MenuUseEvent);
|
|
typedef void (*cb_move)(MenuMoveEvent);
|
|
|
|
|
|
/*
|
|
A menu item will be a container for an item that is a part of a menu
|
|
Each such item has a logical position in the hierarchy as well as a text and maybe a mnemonic shortkey
|
|
*/
|
|
class MenuItem
|
|
{
|
|
public:
|
|
const __FlashStringHelper *name;
|
|
|
|
/*
|
|
|| @constructor
|
|
|| | Basic item with a name and an optional mnemonic [File (f)]
|
|
|| #
|
|
||
|
|
|| @example
|
|
|| | MenuItem file = MenuItem("File",'F');
|
|
|| #
|
|
||
|
|
|| @parameter itemName the name of the item
|
|
|| @parameter shortKey the mnemonic 'shortkey'
|
|
*/
|
|
MenuItem(const __FlashStringHelper *itemName, uint8_t shortKey = 0) : name(itemName), shortkey(shortKey)
|
|
{
|
|
before = right = after = left = 0;
|
|
menuBackend = 0;
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Register a backend for this item to use for callbacks and such
|
|
|| #
|
|
||
|
|
|| @parameter mb the menu backend that controls this item
|
|
*/
|
|
inline void registerBackend(MenuBackend &mb)
|
|
{
|
|
menuBackend = &mb;
|
|
}
|
|
|
|
//void use(){} //update some internal data / statistics
|
|
/*
|
|
|| @description
|
|
|| | Get the name of this item
|
|
|| #
|
|
||
|
|
|| @return The name of this item
|
|
*/
|
|
inline const __FlashStringHelper * getName() const
|
|
{
|
|
return name;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Get the shortkey of this item
|
|
|| #
|
|
||
|
|
|| @return The shortkey of this item
|
|
*/
|
|
inline uint8_t getShortkey() const
|
|
{
|
|
return shortkey;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Check to see if this item has a shorkey
|
|
|| #
|
|
||
|
|
|| @return true if this item has a shorkey
|
|
*/
|
|
inline const bool hasShortkey() const
|
|
{
|
|
return (shortkey != '\0');
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Check to see if this item has children
|
|
|| #
|
|
||
|
|
|| @return true if the item has children
|
|
*/
|
|
inline const bool hasChildren() const
|
|
{
|
|
return (before || right || after || left);
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Get the item 'before' this item
|
|
|| #
|
|
||
|
|
|| @return the item before this
|
|
*/
|
|
inline MenuItem *getBefore() const
|
|
{
|
|
return before;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Get the item 'right' of this item
|
|
|| #
|
|
||
|
|
|| @return the item right of this
|
|
*/
|
|
inline MenuItem *getRight() const
|
|
{
|
|
return right;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Get the item 'after' this item
|
|
|| #
|
|
||
|
|
|| @return the item after this
|
|
*/
|
|
inline MenuItem *getAfter() const
|
|
{
|
|
return after;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Get the item 'left' of this item
|
|
|| #
|
|
||
|
|
|| @return the item left of this
|
|
*/
|
|
inline MenuItem *getLeft() const
|
|
{
|
|
return left;
|
|
}
|
|
|
|
//default vertical menu
|
|
/*
|
|
|| @description
|
|
|| | Add an item after this item in the hierarchy
|
|
|| #
|
|
||
|
|
|| @parameter mi the item to add
|
|
|| @return the item sent as parameter for chaining
|
|
*/
|
|
MenuItem &add(MenuItem &mi)
|
|
{
|
|
return addAfter(mi);
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Add an item before this item in the hierarchy
|
|
|| #
|
|
||
|
|
|| @parameter mi the item to add
|
|
|| @return the item sent as parameter for chaining
|
|
*/
|
|
MenuItem &addBefore(MenuItem &mi)
|
|
{
|
|
mi.after = this;
|
|
before = &mi;
|
|
return mi;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Add an item right to this item in the hierarchy
|
|
|| #
|
|
||
|
|
|| @parameter mi the item to add
|
|
|| @return the item sent as parameter for chaining
|
|
*/
|
|
MenuItem &addRight(MenuItem &mi)
|
|
{
|
|
mi.left = this;
|
|
right = &mi;
|
|
return mi;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Add an item after this item in the hierarchy
|
|
|| #
|
|
||
|
|
|| @parameter mi the item to add
|
|
|| @return the item sent as parameter for chaining
|
|
*/
|
|
MenuItem &addAfter(MenuItem &mi)
|
|
{
|
|
mi.before = this;
|
|
after = &mi;
|
|
return mi;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Add an item left of this item in the hierarchy
|
|
|| #
|
|
||
|
|
|| @parameter mi the item to add
|
|
|| @return the item sent as parameter for chaining
|
|
*/
|
|
MenuItem &addLeft(MenuItem &mi)
|
|
{
|
|
mi.right = this;
|
|
left = &mi;
|
|
return mi;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired before any 'move' function is called with this item
|
|
|| | as the current MenuItem.
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onChangeFrom(cb_change cb)
|
|
{
|
|
cb_onChangeFrom = cb;
|
|
return *this;
|
|
}*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired after any 'move' function is called with this item
|
|
|| | as the resulting MenuItem.
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onChangeTo(cb_change cb)
|
|
{
|
|
cb_onChangeTo = cb;
|
|
return *this;
|
|
}
|
|
*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired when 'moveUp' is called with this item as the current MenuItem
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onUp(cb_move cb)
|
|
{
|
|
cb_onUp = cb;
|
|
return *this;
|
|
}*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired when 'moveDown' is called with this item as the current MenuItem
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onDown(cb_move cb)
|
|
{
|
|
cb_onDown = cb;
|
|
return *this;
|
|
}*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired when 'moveLeft' is called with this item as the current MenuItem
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onLeft(cb_move cb)
|
|
{
|
|
cb_onLeft = cb;
|
|
return *this;
|
|
}*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired when 'moveRight' is called with this item as the current MenuItem
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onRight(cb_move cb)
|
|
{
|
|
cb_onRight = cb;
|
|
return *this;
|
|
}*/
|
|
/*
|
|
|| @description
|
|
|| | Set a callback to be fired when 'use' is called with this item as the current MenuItem
|
|
|| #
|
|
||
|
|
|| @paramter cb The callback to be fired
|
|
|| @return this MenuItem
|
|
*/
|
|
/*
|
|
MenuItem &onUse(cb_use cb)
|
|
{
|
|
cb_onUse = cb;
|
|
return *this;
|
|
}*/
|
|
|
|
protected:
|
|
uint8_t shortkey;
|
|
|
|
MenuItem *before;
|
|
MenuItem *right;
|
|
MenuItem *after;
|
|
MenuItem *left;
|
|
|
|
//cb_change cb_onChangeFrom;
|
|
// cb_change cb_onChangeTo;
|
|
// cb_move cb_onUp;
|
|
// cb_move cb_onDown;
|
|
// cb_move cb_onLeft;
|
|
// cb_move cb_onRight;
|
|
// cb_use cb_onUse;
|
|
|
|
MenuBackend *menuBackend;
|
|
|
|
private:
|
|
friend class MenuBackend;
|
|
|
|
MenuItem *moveUp()
|
|
{
|
|
return before;
|
|
}
|
|
|
|
MenuItem *moveDown()
|
|
{
|
|
return after;
|
|
}
|
|
|
|
MenuItem *moveLeft()
|
|
{
|
|
return left;
|
|
}
|
|
|
|
MenuItem *moveRight()
|
|
{
|
|
return right;
|
|
}
|
|
|
|
};
|
|
|
|
class MenuBackend
|
|
{
|
|
public:
|
|
MenuBackend(cb_use menuUse, cb_change menuChange = 0) : root(F(""))
|
|
{
|
|
current = &root;
|
|
cb_menuChange = menuChange;
|
|
cb_menuUse = menuUse;
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Get the root of this menu
|
|
|| #
|
|
||
|
|
|| @return the root item
|
|
*/
|
|
MenuItem &getRoot()
|
|
{
|
|
return root;
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Get the current item of this menu
|
|
|| #
|
|
||
|
|
|| @return the current item
|
|
*/
|
|
MenuItem &getCurrent()
|
|
{
|
|
return *current;
|
|
}
|
|
|
|
/*
|
|
|| @description
|
|
|| | Move up in the menu structure, will fire move event
|
|
|| #
|
|
*/
|
|
void moveUp()
|
|
{/*
|
|
if (current->cb_onUp)
|
|
{
|
|
MenuMoveEvent mme = { *current };
|
|
(*current->cb_onUp)(mme);
|
|
}*/
|
|
setCurrent(current->moveUp());
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Move down in the menu structure, will fire move event
|
|
|| #
|
|
*/
|
|
void moveDown()
|
|
{/*
|
|
if (current->cb_onDown)
|
|
{
|
|
MenuMoveEvent mme = { *current };
|
|
(*current->cb_onDown)(mme);
|
|
}*/
|
|
setCurrent(current->moveDown());
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Move left in the menu structure, will fire move event
|
|
|| #
|
|
*/
|
|
void moveLeft()
|
|
{
|
|
/*
|
|
if (current->cb_onLeft)
|
|
{
|
|
MenuMoveEvent mme = { *current };
|
|
(*current->cb_onLeft)(mme);
|
|
}*/
|
|
setCurrent(current->moveLeft());
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Move right in the menu structure, will fire move event
|
|
|| #
|
|
*/
|
|
void moveRight()
|
|
{/*
|
|
if (current->cb_onRight)
|
|
{
|
|
MenuMoveEvent mme = { *current };
|
|
(*current->cb_onRight)(mme);
|
|
}*/
|
|
setCurrent(current->moveRight());
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Use an item
|
|
|| #
|
|
||
|
|
|| @parameter item is the item to use
|
|
*/
|
|
void use(MenuItem &item)
|
|
{
|
|
setCurrent(&item);
|
|
use();
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Use an item per its shortkey
|
|
|| #
|
|
||
|
|
|| @parameter shortkey the shortkey of the target item
|
|
*/
|
|
void use(uint8_t shortkey)
|
|
{
|
|
recursiveSearch(shortkey, &root);
|
|
use();
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Use an item, will fire use event
|
|
|| #
|
|
*/
|
|
void use()
|
|
{
|
|
//current->use();
|
|
/*
|
|
if (current->cb_onUse)
|
|
{
|
|
MenuUseEvent mue = { *current };
|
|
(*current->cb_onUse)(mue);
|
|
}*/
|
|
if (cb_menuUse)
|
|
{
|
|
MenuUseEvent mue = { *current };
|
|
cb_menuUse(mue);
|
|
}
|
|
}
|
|
/*
|
|
|| @description
|
|
|| | Select an item, will fire change event
|
|
|| #
|
|
*/
|
|
void select(MenuItem &item)
|
|
{
|
|
setCurrent(&item);
|
|
}
|
|
|
|
private:
|
|
void setCurrent(MenuItem *next)
|
|
{
|
|
if (next)
|
|
{
|
|
MenuChangeEvent mce = { *next };
|
|
/*
|
|
if (current->cb_onChangeFrom)
|
|
{
|
|
(*current->cb_onChangeFrom)(mce);
|
|
}*/
|
|
/*
|
|
if (next->cb_onChangeTo)
|
|
{
|
|
(*next->cb_onChangeTo)(mce);
|
|
}*/
|
|
if (cb_menuChange)
|
|
{
|
|
(*cb_menuChange)(mce);
|
|
}
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
void foundShortkeyItem(MenuItem *mi)
|
|
{
|
|
current = mi;
|
|
}
|
|
|
|
char canSearch(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (m == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if (m->getShortkey() == shortkey)
|
|
{
|
|
foundShortkeyItem(m);
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void rSAfter(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (canSearch(shortkey, m) != 1)
|
|
{
|
|
rSAfter(shortkey, m->getAfter());
|
|
rSRight(shortkey, m->getRight());
|
|
rSLeft(shortkey, m->getLeft());
|
|
}
|
|
}
|
|
|
|
void rSRight(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (canSearch(shortkey, m) != 1)
|
|
{
|
|
rSAfter(shortkey, m->getAfter());
|
|
rSRight(shortkey, m->getRight());
|
|
rSBefore(shortkey, m->getBefore());
|
|
}
|
|
}
|
|
|
|
void rSLeft(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (canSearch(shortkey, m) != 1)
|
|
{
|
|
rSAfter(shortkey, m->getAfter());
|
|
rSLeft(shortkey, m->getLeft());
|
|
rSBefore(shortkey, m->getBefore());
|
|
}
|
|
}
|
|
|
|
void rSBefore(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (canSearch(shortkey, m) != 1)
|
|
{
|
|
rSRight(shortkey, m->getRight());
|
|
rSLeft(shortkey, m->getLeft());
|
|
rSBefore(shortkey, m->getBefore());
|
|
}
|
|
}
|
|
|
|
void recursiveSearch(uint8_t shortkey, MenuItem *m)
|
|
{
|
|
if (canSearch(shortkey, m) != 1)
|
|
{
|
|
rSAfter(shortkey, m->getAfter());
|
|
rSRight(shortkey, m->getRight());
|
|
rSLeft(shortkey, m->getLeft());
|
|
rSBefore(shortkey, m->getBefore());
|
|
}
|
|
}
|
|
|
|
MenuItem root;
|
|
MenuItem *current;
|
|
|
|
cb_change cb_menuChange;
|
|
cb_use cb_menuUse;
|
|
};
|
|
|
|
#endif
|
|
// MENUBACKEND_H
|