Então PARA...

Dependencies:   MenuLCD mbed

Revision:
0:92357d1220f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/menbed/menbedNavigator.cpp	Fri May 19 13:07:52 2017 +0000
@@ -0,0 +1,477 @@
+#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;
+}