user26860: I'm trying to make a navigational menu with sub menus to display on an OLED.  Have managed this OK, however I would like to display the next item on the same level, to encourage scrolling, but I am unable to figure this out.
I want to have it so that the current menu item is displayed, along with the next item one would reach if you scrolled downwards.  I know you can display the previous and current items easily with   
  Serial.print(fromFlash(changed.from.getName())); 

and 
  Serial.println(fromFlash(changed.to.getName()));

respectively - but I would like to display the current and the next (below but on same level) item.  How can I do this?  I played around for most of today on this but am pretty stumped.
I've simplified the example given which is below.  Below that, I've included menubackend.h 
Running on an ATMEGA328P chip.
#include "MenuBackend.h"

/*
    This is the structure of the modelled menu

    Settings
        Pin
        Debug
    Options
        Delay (D)
            100 ms
            200 ms
            300 ms
            400 ms
 */

#define TEST_FROMFLASH
#undef TEST_GETITEMNAMEFROMFLASH

const char itemNameSettings[] PROGMEM = "Settings";
const char itemNamePin[] PROGMEM      = "Pin";
const char itemNameDebug[] PROGMEM    = "Debug";
const char itemNameOptions[] PROGMEM  = "Options";
const char itemNameDelay[] PROGMEM    = "Delay";
const char itemName100ms[] PROGMEM    = "100 ms";
const char itemName200ms[] PROGMEM    = "200 ms";
const char itemName300ms[] PROGMEM    = "300 ms";
const char itemName400ms[] PROGMEM    = "400 ms";
const char itemNameshutdown[] PROGMEM = "shutdown";


static void menuChangeEvent(MenuChangeEvent changed);
static void menuUseEvent(MenuUseEvent used);
//this controls the menu backend and the event generation
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
//beneath is list of menu items needed to build the menu
MenuItem settings = MenuItem(itemNameSettings);
MenuItem pin = MenuItem(itemNamePin);
MenuItem debug = MenuItem(itemNameDebug);
MenuItem options = MenuItem(itemNameOptions);
MenuItem setDelay = MenuItem(itemNameDelay,'D');
MenuItem d100 = MenuItem(itemName100ms);
MenuItem d200 = MenuItem(itemName200ms);
MenuItem d300 = MenuItem(itemName300ms);
MenuItem d400 = MenuItem(itemName400ms);
MenuItem shutdown1 = MenuItem(itemNameshutdown);

//this function builds the menu and connects the correct items together
void menuSetup()
{
    Serial.println(F("Setting up menu..."));
    //add the file menu to the menu root
    menu.getRoot().add(settings); 
    //setup the settings menu item
    settings.addRight(pin);
    //we want looping both up and down
       pin.addBefore(debug);
       pin.addAfter(debug);
       debug.addAfter(pin);
    //we want a left movement to pint to settings from anywhere
       debug.addLeft(shutdown1);
       pin.addLeft(settings);
    settings.addAfter(options);
    shutdown1.addAfter(settings);
    options.addAfter(shutdown1);
      options.addRight(setDelay);
      setDelay.addLeft(options);
      setDelay.addRight(d100);
      d100.addBefore(d100); //loop to d400 
      d100.addAfter(d200);
      d200.addAfter(d300);
      d300.addAfter(d400);
      d400.addAfter(d100); //loop back to d100
      //we want left to always be bak to delay
      d100.addLeft(setDelay);
      d200.addLeft(setDelay);
      d300.addLeft(setDelay);
      d400.addLeft(setDelay);


}

#ifdef TEST_FROMFLASH

inline const __FlashStringHelper * fromFlash(const char * pStr)
{
    return reinterpret_cast<const __FlashStringHelper *>(pStr);
}

#endif

#ifdef TEST_GETITEMNAMEFROMFLASH

inline char * getItemNameFromFlash(const MenuItem & item, char * copyToBuffer, size_t bufferSize)
{
    strncpy_P(copyToBuffer, item.getName(), bufferSize);
    copyToBuffer[bufferSize - 1] = '\0'; // Make sure the buffer is null-terminated. 

    return copyToBuffer;
}

#endif

/*
    This is an important function
    Here all use events are handled

    This is where you define a behaviour for a menu item
 */
