4 errors
Dependencies: KS0108_PCF8574 mbed
Diff: menbed/menbedNavigator.cpp
- Revision:
- 0:936f1c020120
diff -r 000000000000 -r 936f1c020120 menbed/menbedNavigator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/menbed/menbedNavigator.cpp Wed Sep 05 07:21:59 2012 +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; +}