/* * Copyright (C) OpenTX * * Based on code named * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program 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 General Public License for more details. */ #include "opentx.h" uint8_t THEME_ICONS[] = { ICON_THEME, ICON_THEME_SETUP, ICON_THEME_VIEW1, ICON_THEME_VIEW2, ICON_THEME_VIEW3, ICON_THEME_VIEW4, ICON_THEME_VIEW5, ICON_THEME_ADD_VIEW, }; Layout * currentScreen; WidgetsContainerInterface * currentContainer; Widget * currentWidget; uint8_t currentZone; bool widgetNeedsSettings; #define SCREENS_SETUP_2ND_COLUMN 200 char fileSelection[LEN_ZONE_OPTION_STRING]; uint8_t fileSelectionDone; int updateMainviewsMenu(); bool menuScreenAdd(event_t event); void onScreenSetupMenu(const char * result); void onZoneOptionFileSelectionMenu(const char * result) { if (result == STR_UPDATE_LIST) { if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, LEN_ZONE_OPTION_STRING, NULL)) { POPUP_WARNING(STR_NO_BITMAPS_ON_SD); } } else { fileSelectionDone = true; memcpy(fileSelection, result, sizeof(fileSelection)); } } uint8_t getZoneOptionColumns(const ZoneOption * option) { if (option->type == ZoneOption::Color) { return uint8_t(2 | NAVIGATION_LINE_BY_LINE); } else { return 0; } } uint8_t editColorPart(coord_t x, coord_t y, event_t event, uint8_t part, uint8_t value, LcdFlags attr, uint32_t i_flags) { const char * STR_COLOR_PARTS = "\002" "R:" "G:" "B:"; uint8_t PART_BITS[] = { 5, 6, 5 }; lcdDrawTextAtIndex(x, y, STR_COLOR_PARTS, part, (attr && menuHorizontalPosition < 0) ? TEXT_INVERTED_COLOR : TEXT_COLOR); lcdDrawNumber(x + 20, y, value << (8-PART_BITS[part]), LEFT|TEXT_COLOR|((attr && (menuHorizontalPosition < 0 || menuHorizontalPosition == part)) ? attr : TEXT_COLOR)); if (attr && menuHorizontalPosition == part) { value = checkIncDec(event, value, 0, (1 << PART_BITS[part])-1, i_flags); } return value; } bool editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue * value, LcdFlags attr, uint32_t i_flags, event_t event) { lcdDrawText(MENUS_MARGIN_LEFT, y, option->name); if (option->type == ZoneOption::Bool) { value->boolValue = editCheckBox(value->boolValue, SCREENS_SETUP_2ND_COLUMN, y, attr, event); // TODO always does storageDirty(EE_MODEL) } else if (option->type == ZoneOption::Integer) { lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN, y, value->signedValue, attr | LEFT); if (attr) { CHECK_INCDEC_MODELVAR(event, value->signedValue, option->min.signedValue, option->max.signedValue); // TODO i_flags } } else if (option->type == ZoneOption::String) { editName(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), event, attr); // TODO i_flags? } else if (option->type == ZoneOption::File) { if (ZEXIST(value->stringValue)) lcdDrawSizedText(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), attr); else lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); // TODO define if (attr) { if (event==EVT_KEY_FIRST(KEY_ENTER)) { s_editMode = 0; if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(value->stringValue), value->stringValue, LIST_NONE_SD_FILE)) { fileSelectionDone = false; POPUP_MENU_START(onZoneOptionFileSelectionMenu); } else { POPUP_WARNING(STR_NO_BITMAPS_ON_SD); } } else if (fileSelectionDone) { memcpy(value->stringValue, fileSelection, sizeof(fileSelection)); fileSelectionDone = false; storageDirty(i_flags); } } } else if (option->type == ZoneOption::TextSize) { lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, "\010StandardTiny\0 Small\0 Mid\0 Double", value->unsignedValue, attr); if (attr) { value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, 4, i_flags); } } else if (option->type == ZoneOption::Timer) { drawStringWithIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TIMER, value->unsignedValue + 1, attr); if (attr) { value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, MAX_TIMERS - 1, i_flags); } } else if (option->type == ZoneOption::Source) { drawSource(SCREENS_SETUP_2ND_COLUMN, y, value->unsignedValue, attr); if (attr) { CHECK_INCDEC_MODELSOURCE(event, value->unsignedValue, 1, MIXSRC_LAST_TELEM); } } else if (option->type == ZoneOption::Color) { RGB_SPLIT(value->unsignedValue, r, g, b); if (attr && menuHorizontalPosition < 0) { lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-3, y-1, 230, FH+1, TEXT_INVERTED_BGCOLOR); } lcdSetColor(value->unsignedValue); lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-1, y+1, 42, 17, TEXT_COLOR); lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN, y+2, 40, 15, CUSTOM_COLOR); r = editColorPart(SCREENS_SETUP_2ND_COLUMN + 50, y, event, 0, r, attr, i_flags); g = editColorPart(SCREENS_SETUP_2ND_COLUMN + 110, y, event, 1, g, attr, i_flags); b = editColorPart(SCREENS_SETUP_2ND_COLUMN + 170, y, event, 2, b, attr, i_flags); if (attr && checkIncDec_Ret) { value->unsignedValue = RGB_JOIN(r, g, b); } } return (attr && checkIncDec_Ret); } int getOptionsCount(const ZoneOption * options) { if (options == NULL) { return 0; } else { int count = 0; for (const ZoneOption * option = options; option->name; option++) { count++; } return count; } } template bool menuSettings(const char * title, T * object, uint32_t i_flags, event_t event) { if (object->getErrorMessage()) { // display error instead of widget settings // TODO nicer display (proper word-wrap) SIMPLE_SUBMENU("Widget Error", ICON_MODEL_LUA_SCRIPTS, 1); int len = strlen(object->getErrorMessage()); int y = 3*FH; const char * p = object->getErrorMessage(); while (len > 0) { lcdDrawSizedText(MENUS_MARGIN_LEFT, y, p, 30); p += 30; y += FH; len -= 30; } return true; } const ZoneOption * options = object->getOptions(); linesCount = getOptionsCount(options); uint8_t mstate_tab[MAX_WIDGET_OPTIONS]; for (int i=0; i0) ? BLINK|INVERS : INVERS); LcdFlags attr = (menuVerticalPosition == k ? blink : 0); if (k < linesCount) { const ZoneOption * option = &options[k]; ZoneOptionValue * value = object->getOptionValue(k); if (editZoneOption(y, option, value, attr, i_flags, event)) { object->update(); } } } return true; } bool menuWidgetSettings(event_t event) { return menuSettings(STR_WIDGET_SETTINGS, currentWidget, EE_MODEL, event); } bool menuWidgetChoice(event_t event) { static Widget * previousWidget = NULL; static Widget * currentWidget = NULL; static std::list::const_iterator iterator; static Widget::PersistentData tempData; switch (event) { case EVT_ENTRY: { previousWidget = currentContainer->getWidget(currentZone); currentContainer->setWidget(currentZone, NULL); iterator = getRegisteredWidgets().cbegin(); if (previousWidget) { const WidgetFactory * factory = previousWidget->getFactory(); std::list::const_iterator it = getRegisteredWidgets().cbegin(); for (; it != getRegisteredWidgets().cend(); ++it) { if (factory->getName() == (*it)->getName()) { iterator = it; break; } } } if (iterator != getRegisteredWidgets().cend()) currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); break; } case EVT_KEY_FIRST(KEY_EXIT): if (previousWidget) { if (currentWidget) delete currentWidget; currentContainer->setWidget(currentZone, previousWidget); } popMenu(); return false; case EVT_KEY_FIRST(KEY_ENTER): if (iterator != getRegisteredWidgets().cend()) { if (previousWidget) delete previousWidget; currentContainer->createWidget(currentZone, *iterator); widgetNeedsSettings = currentContainer->getWidget(currentZone)->getFactory()->getOptions(); storageDirty(EE_MODEL); } popMenu(); return false; case EVT_ROTARY_RIGHT: if (iterator != getRegisteredWidgets().cend() && iterator != --getRegisteredWidgets().cend()) { ++iterator; if (currentWidget) delete currentWidget; currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); } break; case EVT_ROTARY_LEFT: if (iterator != getRegisteredWidgets().cbegin()) { --iterator; if (currentWidget) delete currentWidget; currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); } break; } currentScreen->refresh(); Zone zone = currentContainer->getZone(currentZone); lcdDrawFilledRect(0, 0, zone.x-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24)); lcdDrawFilledRect(zone.x+zone.w+2, 0, LCD_W-zone.x-zone.w-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24)); lcdDrawFilledRect(zone.x-2, 0, zone.w+4, zone.y-2, SOLID, OVERLAY_COLOR | (8<<24)); lcdDrawFilledRect(zone.x-2, zone.y+zone.h+2, zone.w+4, LCD_H-zone.y-zone.h-2, SOLID, OVERLAY_COLOR | (8<<24)); if (currentWidget) currentWidget->refresh(); if (iterator != getRegisteredWidgets().cbegin()) { lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, TEXT_INVERTED_BGCOLOR); lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_LEFT, TEXT_INVERTED_COLOR); } if (iterator != --getRegisteredWidgets().cend()) { lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, TEXT_INVERTED_BGCOLOR); lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_RIGHT, TEXT_INVERTED_COLOR); } if (currentWidget) lcdDrawText(zone.x + zone.w, zone.y-1, currentWidget->getFactory()->getName(), RIGHT | TEXT_COLOR | SMLSIZE | INVERS); return true; } void onZoneMenu(const char * result) { if (result == STR_SELECT_WIDGET) { pushMenu(menuWidgetChoice); } else if (result == STR_WIDGET_SETTINGS) { pushMenu(menuWidgetSettings); } else if (result == STR_REMOVE_WIDGET) { currentContainer->createWidget(currentZone, NULL); storageDirty(EE_MODEL); } } bool menuWidgetsSetup(event_t event) { switch (event) { case EVT_ENTRY: menuVerticalPosition = 0; break; case EVT_KEY_FIRST(KEY_EXIT): killEvents(KEY_EXIT); popMenu(); return false; } currentScreen->refresh(); for (int i=currentContainer->getZonesCount()-1; i>=0; i--) { Zone zone = currentContainer->getZone(i); LcdFlags color; int padding, thickness; if (currentContainer == topbar) { color = MENU_TITLE_COLOR; padding = 2; thickness = 1; } else { color = TEXT_INVERTED_BGCOLOR; padding = 4; thickness = 2; } if (menuVerticalPosition == i) { lcdDrawSolidRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, color); if (event == EVT_KEY_FIRST(KEY_ENTER)) { killEvents(KEY_ENTER); currentZone = menuVerticalPosition; currentWidget = currentContainer->getWidget(menuVerticalPosition); if (currentWidget) { POPUP_MENU_ADD_ITEM(STR_SELECT_WIDGET); if (currentWidget->getFactory()->getOptions()) POPUP_MENU_ADD_ITEM(STR_WIDGET_SETTINGS); POPUP_MENU_ADD_ITEM(STR_REMOVE_WIDGET); POPUP_MENU_START(onZoneMenu); } else { onZoneMenu(STR_SELECT_WIDGET); } } } else { if (widgetNeedsSettings) { currentWidget = currentContainer->getWidget(menuVerticalPosition); if (currentWidget) { widgetNeedsSettings = false; onZoneMenu(STR_WIDGET_SETTINGS); } } lcdDrawRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, 0x3F, color); } } navigate(event, currentContainer->getZonesCount(), currentContainer->getZonesCount(), 1); return true; } template T * editThemeChoice(coord_t x, coord_t y, std::list & elList, T * current, bool needsOffsetCheck, LcdFlags attr, event_t event) { static uint8_t menuHorizontalOffset = 0; uint8_t elCount = elList.size(), last, idx; coord_t pos; int currentIndex = 0; typename std::list::const_iterator elItr = elList.cbegin(); if (!elCount) return NULL; for (; elItr != elList.cend(); ++elItr, ++currentIndex) { if ((*elItr) == current) break; } if (event == EVT_ENTRY) { menuHorizontalOffset = 0; needsOffsetCheck = true; } if (needsOffsetCheck) { if (currentIndex < menuHorizontalOffset) { menuHorizontalOffset = currentIndex; } else if (currentIndex > menuHorizontalOffset + 3) { menuHorizontalOffset = currentIndex - 3; } } if (attr) { if (menuHorizontalPosition < 0) { lcdDrawSolidFilledRect(x-3, y-1, min(4, elCount)*56+1, 2*FH-5, TEXT_INVERTED_BGCOLOR); } else { if (needsOffsetCheck) { menuHorizontalPosition = currentIndex; } else if (menuHorizontalPosition < menuHorizontalOffset) { menuHorizontalOffset = menuHorizontalPosition; } else if (menuHorizontalPosition > menuHorizontalOffset + 3) { menuHorizontalOffset = menuHorizontalPosition - 3; } } } last = min(menuHorizontalOffset + 4, elCount); idx = menuHorizontalOffset; pos = x; elItr = elList.cbegin(); std::advance(elItr, min(menuHorizontalOffset, elCount - 1)); for (; idx < last && elItr != elList.cend(); ++idx, ++elItr, pos += 56) { (*elItr)->drawThumb(pos, y+1, current == (*elItr) ? ((attr && menuHorizontalPosition < 0) ? TEXT_INVERTED_COLOR : TEXT_INVERTED_BGCOLOR) : LINE_COLOR); } if (elCount > 4) { lcdDrawBitmapPattern(x - 12, y+1, LBM_CARROUSSEL_LEFT, menuHorizontalOffset > 0 ? LINE_COLOR : CURVE_AXIS_COLOR); lcdDrawBitmapPattern(x + 4 * 56, y+1, LBM_CARROUSSEL_RIGHT, last < getRegisteredLayouts().size() ? LINE_COLOR : CURVE_AXIS_COLOR); } if (attr && menuHorizontalPosition >= 0) { lcdDrawSolidRect(x + (menuHorizontalPosition - menuHorizontalOffset) * 56 - 3, y - 1, 57, 35, 1, TEXT_INVERTED_BGCOLOR); if (event == EVT_KEY_FIRST(KEY_ENTER)) { s_editMode = 0; elItr = elList.cbegin(); if (menuHorizontalPosition < (int)elList.size()) std::advance(elItr, menuHorizontalPosition); if (elItr != elList.cend()) return (*elItr); else return NULL; } } return NULL; } enum menuScreensThemeItems { ITEM_SCREEN_SETUP_THEME, ITEM_SCREEN_SETUP_THEME_OPTION1 = ITEM_SCREEN_SETUP_THEME+2 }; bool menuScreensTheme(event_t event) { bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); const ZoneOption * options = theme->getOptions(); int optionsCount = getOptionsCount(options); linesCount = ITEM_SCREEN_SETUP_THEME_OPTION1 + optionsCount + 1; menuPageCount = updateMainviewsMenu(); uint8_t mstate_tab[2 + MAX_THEME_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredThemes().size()-1)), ORPHAN_ROW }; for (int i=0; i 0) ? BLINK | INVERS : INVERS); LcdFlags attr = (menuVerticalPosition == k ? blink : 0); switch (k) { case ITEM_SCREEN_SETUP_THEME: { lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_THEME); Theme * new_theme = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredThemes(), theme, needsOffsetCheck, attr, event); if (new_theme) { new_theme->init(); loadTheme(new_theme); strncpy(g_eeGeneral.themeName, new_theme->getName(), sizeof(g_eeGeneral.themeName)); killEvents(KEY_ENTER); storageDirty(EE_GENERAL); } break; } case ITEM_SCREEN_SETUP_THEME+1: break; default: { uint8_t index = k - ITEM_SCREEN_SETUP_THEME_OPTION1; if (index < optionsCount) { const ZoneOption * option = &options[index]; ZoneOptionValue * value = theme->getOptionValue(index); if (editZoneOption(y, option, value, attr, EE_GENERAL, event)) { theme->update(); } } else if (index == optionsCount) { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TOP_BAR); drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP, attr); if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { currentScreen = customScreens[0]; currentContainer = topbar; pushMenu(menuWidgetsSetup); } } break; } } } return true; } enum MenuScreenSetupItems { ITEM_SCREEN_SETUP_LAYOUT, ITEM_SCREEN_SETUP_WIDGETS_SETUP = ITEM_SCREEN_SETUP_LAYOUT+2, ITEM_SCREEN_SETUP_LAYOUT_OPTION1, }; bool menuScreenSetup(int index, event_t event) { if (customScreens[index] == NULL) { return menuScreenAdd(event); } currentScreen = customScreens[index]; currentContainer = currentScreen; bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); char title[] = "Main view X"; title[sizeof(title)-2] = '1' + index; menuPageCount = updateMainviewsMenu(); const ZoneOption * options = currentScreen->getFactory()->getOptions(); int optionsCount = getOptionsCount(options); linesCount = ITEM_SCREEN_SETUP_LAYOUT_OPTION1 + optionsCount; if (menuPageCount > 3) ++linesCount; uint8_t mstate_tab[2 + MAX_LAYOUT_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredLayouts().size()-1)), ORPHAN_ROW }; for (int i=0; i0) ? BLINK|INVERS : INVERS); LcdFlags attr = (menuVerticalPosition == k ? blink : 0); switch(k) { case ITEM_SCREEN_SETUP_LAYOUT: { lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_LAYOUT); const LayoutFactory * factory = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredLayouts(), currentScreen->getFactory(), needsOffsetCheck, attr, event); if (factory) { delete customScreens[index]; currentScreen = customScreens[index] = factory->create(&g_model.screenData[index].layoutData); strncpy(g_model.screenData[index].layoutName, factory->getName(), sizeof(g_model.screenData[index].layoutName)); killEvents(KEY_ENTER); storageDirty(EE_MODEL); } break; } case ITEM_SCREEN_SETUP_LAYOUT+1: break; case ITEM_SCREEN_SETUP_WIDGETS_SETUP: drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP_WIDGETS, attr); if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { pushMenu(menuWidgetsSetup); } break; default: { uint8_t o = k - ITEM_SCREEN_SETUP_LAYOUT_OPTION1; if (o < optionsCount) { const ZoneOption * option = &options[o]; ZoneOptionValue * value = currentScreen->getOptionValue(o); if (editZoneOption(y, option, value, attr, EE_MODEL, event)) { currentScreen->update(); } } else if (menuPageCount > 3 && o == optionsCount) { drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_REMOVE_SCREEN, attr); if (attr && event == EVT_KEY_LONG(KEY_ENTER)) { delete currentScreen; if (index != MAX_CUSTOM_SCREENS-1) { memmove(&g_model.screenData[index], &g_model.screenData[index + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - index - 1)); memmove(&customScreens[index], &customScreens[index + 1], sizeof(Layout *) * (MAX_CUSTOM_SCREENS - index - 1)); } memset(&g_model.screenData[MAX_CUSTOM_SCREENS-1], 0, sizeof(CustomScreenData)); customScreens[MAX_CUSTOM_SCREENS-1] = NULL; loadCustomScreens(); killEvents(KEY_ENTER); chainMenu(menuTabScreensSetup[index > 0 ? index : 1]); return false; } } break; } } } return true; } template bool menuCustomScreenSetup(event_t event) { return menuScreenSetup(N, event); } const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] = { menuScreensTheme, menuCustomScreenSetup<0>, menuCustomScreenSetup<1>, menuCustomScreenSetup<2>, menuCustomScreenSetup<3>, menuCustomScreenSetup<4> }; int updateMainviewsMenu() { for (int index=1; indexcreate(&g_model.screenData[menuPageCount-2].layoutData); strncpy(g_model.screenData[menuPageCount-2].layoutName, lf->getName(), sizeof(g_model.screenData[menuPageCount-2].layoutName)); s_editMode = 0; menuHorizontalPosition = -1; killEvents(KEY_ENTER); storageDirty(EE_MODEL); return false; } SIMPLE_MENU_WITH_OPTIONS(STR_ADDMAINVIEW, THEME_ICONS, menuTabScreensSetup, menuPageCount, menuPageCount-1, 0); return true; }