void menuUseEvent(MenuUseEvent used)
{
    Serial.print(F("Menu use "));

#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(used.item.getName()));
#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';
    Serial.println(getItemNameFromFlash(used.item, itemNameBuffer, sizeof(itemNameBuffer)));
#endif

    if (used.item == setDelay) //comparison against a known item
    {
        Serial.println(F("menuUseEvent found Delay (D)"));
    }
}

/*
    This is an important function
    Here we get a notification whenever the user changes the menu
    That is, when the menu is navigated
 */
void menuChangeEvent(MenuChangeEvent changed)
{
    Serial.print("Menu change ");

#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(changed.to.getName()));

#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';
    Serial.print(getItemNameFromFlash(changed.from, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.print(" char is ");
    Serial.println(getItemNameFromFlash(changed.to, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}

void setup()
{
    Serial.begin(9600);

    Serial.println(F("Revision 1.0."));

    menuSetup();
    Serial.println(F("Starting navigation:\r\n   Down: s   Left: a   Right: d   Use: e"));
}

void showAllNames()
{
#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(settings.getName()));
    Serial.println(fromFlash(pin.getName()));
    Serial.println(fromFlash(debug.getName()));
    Serial.println(fromFlash(options.getName()));
    Serial.println(fromFlash(setDelay.getName()));
    Serial.println(fromFlash(d100.getName()));
    Serial.println(fromFlash(d200.getName()));
    Serial.println(fromFlash(d300.getName()));
    Serial.println(fromFlash(d400.getName()));
#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';

    Serial.println(getItemNameFromFlash(settings, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(pin, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(debug, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(options, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(setDelay, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d100, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d200, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d300, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d400, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}

void loop()
{
    if (Serial.available()) {
        byte read = Serial.read();
        switch (read) {
        case 's': 
            menu.moveDown(); 
            break;
        case 'd': 
            menu.moveRight(); 
            break;
        case 'a': 
            menu.moveLeft(); 
            break;
        case 'e': 
            menu.use(); 
            break;
        case 'z': 
            showAllNames(); 
            break;
        }
    }
}

MenuBackend.h
/*
||
|| @file    MenuBackend.h
|| @version 1.4
|| @author  Alexander Brevig
|| @contact alexanderbrevig@gmail.com
|| @contribution Adrian Brzezinski adrb@wp.pl, http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?action=viewprofile;username=vzhang
||
|| @description
|| | Provide an easy way of making menus
|| #
||
|| @license
|| | This library is free software; you can redistribute it and/or
|| | modify it under the terms of the GNU Lesser General Public
|| | License as published by the Free Software Foundation; version
|| | 2.1 of the License.
|| |
|| | This library 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
|| | Lesser General Public License for more details.
|| |
|| | You should have received a copy of the GNU Lesser General Public
|| | License along with this library; if not, write to the Free Software
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
|| #
||
*/

#ifndef MenuBackend_h
#define MenuBackend_h

#include <avr/pgmspace.h>

/*
    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:
    MenuItem(const char * itemName, char shortKey='\0' ) : name(itemName), shortkey(shortKey) {
        before = right = after = left = 0;
    }

    //void use(){} //update some internal data / statistics
    inline const char * getName() const { return name; }
    inline const char getShortkey() const { return shortkey; }
    inline const bool hasShortkey() const { return (shortkey!='\0'); }
    inline void setBack(MenuItem *b) { back = b; }
    inline MenuItem* getBack() const { return back; }
    inline MenuItem* getBefore() const { return before; }
    inline MenuItem* getRight() const { return right; }
    inline MenuItem* getAfter() const { return after; }
    inline MenuItem* getLeft() const { return left; }

    MenuItem *moveBack() { return back; }

    MenuItem *moveUp() { 
        if (before) { before->back = this; }
        return before; 
    }

    MenuItem *moveDown() { 
        if (after) { after->back = this; }
        return after; 
    }

    MenuItem *moveLeft() { 
        if (left) { left->back = this; }
        return left; 
    }

    MenuItem *moveRight() { 
        if (right) { right->back = this; }
        return right; 
    }

    //default vertical menu
    MenuItem &add(MenuItem &mi) { return addAfter(mi); }

    MenuItem &addBefore(MenuItem &mi) {
        mi.after = this;
        before = &mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &addRight(MenuItem &mi) {
        mi.left = this;
        right = &mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &addAfter(MenuItem &mi) {
        mi.before = this;
        after = &mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &addLeft(MenuItem &mi) {
        mi.right = this;
        left = &mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
protected:

    const char * name;
    const char shortkey;

    MenuItem *before;
    MenuItem *right;
    MenuItem *after;
    MenuItem *left;
    MenuItem *back;
};

//no dependant inclusion of string or cstring
bool menuTestStrings1(const char *a, const char *b) { // a is in flash, b is in SRAM.
    for (uint8_t da = pgm_read_byte_near(a); da; da = pgm_read_byte_near(++a), b++) 
    { 
        if (da != *b) 
            return false; 
    }
    return (*b == '\0'); // We reached the end of a.  Strings are equal only if we reached the end of b too.
}
bool menuTestStrings2(const char *a, const char *b) { // a and b are both in flash.
    uint8_t da = pgm_read_byte_near(a); // Dereferenced a.
    uint8_t db = pgm_read_byte_near(b); // Dereferenced b.

    for ( ; da; da = pgm_read_byte_near(++a), db = pgm_read_byte_near(++b)) 
    { 
        if (da != db)
            return false; 
    }

    return (db == '\0'); // We reached the end of a.  Strings are equal only if we reached the end of b too.
}
bool operator==(MenuItem &lhs, char* test) {
    return menuTestStrings1(lhs.getName(),test);
}
bool operator==(const MenuItem &lhs, char* test) {
    return menuTestStrings1(lhs.getName(),test);
}
bool operator==(MenuItem &lhs, MenuItem &rhs) {
    return menuTestStrings2(lhs.getName(),rhs.getName());
}
bool operator==(const MenuItem &lhs, MenuItem &rhs) {
    return menuTestStrings2(lhs.getName(),rhs.getName());
}

struct MenuChangeEvent {
    const MenuItem &from;
    const MenuItem &to;
};

struct MenuUseEvent {
    const MenuItem &item;
};

typedef void (*cb_change)(MenuChangeEvent);
typedef void (*cb_use)(MenuUseEvent);

const char itemNameMenuRoot[] PROGMEM = "MenuRoot";

class MenuBackend {
public:

    MenuBackend(cb_use menuUse, cb_change menuChange = 0) : root(itemNameMenuRoot) {
        current = &root;
        cb_menuChange = menuChange;
        cb_menuUse = menuUse;
    }

    MenuItem &getRoot() {
        return root;
    }
    MenuItem &getCurrent() {
        return *current;
    }

    void moveBack() {
        setCurrent(current->getBack());
    }

    void moveUp() {
        setCurrent(current->moveUp());
    }

    void moveDown() {
        setCurrent(current->moveDown());
    }

    void moveLeft() {
        setCurrent(current->moveLeft());
    }

    void moveRight() {
        setCurrent(current->moveRight());
    }

    void use(char shortkey)
    {
        recursiveSearch(shortkey,&root);
        use();
    }

    void use() {
        //current->use();
        if (cb_menuUse) {
            MenuUseEvent mue = { *current };
            cb_menuUse(mue);
        }
    }

private:
    void setCurrent( MenuItem *next ) {
        if (next) {
            if (cb_menuChange) {
                MenuChangeEvent mce = { *current, *next };
                (*cb_menuChange)(mce);
            }
            current = next;
        }
    }
    void foundShortkeyItem(MenuItem *mi) {
        mi->setBack(current);
        current = mi;
    }
    char canSearch(const char shortkey, MenuItem *m) {
        if (m==0) { return 0; }
        else  {
            if (m->getShortkey()==shortkey) {
                foundShortkeyItem(m);
                return 1;
            }
            return -1;
        }
    }
    void rSAfter(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m->getAfter());
            rSRight(shortkey, m->getRight());
            rSLeft(shortkey, m->getLeft());
        }
    }
    void rSRight(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m->getAfter());
            rSRight(shortkey, m->getRight());
            rSBefore(shortkey, m->getBefore());
        }
    }
    void rSLeft(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m->getAfter());
            rSLeft(shortkey, m->getLeft());
            rSBefore(shortkey, m->getBefore());
        }
    }
    void rSBefore(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSRight(shortkey, m->getRight());
            rSLeft(shortkey, m->getLeft());
            rSBefore(shortkey, m->getBefore());
        }
    }
    void recursiveSearch(const char 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


user27653: You have a global variable menu, which is of type MenuBackend, which contains the structure of your menu system. One of the member functions of MenuBackend is MenuItem getCurrent(), which returns a reference to the currently active menu.
If you look at the class definition of MenuItem, you can see that among the public member functions, you have:
inline MenuItem* getBack() const { return back; }
inline MenuItem* getBefore() const { return before; }
inline MenuItem* getRight() const { return right; }
inline MenuItem* getAfter() const { return after; }
inline MenuItem* getLeft() const { return left; }

Since menu is a global, anywhere in your program, you ought to be able to use:
menu.getCurrent().getAfter();

to access the MenuItem after the current one. Likewise with just menu.getCurrent().

EDIT:
I add the following lines to your showAllNames() function, just to check:
Serial.println(fromFlash(menu.getCurrent().getName()));
Serial.println(fromFlash(menu.getCurrent().getAfter()->getName()));

And it seemed to work fine.

26860: <p>I'm trying to make a navigational menu with sub menus to display on an OLED.  Have managed this OK, however I would like to display the next item on the same level, to encourage scrolling, but I am unable to figure this out.</p>

<p>I want to have it so that the current menu item is displayed, along with the next item one would reach if you scrolled downwards.  I know you can display the previous and current items easily with   </p>

<pre><code>  Serial.print(fromFlash(changed.from.getName())); 
</code></pre>

<p>and </p>

<pre><code>  Serial.println(fromFlash(changed.to.getName()));
</code></pre>

<p>respectively - but I would like to display the current and the next (below but on same level) item.  How can I do this?  I played around for most of today on this but am pretty stumped.</p>

<p>I've simplified the example given which is below.  Below that, I've included <code>menubackend.h</code>  </p>

<p>Running on an ATMEGA328P chip.</p>

<pre><code>#include "MenuBackend.h"

/*
    This is the structure of the modelled menu

    Settings
        Pin
        Debug
    Options
        Delay (D)
            100 ms
            200 ms
            300 ms
            400 ms
 */

#define TEST_FROMFLASH
#undef TEST_GETITEMNAMEFROMFLASH

const char itemNameSettings[] PROGMEM = "Settings";
const char itemNamePin[] PROGMEM      = "Pin";
const char itemNameDebug[] PROGMEM    = "Debug";
const char itemNameOptions[] PROGMEM  = "Options";
const char itemNameDelay[] PROGMEM    = "Delay";
const char itemName100ms[] PROGMEM    = "100 ms";
const char itemName200ms[] PROGMEM    = "200 ms";
const char itemName300ms[] PROGMEM    = "300 ms";
const char itemName400ms[] PROGMEM    = "400 ms";
const char itemNameshutdown[] PROGMEM = "shutdown";


static void menuChangeEvent(MenuChangeEvent changed);
static void menuUseEvent(MenuUseEvent used);
//this controls the menu backend and the event generation
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
//beneath is list of menu items needed to build the menu
MenuItem settings = MenuItem(itemNameSettings);
MenuItem pin = MenuItem(itemNamePin);
MenuItem debug = MenuItem(itemNameDebug);
MenuItem options = MenuItem(itemNameOptions);
MenuItem setDelay = MenuItem(itemNameDelay,'D');
MenuItem d100 = MenuItem(itemName100ms);
MenuItem d200 = MenuItem(itemName200ms);
MenuItem d300 = MenuItem(itemName300ms);
MenuItem d400 = MenuItem(itemName400ms);
MenuItem shutdown1 = MenuItem(itemNameshutdown);

//this function builds the menu and connects the correct items together
void menuSetup()
{
    Serial.println(F("Setting up menu..."));
    //add the file menu to the menu root
    menu.getRoot().add(settings); 
    //setup the settings menu item
    settings.addRight(pin);
    //we want looping both up and down
       pin.addBefore(debug);
       pin.addAfter(debug);
       debug.addAfter(pin);
    //we want a left movement to pint to settings from anywhere
       debug.addLeft(shutdown1);
       pin.addLeft(settings);
    settings.addAfter(options);
    shutdown1.addAfter(settings);
    options.addAfter(shutdown1);
      options.addRight(setDelay);
      setDelay.addLeft(options);
      setDelay.addRight(d100);
      d100.addBefore(d100); //loop to d400 
      d100.addAfter(d200);
      d200.addAfter(d300);
      d300.addAfter(d400);
      d400.addAfter(d100); //loop back to d100
      //we want left to always be bak to delay
      d100.addLeft(setDelay);
      d200.addLeft(setDelay);
      d300.addLeft(setDelay);
      d400.addLeft(setDelay);


}

#ifdef TEST_FROMFLASH

inline const __FlashStringHelper * fromFlash(const char * pStr)
{
    return reinterpret_cast&lt;const __FlashStringHelper *&gt;(pStr);
}

#endif

#ifdef TEST_GETITEMNAMEFROMFLASH

inline char * getItemNameFromFlash(const MenuItem &amp; item, char * copyToBuffer, size_t bufferSize)
{
    strncpy_P(copyToBuffer, item.getName(), bufferSize);
    copyToBuffer[bufferSize - 1] = '\0'; // Make sure the buffer is null-terminated. 

    return copyToBuffer;
}

#endif

/*
    This is an important function
    Here all use events are handled

    This is where you define a behaviour for a menu item
 */
void menuUseEvent(MenuUseEvent used)
{
    Serial.print(F("Menu use "));

#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(used.item.getName()));
#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';
    Serial.println(getItemNameFromFlash(used.item, itemNameBuffer, sizeof(itemNameBuffer)));
#endif

    if (used.item == setDelay) //comparison against a known item
    {
        Serial.println(F("menuUseEvent found Delay (D)"));
    }
}

/*
    This is an important function
    Here we get a notification whenever the user changes the menu
    That is, when the menu is navigated
 */
void menuChangeEvent(MenuChangeEvent changed)
{
    Serial.print("Menu change ");

#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(changed.to.getName()));

#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';
    Serial.print(getItemNameFromFlash(changed.from, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.print(" char is ");
    Serial.println(getItemNameFromFlash(changed.to, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}

void setup()
{
    Serial.begin(9600);

    Serial.println(F("Revision 1.0."));

    menuSetup();
    Serial.println(F("Starting navigation:\r\n   Down: s   Left: a   Right: d   Use: e"));
}

void showAllNames()
{
#ifdef TEST_FROMFLASH
    Serial.println(fromFlash(settings.getName()));
    Serial.println(fromFlash(pin.getName()));
    Serial.println(fromFlash(debug.getName()));
    Serial.println(fromFlash(options.getName()));
    Serial.println(fromFlash(setDelay.getName()));
    Serial.println(fromFlash(d100.getName()));
    Serial.println(fromFlash(d200.getName()));
    Serial.println(fromFlash(d300.getName()));
    Serial.println(fromFlash(d400.getName()));
#endif

#ifdef TEST_GETITEMNAMEFROMFLASH
    char itemNameBuffer[31]; // 30 chars + '\0';

    Serial.println(getItemNameFromFlash(settings, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(pin, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(debug, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(options, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(setDelay, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d100, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d200, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d300, itemNameBuffer, sizeof(itemNameBuffer)));
    Serial.println(getItemNameFromFlash(d400, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}

void loop()
{
    if (Serial.available()) {
        byte read = Serial.read();
        switch (read) {
        case 's': 
            menu.moveDown(); 
            break;
        case 'd': 
            menu.moveRight(); 
            break;
        case 'a': 
            menu.moveLeft(); 
            break;
        case 'e': 
            menu.use(); 
            break;
        case 'z': 
            showAllNames(); 
            break;
        }
    }
}
</code></pre>

<p>MenuBackend.h</p>

<pre><code>/*
||
|| @file    MenuBackend.h
|| @version 1.4
|| @author  Alexander Brevig
|| @contact alexanderbrevig@gmail.com
|| @contribution Adrian Brzezinski adrb@wp.pl, http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?action=viewprofile;username=vzhang
||
|| @description
|| | Provide an easy way of making menus
|| #
||
|| @license
|| | This library is free software; you can redistribute it and/or
|| | modify it under the terms of the GNU Lesser General Public
|| | License as published by the Free Software Foundation; version
|| | 2.1 of the License.
|| |
|| | This library 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
|| | Lesser General Public License for more details.
|| |
|| | You should have received a copy of the GNU Lesser General Public
|| | License along with this library; if not, write to the Free Software
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
|| #
||
*/

#ifndef MenuBackend_h
#define MenuBackend_h

#include &lt;avr/pgmspace.h&gt;

/*
    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:
    MenuItem(const char * itemName, char shortKey='\0' ) : name(itemName), shortkey(shortKey) {
        before = right = after = left = 0;
    }

    //void use(){} //update some internal data / statistics
    inline const char * getName() const { return name; }
    inline const char getShortkey() const { return shortkey; }
    inline const bool hasShortkey() const { return (shortkey!='\0'); }
    inline void setBack(MenuItem *b) { back = b; }
    inline MenuItem* getBack() const { return back; }
    inline MenuItem* getBefore() const { return before; }
    inline MenuItem* getRight() const { return right; }
    inline MenuItem* getAfter() const { return after; }
    inline MenuItem* getLeft() const { return left; }

    MenuItem *moveBack() { return back; }

    MenuItem *moveUp() { 
        if (before) { before-&gt;back = this; }
        return before; 
    }

    MenuItem *moveDown() { 
        if (after) { after-&gt;back = this; }
        return after; 
    }

    MenuItem *moveLeft() { 
        if (left) { left-&gt;back = this; }
        return left; 
    }

    MenuItem *moveRight() { 
        if (right) { right-&gt;back = this; }
        return right; 
    }

    //default vertical menu
    MenuItem &amp;add(MenuItem &amp;mi) { return addAfter(mi); }

    MenuItem &amp;addBefore(MenuItem &amp;mi) {
        mi.after = this;
        before = &amp;mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &amp;addRight(MenuItem &amp;mi) {
        mi.left = this;
        right = &amp;mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &amp;addAfter(MenuItem &amp;mi) {
        mi.before = this;
        after = &amp;mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
    MenuItem &amp;addLeft(MenuItem &amp;mi) {
        mi.right = this;
        left = &amp;mi;
        if ( !mi.back ) mi.back = back;
        return mi;
    }
protected:

    const char * name;
    const char shortkey;

    MenuItem *before;
    MenuItem *right;
    MenuItem *after;
    MenuItem *left;
    MenuItem *back;
};

//no dependant inclusion of string or cstring
bool menuTestStrings1(const char *a, const char *b) { // a is in flash, b is in SRAM.
    for (uint8_t da = pgm_read_byte_near(a); da; da = pgm_read_byte_near(++a), b++) 
    { 
        if (da != *b) 
            return false; 
    }
    return (*b == '\0'); // We reached the end of a.  Strings are equal only if we reached the end of b too.
}
bool menuTestStrings2(const char *a, const char *b) { // a and b are both in flash.
    uint8_t da = pgm_read_byte_near(a); // Dereferenced a.
    uint8_t db = pgm_read_byte_near(b); // Dereferenced b.

    for ( ; da; da = pgm_read_byte_near(++a), db = pgm_read_byte_near(++b)) 
    { 
        if (da != db)
            return false; 
    }

    return (db == '\0'); // We reached the end of a.  Strings are equal only if we reached the end of b too.
}
bool operator==(MenuItem &amp;lhs, char* test) {
    return menuTestStrings1(lhs.getName(),test);
}
bool operator==(const MenuItem &amp;lhs, char* test) {
    return menuTestStrings1(lhs.getName(),test);
}
bool operator==(MenuItem &amp;lhs, MenuItem &amp;rhs) {
    return menuTestStrings2(lhs.getName(),rhs.getName());
}
bool operator==(const MenuItem &amp;lhs, MenuItem &amp;rhs) {
    return menuTestStrings2(lhs.getName(),rhs.getName());
}

struct MenuChangeEvent {
    const MenuItem &amp;from;
    const MenuItem &amp;to;
};

struct MenuUseEvent {
    const MenuItem &amp;item;
};

typedef void (*cb_change)(MenuChangeEvent);
typedef void (*cb_use)(MenuUseEvent);

const char itemNameMenuRoot[] PROGMEM = "MenuRoot";

class MenuBackend {
public:

    MenuBackend(cb_use menuUse, cb_change menuChange = 0) : root(itemNameMenuRoot) {
        current = &amp;root;
        cb_menuChange = menuChange;
        cb_menuUse = menuUse;
    }

    MenuItem &amp;getRoot() {
        return root;
    }
    MenuItem &amp;getCurrent() {
        return *current;
    }

    void moveBack() {
        setCurrent(current-&gt;getBack());
    }

    void moveUp() {
        setCurrent(current-&gt;moveUp());
    }

    void moveDown() {
        setCurrent(current-&gt;moveDown());
    }

    void moveLeft() {
        setCurrent(current-&gt;moveLeft());
    }

    void moveRight() {
        setCurrent(current-&gt;moveRight());
    }

    void use(char shortkey)
    {
        recursiveSearch(shortkey,&amp;root);
        use();
    }

    void use() {
        //current-&gt;use();
        if (cb_menuUse) {
            MenuUseEvent mue = { *current };
            cb_menuUse(mue);
        }
    }

private:
    void setCurrent( MenuItem *next ) {
        if (next) {
            if (cb_menuChange) {
                MenuChangeEvent mce = { *current, *next };
                (*cb_menuChange)(mce);
            }
            current = next;
        }
    }
    void foundShortkeyItem(MenuItem *mi) {
        mi-&gt;setBack(current);
        current = mi;
    }
    char canSearch(const char shortkey, MenuItem *m) {
        if (m==0) { return 0; }
        else  {
            if (m-&gt;getShortkey()==shortkey) {
                foundShortkeyItem(m);
                return 1;
            }
            return -1;
        }
    }
    void rSAfter(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m-&gt;getAfter());
            rSRight(shortkey, m-&gt;getRight());
            rSLeft(shortkey, m-&gt;getLeft());
        }
    }
    void rSRight(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m-&gt;getAfter());
            rSRight(shortkey, m-&gt;getRight());
            rSBefore(shortkey, m-&gt;getBefore());
        }
    }
    void rSLeft(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m-&gt;getAfter());
            rSLeft(shortkey, m-&gt;getLeft());
            rSBefore(shortkey, m-&gt;getBefore());
        }
    }
    void rSBefore(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSRight(shortkey, m-&gt;getRight());
            rSLeft(shortkey, m-&gt;getLeft());
            rSBefore(shortkey, m-&gt;getBefore());
        }
    }
    void recursiveSearch(const char shortkey, MenuItem *m) {
        if (canSearch(shortkey,m)!=1) {
            rSAfter(shortkey, m-&gt;getAfter());
            rSRight(shortkey, m-&gt;getRight());
            rSLeft(shortkey, m-&gt;getLeft());
            rSBefore(shortkey, m-&gt;getBefore());
        }
    }

    MenuItem root;
    MenuItem *current;

    cb_change cb_menuChange;
    cb_use cb_menuUse;
};

#endif
</code></pre>
 27653: <p>You have a global variable <code>menu</code>, which is of type <code>MenuBackend</code>, which contains the structure of your menu system. One of the member functions of <code>MenuBackend</code> is <code>MenuItem getCurrent()</code>, which returns a reference to the currently active menu.</p>

<p>If you look at the class definition of <code>MenuItem</code>, you can see that among the public member functions, you have:</p>

<pre><code>inline MenuItem* getBack() const { return back; }
inline MenuItem* getBefore() const { return before; }
inline MenuItem* getRight() const { return right; }
inline MenuItem* getAfter() const { return after; }
inline MenuItem* getLeft() const { return left; }
</code></pre>

<p>Since <code>menu</code> is a global, anywhere in your program, you ought to be able to use:</p>

<pre><code>menu.getCurrent().getAfter();
</code></pre>

<p>to access the <code>MenuItem</code> after the current one. Likewise with just <code>menu.getCurrent()</code>.</p>

<hr>

<p><strong>EDIT:</strong></p>

<p>I add the following lines to your <code>showAllNames()</code> function, just to check:</p>

<pre><code>Serial.println(fromFlash(menu.getCurrent().getName()));
Serial.println(fromFlash(menu.getCurrent().getAfter()-&gt;getName()));
</code></pre>

<p>And it seemed to work fine.</p>
