this locks like shit

Dependencies:   MenuLCD mbed

Fork of MenuLCD_copy by Vinícius Alves

menbed/menbedNavigator.cpp

Committer:
ViniR
Date:
2017-05-19
Revision:
0:92357d1220f3

File content as of revision 0:92357d1220f3:

#include "mbed.h"
#include "include/menbedNavigator.h"
#include "include/menbedButtonEvent.h"
#include "include/menbedMenuMessage.h"
#include "include/menbedMenu.h"
#include <cstdio>

extern void toggleLed3();

MenbedNavigator::MenbedNavigator(MenbedMenu *rootMenu,  
        MenbedDisplayer *displayer) :
    activeMenu(rootMenu), displayer(displayer)
{
    selectedItemIndex = -1;
    topOfScreenItemIndex = 0;
    paramEditMode = false;
    
    numLines = displayer->getDisplay()->getLines();
    lineLength = displayer->getDisplay()->getLineLength();
}

void MenbedNavigator::updateDisplay()
{
    MenbedMenuMessage menuMsg (numLines, lineLength);

    printMenu (menuMsg.text);
    menuMsg.showUpArrow = (topOfScreenItemIndex >= 1);
    menuMsg.showDownArrow = (topOfScreenItemIndex + numLines < (int)(activeMenu->menuItems.size()));
    
    displayer->update (&menuMsg);
}


void MenbedNavigator::handleButtonEvent (MenbedButtonEvent buttonEvent)
{
    numButtons = buttonEvent.numButtons;

    switch (buttonEvent.name)
    {
    case MenbedButtonEvent::ButtonSelect: // Select
        if (!paramEditMode && (buttonEvent.action == 
                MenbedButtonEvent::BUTTON_ACTION_RELEASED_SHORT))
            selectItem();
        else if (paramEditMode && (buttonEvent.action ==
                MenbedButtonEvent::BUTTON_ACTION_RELEASED_SHORT))
            saveParam();
        else if ((numButtons < 4) && 
            paramEditMode && (buttonEvent.action == 
                MenbedButtonEvent::BUTTON_ACTION_HELD_LONG))
            restoreParam();
        else if ((numButtons < 4) && !paramEditMode && 
            (buttonEvent.action ==
                MenbedButtonEvent::BUTTON_ACTION_HELD_LONG))
            gotoParent();
        break;
        
    case MenbedButtonEvent::ButtonDown:
        if (paramEditMode && 
                (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
            decParam();
        else if (!paramEditMode && 
                (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
            moveDown();
        break;
        
    case MenbedButtonEvent::ButtonUp:
        if (numButtons > 2)
        {
            if (paramEditMode && 
                    (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
                incParam();
            else if (!paramEditMode &&
                    (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
                moveUp();
        }
        break;
        
    case MenbedButtonEvent::ButtonCancel:
        if (numButtons > 3)
        {
            if (paramEditMode && 
                    (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
                restoreParam();
            else if (!paramEditMode && 
                    (buttonEvent.action == MenbedButtonEvent::BUTTON_ACTION_PUSHED))
                gotoParent();
        }
        break;
    }

    updateDisplay();
    //menuRefresh_refreshed();
}


void MenbedNavigator::selectItem()
{
    MenbedMenu **childMenuPtr;
    MenbedMenu *childMenu;

    if ((selectedItemIndex < 0) || 
            (selectedItemIndex >= (int)(activeMenu->menuItems.size())))
         return;

    // If it exists, execute the function associated with the menu item
    if (activeMenu->menuItems[selectedItemIndex]->selFcn != NULL)
        activeMenu->menuItems[selectedItemIndex]->selFcn();

    // Show the child menu associated with the menu item.  Initially, the first
    // item in the child menu is placed at the top of the screen, but is it
    // left unselected.
    childMenuPtr = activeMenu->menuItems[selectedItemIndex]->childMenu;
    if (childMenuPtr != NULL)
    {
        childMenu = *(activeMenu->menuItems[selectedItemIndex]->childMenu);
    
        if (!activeMenu->menuItems[selectedItemIndex]->childMenuIsAncestor)
        {
            childMenu->parentMenu = activeMenu;
            childMenu->parentSelectedItemIndex = selectedItemIndex;
            childMenu->parentTopOfScreenItemIndex = topOfScreenItemIndex;
        }
        else
            childMenu->parentMenu = NULL;

        activeMenu = childMenu;
        topOfScreenItemIndex = 0;
        selectedItemIndex = -1;       
    }
    // Otherwise, if the current menu item has a parameter that can be modified,
    // we switch to the parameter editing mode.
    else if ((activeMenu->menuItems[selectedItemIndex]->param != NULL) &&
            (activeMenu->menuItems[selectedItemIndex]->param->inc() != 0))
    {
        // All incrementing and decrementing of the parameter actually happens
        // to a shadow variable in the param structure named tempValue.  The
        // parameter value used by the other parts of the system is not updated
        // until the user is done editing the parameter.
        activeMenu->menuItems[selectedItemIndex]->param->initVal =
                activeMenu->menuItems[selectedItemIndex]->param->getVal();
        activeMenu->menuItems[selectedItemIndex]->param->tempVal =
                activeMenu->menuItems[selectedItemIndex]->param->initVal;
        paramEditMode = true;
    }
}


void MenbedNavigator::gotoParent()
{
    if (activeMenu->parentMenu == NULL)
        return;

    selectedItemIndex = activeMenu->parentSelectedItemIndex;
    topOfScreenItemIndex = activeMenu->parentTopOfScreenItemIndex;
    activeMenu = activeMenu->parentMenu;
}


void MenbedNavigator::moveUp()
{
    // If we're already at the top of the menu, do nothing
    if (selectedItemIndex <= -1)
        return;
    // If the top item of the menu is already selected, we send a NOP message
    // which deselects the top line and displays the down arrow if the menu
    // contains more items than can fit on the screen.  In effect, this allows
    // the user to deselect all menu items which adds a nice look to the system.
    else if (selectedItemIndex == 0)
        selectedItemIndex = -1;
    // If the currently selected menu item is the also the one at the top of the
    // screen, we need to scroll the screen down and add the item above the
    // currently selected one to the top of the screen.
    else if (selectedItemIndex == topOfScreenItemIndex)
    {
        selectedItemIndex--;
        topOfScreenItemIndex--;
    }
    // The selected item is not the top item on the screen.  All we need to do
    // is select the item above the currently selected item.
    else
        selectedItemIndex--;
}



void MenbedNavigator::moveDown()
{
    // If the last item of the menu is already selected, our behavior depends
    // on how many buttons are present.  If there is no up button, we cycle
    // back to the top of the menu.  Otherwise, if an up button is present,
    // we do nothing.
    if (selectedItemIndex >= ((int)(activeMenu->menuItems.size()) - 1))
    {
        if (numButtons < 3)
        {
            selectedItemIndex = -1;
            topOfScreenItemIndex = 0;
        }
        else
            ;
    }
    // If the menu item displayed at the bottom of the screen is already
    // selected, we will need to scroll the screen up to make room for a new
    // line at the bottom of the screen.
    else if (selectedItemIndex ==
            (topOfScreenItemIndex + numLines - 1))
    {
        selectedItemIndex++;
        topOfScreenItemIndex++;
    }
    // Otherwise, if the currently selected menu item is now the one displayed
    // at the bottom of the screen, we simply change which of the visible items
    // is highlighted.
    else
        selectedItemIndex++;
        
}


void MenbedNavigator::incParam()
{
    float inc;
    float tempVal;

    if (paramEditMode != true)
        return;

    inc = activeMenu->menuItems[selectedItemIndex]->param->inc();

    // Initialize our own local copy of the parameter's temporary value.  We do
    // this so that we can more easily check for violations of the allowed min
    // and max values.
    tempVal = activeMenu->menuItems[selectedItemIndex]->param->tempVal;
    tempVal += inc;

    // Check the bounds on the parameter.
    if (tempVal > activeMenu->menuItems[selectedItemIndex]->param->max())
        tempVal = activeMenu->menuItems[selectedItemIndex]->param->max();
    else if (tempVal < activeMenu->menuItems[selectedItemIndex]->param->min())
        tempVal = activeMenu->menuItems[selectedItemIndex]->param->min();

    // Assign the local temp. value back to the temporary value in the active
    // parameter structue.
    activeMenu->menuItems[selectedItemIndex]->param->tempVal = tempVal;

    // If the parameter is configured to produce live updates, call the
    // finalValFcn.
    if (activeMenu->menuItems[selectedItemIndex]->param->liveUpdate())
        activeMenu->menuItems[selectedItemIndex]->param->setVal(tempVal);
}


void MenbedNavigator::decParam()
{
    float inc;
    float tempVal;

    if (paramEditMode != true)
        return;

    inc = activeMenu->menuItems[selectedItemIndex]->param->inc();

    // Initialize our own local copy of the parameter's temporary value.  We do
    // this so that we can more easily check for violations of the allowed min
    // and max values.
    tempVal = activeMenu->menuItems[selectedItemIndex]->param->tempVal;
    tempVal -= inc;

    // Check the bounds on the parameter.
    if (tempVal > activeMenu->menuItems[selectedItemIndex]->param->max())
        tempVal = activeMenu->menuItems[selectedItemIndex]->param->max();
    // If we reach the minimum parameter value when we only have a down button
    // and not an up button connected to the system, we wrap the parameter
    // value back around to its maximum.  Otherwise, if there is an up button
    // present, we peg the parameter at its minimum value.
    else if (tempVal < activeMenu->menuItems[selectedItemIndex]->param->min())
    {
        if (numButtons >= 3)
            tempVal = activeMenu->menuItems[selectedItemIndex]->param->min();
        else
            tempVal = activeMenu->menuItems[selectedItemIndex]->param->max();
    }

    // Assign the local temp. value back to the temporary value in the active
    // parameter structue.
    activeMenu->menuItems[selectedItemIndex]->param->tempVal = tempVal;

    // If the parameter is configured to produce live updates, call the
    // finalValFcn.
    if (activeMenu->menuItems[selectedItemIndex]->param->liveUpdate())
        activeMenu->menuItems[selectedItemIndex]->param->setVal(tempVal);
}


void MenbedNavigator::saveParam()
{
    // Save the changes made the shadow variable tempValue to the real parameter
    // value that is used by the rest of the application.
    activeMenu->menuItems[selectedItemIndex]->param->setVal (
            activeMenu->menuItems[selectedItemIndex]->param->tempVal
            );
    paramEditMode = false;
}


void MenbedNavigator::restoreParam()
{
    // Revert any changes made the parameter by calling the finalValFcn with
    // the initVal that was stored when we first began editing this parameter.
    activeMenu->menuItems[selectedItemIndex]->param->setVal(
            activeMenu->menuItems[selectedItemIndex]->param->initVal
            );
    paramEditMode = false;
}


void MenbedNavigator::printMenu (char *menuStr)
{
    uint8_t i;
    char *lineStr = new char[lineLength];
    bool itemSel;

    menuStr[0] = '\0';

    for (i=topOfScreenItemIndex; i<topOfScreenItemIndex + numLines; i++)
    {
        // Make sure we don't try to print more menu items than exist in the
        // active menu.
        if (i > ((int)activeMenu->menuItems.size() - 1))
        {
            strcat (menuStr, "\n");
            continue;
        }

        itemSel = (i == selectedItemIndex);

        printItem (activeMenu->menuItems[i], lineStr, itemSel,
                paramEditMode && itemSel);

        strncat (menuStr, lineStr, lineLength);
        strcat (menuStr, "\n");
    }
    
    delete[] lineStr;
}


void MenbedNavigator::printItem (MenbedMenuItem *item, char *line, bool itemSel,
        bool paramSel)
{
    uint8_t i = 0;
    int8_t j;
    char *tempStr = new char[lineLength];
    char *frontTab, *backTab;
    char *subStr = new char[lineLength];
    uint8_t copySize;

    // Clear the line of text
    line[0] = '\0';

    // Iterate over the element in the array of text strings in the provided
    // menu item until an empty string is found indicating the end of the text
    // that should be printed for the current line.  For safety, we assume there
    // are a maximum of three element in the array: 1) text before the
    // parameter, 2) the parameter, and 3) text after the parameter.
    
    frontTab = item->text;
    while ((strlen (frontTab) > 0) && (i < 3))
    {
        backTab = strchr (frontTab, '\t');
        if (backTab == NULL)
        {
            backTab = frontTab + strlen(frontTab);
            i = 3; // force our way out of the while loop
        }
         
        copySize = backTab - frontTab;
        if (copySize >= lineLength)
            copySize = lineLength - 1;
            
        strncpy (subStr, frontTab, copySize);
        subStr[copySize] = '\0';
        
        // If the current string in the array is a printf-style conversion
        // specifier for a float, we replace it with the parameter value.
        if (checkConvSpec (subStr))
        {
            // If the user is currently editing the parameter, print the value
            // of the shadow variable tempValue instead of the parameters actual
            // value.  The tempValue is not copied over to the value field of
            // the structure until the user is done editing the parameter.  To
            // show that the parameter is being edited, we highlight the value
            // by inverting the text.
            if (paramSel)
            {
                snprintf (tempStr, lineLength, subStr, item->param->tempVal);

                // We highlight the parameter by inverting the characters on the
                // screen.  The menu system only allows the standard (0-127)
                // ASCII character, so we use the MSB/7th bit of each character
                // to indicate that it should be inverted when printed on the
                // screen.
                for (j=strlen(tempStr) - 1; j>=0; j--)
                    tempStr[j] |= 0x80;

            }
            // If the user is not currently editing the parameter, we display
            // the value pointed to by the value field of the param structure.
            else
                snprintf (tempStr, lineLength,
                        subStr, item->param->getVal());                       

            // Attach the parameter string to the growing line.
            strncat (line, tempStr, lineLength);
        }
        // If the string is not a printf-style conversion specifier for a float,
        // simply catenate the string with the growing line of text.
        else
        {
            strncat (line, subStr, lineLength);
        }
           
        frontTab = backTab + 1;
        i++;
    }

    // Append a space to the very end of the line.  The LCD driver looks to the
    // last character in the line to determine whether to highlight any
    // remaining whitespace after the text ends.  This approach causes problems
    // when the parameter is the last text on the line and we are in parameter
    // modification mode.  Without the extra space at the end of the line, the
    // LCD controller will highlight the rest of the line even though it is only
    // the parameter itself that should be highlighted.
    strncat (line, " ", lineLength);

    // If the parameter has not been selected for modification but the menu item
    // is currently selected, we highlight the entire line.  In the menu system,
    // the only allowable character are the standard ASCII codes (0-127).  We
    // use the (MSB) 7th bit of every character to indicate whether it should be
    // highlighted/inverted.
    if (!paramSel && itemSel)
    {
        // Set the MSB of each character to invert it when displayed.
        for (j = strlen(line) - 1; j>= 0; j--)
            line[j] |= 0x80;
    }
    
    delete[] tempStr;
    delete[] subStr;
}


// Returns true if the provided string is a printf-style conversion specifier
// for a float (conversion character is f, e, E, g, or G).  Otherwise, returns
// false.
bool MenbedNavigator::checkConvSpec (const char *s)
{
    char lastChar;

    // Conversion specifications must begin with a '%'.
    if (s[0] != '%')
        return false;

    // Identify the last last character in the conversion specification
    lastChar = s[strlen(s) - 1];

    // Check that the last character in the conversion specification is either a
    // 'f', 'e', 'E', 'g', or 'G'--the conversion specifiers for floats.  If it
    // is, the conversion specification is probably a valid conversion
    // specification.
    if ((lastChar == 'f') || (lastChar == 'e') || (lastChar == 'E') ||
            (lastChar == 'g') || (lastChar == 'G'))
        return true;

    // Otherwise, it is not.
    return false;
}