From 1d8c5d38e4ff31df683d7482ba4bfacaa9dd4503 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 21 Oct 2019 21:06:43 +0200 Subject: [PATCH] BitmapBuffer::load renamed to BitmapBuffer::loadBitmap --- radio/src/gui/colorlcd/CMakeLists.txt | 5 +- radio/src/gui/colorlcd/draw_functions.cpp | 4 +- radio/src/gui/colorlcd/model_select.cpp | 559 ++++++------------- radio/src/gui/colorlcd/model_select.h | 31 ++ radio/src/gui/colorlcd/model_select_old.cpp | 519 ++++++++++++++++++ radio/src/gui/colorlcd/radio_sdmanager.cpp | 2 +- radio/src/gui/colorlcd/splash.cpp | 2 +- radio/src/gui/colorlcd/theme.cpp | 8 +- radio/src/gui/colorlcd/themes/flysky.cpp | 14 +- radio/src/gui/colorlcd/themes/frsky.cpp | 14 +- radio/src/gui/colorlcd/view_main.cpp | 96 ++-- radio/src/gui/colorlcd/view_main.h | 1 + radio/src/storage/modelslist.cpp | 572 ++++++++++++++++++++ radio/src/storage/modelslist.h | 147 +++++ radio/src/storage/sdcard_raw.cpp | 7 +- radio/src/thirdparty/libopenui | 2 +- 16 files changed, 1513 insertions(+), 470 deletions(-) create mode 100644 radio/src/gui/colorlcd/model_select.h create mode 100644 radio/src/gui/colorlcd/model_select_old.cpp create mode 100644 radio/src/storage/modelslist.cpp create mode 100644 radio/src/storage/modelslist.h diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index a7136f7d2..0538a23b9 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -30,7 +30,7 @@ set(GUI_SRC draw_functions.cpp # menus.cpp menu_model.cpp - # model_select.cpp + model_select.cpp model_setup.cpp model_flightmodes.cpp model_inputs.cpp @@ -83,6 +83,7 @@ endif() set(SRC ${SRC} stubs.cpp + storage/modelslist.cpp ) # includes libopenui @@ -104,5 +105,3 @@ add_gui_src(curveedit.cpp) if(HARDWARE_TOUCH) add_gui_src(keyboard_curve.cpp) endif() - -# gui/${GUI_DIR}/modelslist.cpp diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index d26717051..5caea1022 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -203,7 +203,7 @@ void drawSleepBitmap() lcd->clearClippingRect(); lcd->clear(); - const BitmapBuffer * bitmap = BitmapBuffer::load(static_cast(theme)->getFilePath("sleep.bmp")); + const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(static_cast(theme)->getFilePath("sleep.bmp")); if (bitmap) { lcd->drawBitmap((LCD_W-bitmap->getWidth())/2, (LCD_H-bitmap->getHeight())/2, bitmap); delete bitmap; @@ -219,7 +219,7 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char return; static uint32_t lastDuration = 0xffffffff; - static const BitmapBuffer * shutdown = BitmapBuffer::load(static_cast(theme)->getFilePath("shutdown.bmp")); + static const BitmapBuffer * shutdown = BitmapBuffer::loadBitmap(static_cast(theme)->getFilePath("shutdown.bmp")); if (shutdown) { if (duration < lastDuration) { diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index cfe82b3ae..ee515f6ae 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -18,8 +18,10 @@ * GNU General Public License for more details. */ +#include +#include "model_select.h" #include "opentx.h" -#include "modelslist.h" +#include "storage/modelslist.h" #define CATEGORIES_WIDTH 120 #define MODELS_LEFT 123 @@ -37,12 +39,14 @@ enum ModelDeleteMode { }; uint8_t selectMode, deleteMode; +//ModelsList modelslist; ModelsCategory * currentCategory; int currentCategoryIndex; ModelCell * currentModel; -void drawCategory(coord_t y, const char * name, bool selected) +/* +void drawCategory(BitmapBuffer * dc, coord_t y, const char * name, bool selected) { if (selected) { lcdDrawSolidFilledRect(1, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH-10, INVERT_LINE_HEIGHT+2, FOCUS_BGCOLOR); @@ -64,247 +68,38 @@ void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selec drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); if (selectMode == MODE_MOVE_MODEL) { lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth(), y, modelselModelMoveBackground, TITLE_BGCOLOR); - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth()+12, y+5, modelselModelMoveIcon, DEFAULT_BGCOLOR); + lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth()+12, y+5, modelselModelMoveIcon, TEXT_BGCOLOR); } } } +*/ uint16_t categoriesVerticalOffset = 0; uint16_t categoriesVerticalPosition = 0; #define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) +#if 0 void setCurrentModel(unsigned int index) { - std::list::iterator it = currentCategory->begin(); + auto it = currentCategory->begin(); std::advance(it, index); currentModel = *it; - menuVerticalPosition = index / 2; - menuHorizontalPosition = index & 1; - menuVerticalOffset = limit(menuVerticalPosition-2, menuVerticalOffset, min(menuVerticalPosition, max(0, (currentCategory->size()-7)/2))); } +#endif + void setCurrentCategory(unsigned int index) { currentCategoryIndex = index; - const std::list& cats = modelslist.getCategories(); - std::list::const_iterator it = cats.begin(); + auto it = modelslist.getCategories().begin(); std::advance(it, index); currentCategory = *it; categoriesVerticalPosition = index; - categoriesVerticalOffset = limit(categoriesVerticalPosition-4, categoriesVerticalOffset, min(categoriesVerticalPosition, max(0, cats.size()-5))); - if (currentCategory->size() > 0) + categoriesVerticalOffset = limit(categoriesVerticalPosition-4, categoriesVerticalOffset, min(categoriesVerticalPosition, max(0, modelslist.getCategories().size()-5))); + /*if (currentCategory->size() > 0) setCurrentModel(0); else - currentModel = NULL; -} - -#if defined(LUA) - -#define MAX_WIZARD_NAME_LEN (sizeof(WIZARD_PATH)+20) -#define WIZARD_SPACING 40 -#define WIZARD_LEFT_SPACING 30 -#define WIZARD_ICON_X 80 -#define WIZARD_ICON_Y 110 -#define WIZARD_TEXT_Y 195 - -uint8_t getWizardCount() -{ - uint8_t wizCnt=0; - DIR dir; - static FILINFO fno; - - FRESULT res = f_opendir(&dir, WIZARD_PATH); - if (res == FR_OK) { - for (;;) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) { - break; - } - if (fno.fattrib & AM_DIR) { - wizCnt++; - } - } - } - f_closedir(&dir); - return wizCnt; -} - -bool menuModelWizard(event_t event) -{ - static uint8_t wizardSelected = 0; - static uint8_t wizardCnt = getWizardCount(); - bool executeMe = false; - uint8_t first = 0; - DIR dir; - static FILINFO fno; - char wizpath[MAX_WIZARD_NAME_LEN]; - - if (wizardCnt == 0) { - chainMenu(menuModelSelect); - return false; - } - - switch(event) { - case 0: - // no need to refresh the screen - return false; - - case EVT_KEY_FIRST(KEY_EXIT): - chainMenu(menuModelSelect); - return false; - - case EVT_KEY_BREAK(KEY_ENTER): - executeMe = true; - break; - - case EVT_ROTARY_RIGHT: - if (wizardSelected < wizardCnt-1) { - wizardSelected++; - } - if (wizardSelected > 3) { - first = wizardSelected - 3; - } - break; - - case EVT_ROTARY_LEFT: - if (wizardSelected != 0) { - wizardSelected--; - } - if(wizardSelected < first) - { - first = wizardSelected; - } - break; - } - strcpy(wizpath, WIZARD_PATH); - strcpy(&wizpath[sizeof(WIZARD_PATH)-1], "/"); - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); - lcd->drawBitmap(0, 0, modelselWizardBackground); - FRESULT res = f_opendir(&dir, WIZARD_PATH); - if (res == FR_OK) { - for (uint8_t wizidx=0;;wizidx++) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) { - break; - } - if (fno.fattrib & AM_DIR) { - if((wizidx >= first) && (wizidx < (first+4))) { - uint16_t x = WIZARD_LEFT_SPACING + (wizidx - first) * (WIZARD_SPACING + WIZARD_ICON_X); - strcpy(&wizpath[sizeof(WIZARD_PATH)], fno.fname); - strcpy(&wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)], "/icon.png"); - lcdDrawText(x + 10, WIZARD_TEXT_Y, fno.fname); - BitmapBuffer * background = BitmapBuffer::load(wizpath); - lcd->drawBitmap(x, WIZARD_ICON_Y, background); - if(wizidx == wizardSelected ) { - if (wizardSelected < 5) { - lcdDrawRect(x, WIZARD_ICON_Y, 85, 130, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); - lcdDrawRect(x+5, WIZARD_TEXT_Y, 75, 4, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); - } - if (executeMe) { - strcpy(&wizpath[sizeof(WIZARD_PATH)+strlen(fno.fname)], "/wizard.lua"); - if (isFileAvailable(wizpath)) { - wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)] = 0; - f_chdir(wizpath); - luaExec(WIZARD_NAME); - } - } - } - delete background; - } - } - } - f_closedir(&dir); - } - return true; -} -#endif - -void onDeleteModelConfirm(const char * result) -{ - if (result == STR_OK) { - if (deleteMode == MODE_DELETE_CATEGORY) { - modelslist.removeCategory(currentCategory); - modelslist.save(); - setCurrentCategory(currentCategoryIndex > 0 ? currentCategoryIndex-1 : currentCategoryIndex); - } - else if (deleteMode == MODE_DELETE_MODEL){ - int modelIndex = MODEL_INDEX(); - modelslist.removeModel(currentCategory, currentModel); - s_copyMode = 0; - putEvent(EVT_REFRESH); - if (modelIndex > 0) { - modelIndex--; - } - setCurrentModel(modelIndex); - } - } -} - -void onModelSelectMenu(const char * result) -{ - if (result == STR_SELECT_MODEL) { - // we store the latest changes if any - storageFlushCurrentModel(); - storageCheck(true); - memcpy(g_eeGeneral.currModelFilename, currentModel->modelFilename, LEN_MODEL_FILENAME); - modelslist.setCurrentModel(currentModel); - loadModel(g_eeGeneral.currModelFilename, true); - storageDirty(EE_GENERAL); - storageCheck(true); - chainMenu(menuMainView); - } - else if (result == STR_DELETE_MODEL) { - POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); - SET_WARNING_INFO(currentModel->modelName, LEN_MODEL_NAME, 0); - deleteMode = MODE_DELETE_MODEL; - } - else if (result == STR_CREATE_MODEL) { - storageCheck(true); - modelslist.addModel(currentCategory, createModel()); - selectMode = MODE_SELECT_MODEL; - setCurrentModel(currentCategory->size() - 1); - modelslist.setCurrentModel(currentModel); - modelslist.onNewModelCreated(currentModel, &g_model); -#if defined(LUA) - chainMenu(menuModelWizard); -#endif - } - else if (result == STR_DUPLICATE_MODEL) { - char duplicatedFilename[LEN_MODEL_FILENAME+1]; - memcpy(duplicatedFilename, currentModel->modelFilename, sizeof(duplicatedFilename)); - if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { - sdCopyFile(currentModel->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); - ModelCell* dup_model = modelslist.addModel(currentCategory, duplicatedFilename); - dup_model->fetchRfData(); - setCurrentModel(currentCategory->size() - 1); - } - else { - POPUP_WARNING("Invalid File"); - } - } - else if (result == STR_MOVE_MODEL) { - selectMode = MODE_MOVE_MODEL; - } - else if (result == STR_CREATE_CATEGORY) { - currentCategory = modelslist.createCategory(); - setCurrentCategory(modelslist.getCategories().size() - 1); - } - else if (result == STR_RENAME_CATEGORY) { - selectMode = MODE_RENAME_CATEGORY; - s_editMode = EDIT_MODIFY_STRING; - editNameCursorPos = 0; - } - else if (result == STR_DELETE_CATEGORY) { - if (currentCategory->size() > 0){ - POPUP_WARNING(STR_DELETE_ERROR); - SET_WARNING_INFO(STR_CAT_NOT_EMPTY, sizeof(TR_CAT_NOT_EMPTY), 0); - } - else { - POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); - SET_WARNING_INFO(currentCategory->name, LEN_MODEL_FILENAME, 0); - deleteMode = MODE_DELETE_CATEGORY; - } - } + currentModel = NULL;*/ } void initModelsList() @@ -314,13 +109,13 @@ void initModelsList() categoriesVerticalOffset = 0; bool found = false; int index = 0; - const std::list& cats = modelslist.getCategories(); - for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { - if (*it == modelslist.getCurrentCategory()) { + for (auto & category : modelslist.getCategories()) { + if (category == modelslist.getCurrentCategory()) { setCurrentCategory(index); found = true; break; } + index++; } if (!found) { setCurrentCategory(0); @@ -329,191 +124,159 @@ void initModelsList() menuVerticalOffset = 0; found = false; index = 0; - for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { - if (*it == modelslist.getCurrentModel()) { - setCurrentModel(index); + for (auto & cell : *currentCategory) { + if (cell == modelslist.getCurrentModel()) { + // setCurrentModel(index); found = true; break; } } if (!found) { - setCurrentModel(0); + // setCurrentModel(0); } } -bool menuModelSelect(event_t event) -{ - const std::list& cats = modelslist.getCategories(); - switch(event) { - case 0: - // no need to refresh the screen - return false; +class ModelselectButton: public Button { + public: + ModelselectButton(FormGroup * parent, const rect_t & rect, ModelCell * modelCell, Window * footer); - case EVT_ENTRY: - selectMode = MODE_SELECT_MODEL; + void paint(BitmapBuffer * dc) override + { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + dc->drawBitmap(10, 2, modelCell->getBuffer()); + if (modelCell == modelslist.getCurrentModel()) { + dc->drawBitmapPattern(112, 71, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); + } + } + + const char * modelFilename() { + return modelCell->modelFilename; + } + + protected: + ModelCell * modelCell; +}; + +class ModelselectFooter: public Window { + public: + ModelselectFooter(Window * parent, const rect_t & rect): + Window(parent, rect) + { + } + + void paint(BitmapBuffer * dc) override + { + dc->drawSolidFilledRect(0, 5, rect.w, 2, CURVE_AXIS_COLOR); + dc->drawBitmap(7, 12, modelselSdFreeBitmap); + uint32_t size = sdGetSize() / 100; + dc->drawNumber(24, 11, size, PREC1|SMLSIZE, 0, NULL, "GB"); + dc->drawBitmap(77, 12, modelselModelQtyBitmap); + dc->drawNumber(99, 11, modelslist.getModelsCount(), SMLSIZE); + ModelselectButton * selectedModel = dynamic_cast(focusWindow); + if (selectedModel) { + dc->drawBitmap(7, 37, modelselModelNameBitmap); + dc->drawText(24, 32, selectedModel->modelFilename(), SMLSIZE | DEFAULT_COLOR); + } + } +}; + +class ModelselectPage: public PageTab { + public: + ModelselectPage() : + PageTab(STR_MODEL_SELECT, ICON_MODEL_CURVES /*ICON_MODEL_SELECT*/) + { + } + + static void updateModels(FormGroup * window, Window * footer, int selected = -1) + { + window->clear(); + int index = 0; + for (auto it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { + Button * button = new ModelselectButton(window, {10, 10 + index * 104, LCD_W - 20, 94}, *it, footer); + if (selected == index) { + button->setFocus(); + } + } + window->adjustInnerHeight(); + } + + void build(FormWindow * window) override + { initModelsList(); - break; - - case EVT_KEY_BREAK(KEY_ENTER): - if (selectMode == MODE_MOVE_MODEL) - selectMode = MODE_SELECT_MODEL; - break; - - case EVT_KEY_FIRST(KEY_EXIT): - switch (selectMode) { - case MODE_MOVE_MODEL: - selectMode = MODE_SELECT_MODEL; - break; - case MODE_SELECT_MODEL: - chainMenu(menuMainView); - return false; - } - break; - - case EVT_KEY_FIRST(KEY_PGUP): -#if defined(PCBX10) - case EVT_KEY_LONG(KEY_PGDN): -#endif - if (selectMode == MODE_SELECT_MODEL) { - if (categoriesVerticalPosition == 0) - categoriesVerticalPosition = cats.size() - 1; - else - categoriesVerticalPosition -= 1; - setCurrentCategory(categoriesVerticalPosition); - } - else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition > 0) { - ModelsCategory * previous_category = currentCategory; - ModelCell * model = currentModel; - categoriesVerticalPosition -= 1; - setCurrentCategory(categoriesVerticalPosition); - modelslist.moveModel(model, previous_category, currentCategory); - setCurrentModel(currentCategory->size()-1); - } - killEvents(event); - break; - -#if defined(PCBX12S) - case EVT_KEY_FIRST(KEY_PGDN): -#elif defined(PCBX10) - case EVT_KEY_BREAK(KEY_PGDN): -#endif - if (selectMode == MODE_SELECT_MODEL) { - categoriesVerticalPosition += 1; - if (categoriesVerticalPosition >= cats.size()) - categoriesVerticalPosition = 0; - setCurrentCategory(categoriesVerticalPosition); - } - else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition < cats.size()-1) { - ModelsCategory * previous_category = currentCategory; - ModelCell * model = currentModel; - categoriesVerticalPosition += 1; - setCurrentCategory(categoriesVerticalPosition); - modelslist.moveModel(model, previous_category, currentCategory); - setCurrentModel(currentCategory->size()-1); - } - break; - - case EVT_KEY_LONG(KEY_ENTER): - if (selectMode == MODE_SELECT_MODEL) { - killEvents(event); - if (currentModel && currentModel != modelslist.getCurrentModel()) { - POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL); - } - POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); - if (currentModel) { - POPUP_MENU_ADD_ITEM(STR_DUPLICATE_MODEL); - POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL); - } - // POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); - if (currentModel && currentModel != modelslist.getCurrentModel()) { - POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL); - } - // POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL); - POPUP_MENU_ADD_ITEM(STR_CREATE_CATEGORY); - POPUP_MENU_ADD_ITEM(STR_RENAME_CATEGORY); - if (cats.size() > 1) { - POPUP_MENU_ADD_ITEM(STR_DELETE_CATEGORY); - } - POPUP_MENU_START(onModelSelectMenu); - } - break; - - } - - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); - lcd->drawBitmap(0, 0, modelselIconBitmap); - - // Categories - int index = 0; - coord_t y = 97; - drawVerticalScrollbar(CATEGORIES_WIDTH-1, y-1, 5*(FH+7)-5, categoriesVerticalOffset, cats.size(), 5); - for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { - if (index >= categoriesVerticalOffset && index < categoriesVerticalOffset+5) { - if (index != categoriesVerticalOffset) { - lcdDrawSolidHorizontalLine(1, y-4, CATEGORIES_WIDTH-10, LINE_COLOR); - } - if (selectMode == MODE_RENAME_CATEGORY && currentCategory == *it) { - lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN+1, CATEGORIES_WIDTH-2, INVERT_LINE_HEIGHT, DEFAULT_BGCOLOR); - editName(MENUS_MARGIN_LEFT, y, currentCategory->name, LEN_MODEL_FILENAME, event, 1, 0); - if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) { - modelslist.save(); - selectMode = MODE_SELECT_MODEL; - putEvent(EVT_REFRESH); - } - } - else { - drawCategory(y, (*it)->name, currentCategory==*it); - } - y += FH+7; + FormWindow * body = new FormWindow(window, {0, 0, LCD_W, window->height() - 55}); + Window * footer = new ModelselectFooter(window, {0, window->height() - 55, LCD_W, 55}); + updateModels(body, footer); } - } +}; - // Models - index = 0; - y = 5; - for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { - if (index >= menuVerticalOffset*2 && index < (menuVerticalOffset+4)*2) { - bool selected = ((selectMode==MODE_SELECT_MODEL || selectMode==MODE_MOVE_MODEL) && index==menuVerticalPosition*2+menuHorizontalPosition); - bool current = !strncmp((*it)->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); - if (index & 1) { - drawModel(MODELS_LEFT + MODELS_COLUMN_WIDTH, y, *it, current, selected); - y += 66; - } - else { - drawModel(MODELS_LEFT, y, *it, current, selected); - } - if (selected) { - lcd->drawBitmap(5, LCD_H-FH, modelselModelNameBitmap); - lcdDrawText(22, LCD_H-FH-1, (*it)->modelFilename, SMLSIZE|DEFAULT_COLOR); - } - } - } - // Navigation - if (currentModel) { - if (selectMode == MODE_SELECT_MODEL) { - if (navigate(event, index, 4, 2) != 0) { - setCurrentModel(MODEL_INDEX()); - putEvent(EVT_REFRESH); - } - } - else if (selectMode == MODE_MOVE_MODEL) { - int8_t direction = navigate(event, index, 4, 2, false); - if (direction) { - modelslist.moveModel(currentCategory, currentModel, direction); - setCurrentModel(MODEL_INDEX()); - putEvent(EVT_REFRESH); - } - } - } - drawVerticalScrollbar(DEFAULT_SCROLLBAR_X + 4, 7, LCD_H - 15, menuVerticalOffset, (index + 1) / 2, 4); - - // Footer - lcd->drawBitmap(5, LCD_H-FH-20, modelselSdFreeBitmap); - uint32_t size = sdGetSize() / 100; - lcdDrawNumber(22, LCD_H-FH-21, size, PREC1|SMLSIZE, 0, NULL, "GB"); - lcd->drawBitmap(70, LCD_H-FH-20, modelselModelQtyBitmap); - lcdDrawNumber(92, LCD_H-FH-21, modelslist.getModelsCount(),SMLSIZE); - - return true; +ModelselectMenu::ModelselectMenu(): + TabsGroup(ICON_MODEL_CURVES) +{ + addTab(new ModelselectPage()); } + +ModelselectButton::ModelselectButton(FormGroup * parent, const rect_t & rect, ModelCell * modelCell, Window * footer): + Button(parent, rect, + [=]() -> uint8_t { + if (hasFocus()) { + Menu * menu = new Menu(); + if (modelCell && modelCell != modelslist.getCurrentModel()) { + menu->addLine(STR_SELECT_MODEL, [=]() { + // we store the latest changes if any + storageFlushCurrentModel(); + storageCheck(true); + memcpy(g_eeGeneral.currModelFilename, modelCell->modelFilename, LEN_MODEL_FILENAME); + loadModel(g_eeGeneral.currModelFilename, false); + storageDirty(EE_GENERAL); + storageCheck(true); + // chainMenu(menuMainView); + postModelLoad(true); + modelslist.setCurrentModel(modelCell); + // TODO ModelselectPage::updateModels(parent, footer, modelslist.getModelIndex(modelCell)); + }); + } + menu->addLine(STR_CREATE_MODEL, [=]() { + storageCheck(true); + modelslist.setCurrentModel(modelslist.addModel(currentCategory, createModel())); +#if defined(LUA) + // chainMenu(menuModelWizard); +#endif + ModelselectPage::updateModels(parent, footer, currentCategory->size() - 1); + }); + if (modelCell) { + menu->addLine(STR_DUPLICATE_MODEL, [=]() { + char duplicatedFilename[LEN_MODEL_FILENAME + 1]; + memcpy(duplicatedFilename, modelCell->modelFilename, sizeof(duplicatedFilename)); + if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { + sdCopyFile(modelCell->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); + modelslist.addModel(currentCategory, duplicatedFilename); + ModelselectPage::updateModels(parent, footer, currentCategory->size() - 1); + } + else { + POPUP_WARNING("Invalid File"); + } + }); + } + // menu->addLine(STR_MOVE_MODEL); + if (modelCell && modelCell != modelslist.getCurrentModel()) { + menu->addLine(STR_DELETE_MODEL, [=]() { + // POPUP_CONFIRMATION(STR_DELETEMODEL); + // SET_WARNING_INFO(modelCell->modelName, LEN_MODEL_NAME, 0); + unsigned int index = 0; // TODOmodelslist.getModelIndex(modelCell); + if (index > 0) + --index; + modelslist.removeModel(currentCategory, modelCell); + ModelselectPage::updateModels(parent, footer, index); + }); + } + } + else { + setFocus(); + footer->invalidate(); + } + return 1; + }, BUTTON_CHECKED_ON_FOCUS), + modelCell(modelCell) +{ +} \ No newline at end of file diff --git a/radio/src/gui/colorlcd/model_select.h b/radio/src/gui/colorlcd/model_select.h new file mode 100644 index 000000000..33b977807 --- /dev/null +++ b/radio/src/gui/colorlcd/model_select.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#ifndef _MODEL_SELECT_H_ +#define _MODEL_SELECT_H_ + +#include "tabsgroup.h" + +class ModelselectMenu: public TabsGroup { + public: + ModelselectMenu(); +}; + +#endif // _MODEL_SELECT_H_ diff --git a/radio/src/gui/colorlcd/model_select_old.cpp b/radio/src/gui/colorlcd/model_select_old.cpp new file mode 100644 index 000000000..cfe82b3ae --- /dev/null +++ b/radio/src/gui/colorlcd/model_select_old.cpp @@ -0,0 +1,519 @@ +/* + * 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" +#include "modelslist.h" + +#define CATEGORIES_WIDTH 120 +#define MODELS_LEFT 123 +#define MODELS_COLUMN_WIDTH 174 + +enum ModelSelectMode { + MODE_SELECT_MODEL, + MODE_RENAME_CATEGORY, + MODE_MOVE_MODEL, +}; + +enum ModelDeleteMode { + MODE_DELETE_MODEL, + MODE_DELETE_CATEGORY, +}; + +uint8_t selectMode, deleteMode; + +ModelsCategory * currentCategory; +int currentCategoryIndex; +ModelCell * currentModel; + +void drawCategory(coord_t y, const char * name, bool selected) +{ + if (selected) { + lcdDrawSolidFilledRect(1, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH-10, INVERT_LINE_HEIGHT+2, FOCUS_BGCOLOR); + lcdDrawText(6, y, name, DEFAULT_COLOR | INVERS); + } + else { + lcdDrawText(6, y, name, DEFAULT_COLOR); + } +} + +void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selected) +{ + lcd->drawBitmap(x+1, y+1, model->getBuffer()); + if (current) { + lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); + } + if (selected) { + lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR); + drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); + if (selectMode == MODE_MOVE_MODEL) { + lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth(), y, modelselModelMoveBackground, TITLE_BGCOLOR); + lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth()+12, y+5, modelselModelMoveIcon, DEFAULT_BGCOLOR); + } + } +} + +uint16_t categoriesVerticalOffset = 0; +uint16_t categoriesVerticalPosition = 0; +#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) + +void setCurrentModel(unsigned int index) +{ + std::list::iterator it = currentCategory->begin(); + std::advance(it, index); + currentModel = *it; + menuVerticalPosition = index / 2; + menuHorizontalPosition = index & 1; + menuVerticalOffset = limit(menuVerticalPosition-2, menuVerticalOffset, min(menuVerticalPosition, max(0, (currentCategory->size()-7)/2))); +} + +void setCurrentCategory(unsigned int index) +{ + currentCategoryIndex = index; + const std::list& cats = modelslist.getCategories(); + std::list::const_iterator it = cats.begin(); + std::advance(it, index); + currentCategory = *it; + categoriesVerticalPosition = index; + categoriesVerticalOffset = limit(categoriesVerticalPosition-4, categoriesVerticalOffset, min(categoriesVerticalPosition, max(0, cats.size()-5))); + if (currentCategory->size() > 0) + setCurrentModel(0); + else + currentModel = NULL; +} + +#if defined(LUA) + +#define MAX_WIZARD_NAME_LEN (sizeof(WIZARD_PATH)+20) +#define WIZARD_SPACING 40 +#define WIZARD_LEFT_SPACING 30 +#define WIZARD_ICON_X 80 +#define WIZARD_ICON_Y 110 +#define WIZARD_TEXT_Y 195 + +uint8_t getWizardCount() +{ + uint8_t wizCnt=0; + DIR dir; + static FILINFO fno; + + FRESULT res = f_opendir(&dir, WIZARD_PATH); + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) { + break; + } + if (fno.fattrib & AM_DIR) { + wizCnt++; + } + } + } + f_closedir(&dir); + return wizCnt; +} + +bool menuModelWizard(event_t event) +{ + static uint8_t wizardSelected = 0; + static uint8_t wizardCnt = getWizardCount(); + bool executeMe = false; + uint8_t first = 0; + DIR dir; + static FILINFO fno; + char wizpath[MAX_WIZARD_NAME_LEN]; + + if (wizardCnt == 0) { + chainMenu(menuModelSelect); + return false; + } + + switch(event) { + case 0: + // no need to refresh the screen + return false; + + case EVT_KEY_FIRST(KEY_EXIT): + chainMenu(menuModelSelect); + return false; + + case EVT_KEY_BREAK(KEY_ENTER): + executeMe = true; + break; + + case EVT_ROTARY_RIGHT: + if (wizardSelected < wizardCnt-1) { + wizardSelected++; + } + if (wizardSelected > 3) { + first = wizardSelected - 3; + } + break; + + case EVT_ROTARY_LEFT: + if (wizardSelected != 0) { + wizardSelected--; + } + if(wizardSelected < first) + { + first = wizardSelected; + } + break; + } + strcpy(wizpath, WIZARD_PATH); + strcpy(&wizpath[sizeof(WIZARD_PATH)-1], "/"); + lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); + lcd->drawBitmap(0, 0, modelselWizardBackground); + FRESULT res = f_opendir(&dir, WIZARD_PATH); + if (res == FR_OK) { + for (uint8_t wizidx=0;;wizidx++) { + res = f_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) { + break; + } + if (fno.fattrib & AM_DIR) { + if((wizidx >= first) && (wizidx < (first+4))) { + uint16_t x = WIZARD_LEFT_SPACING + (wizidx - first) * (WIZARD_SPACING + WIZARD_ICON_X); + strcpy(&wizpath[sizeof(WIZARD_PATH)], fno.fname); + strcpy(&wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)], "/icon.png"); + lcdDrawText(x + 10, WIZARD_TEXT_Y, fno.fname); + BitmapBuffer * background = BitmapBuffer::load(wizpath); + lcd->drawBitmap(x, WIZARD_ICON_Y, background); + if(wizidx == wizardSelected ) { + if (wizardSelected < 5) { + lcdDrawRect(x, WIZARD_ICON_Y, 85, 130, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); + lcdDrawRect(x+5, WIZARD_TEXT_Y, 75, 4, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); + } + if (executeMe) { + strcpy(&wizpath[sizeof(WIZARD_PATH)+strlen(fno.fname)], "/wizard.lua"); + if (isFileAvailable(wizpath)) { + wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)] = 0; + f_chdir(wizpath); + luaExec(WIZARD_NAME); + } + } + } + delete background; + } + } + } + f_closedir(&dir); + } + return true; +} +#endif + +void onDeleteModelConfirm(const char * result) +{ + if (result == STR_OK) { + if (deleteMode == MODE_DELETE_CATEGORY) { + modelslist.removeCategory(currentCategory); + modelslist.save(); + setCurrentCategory(currentCategoryIndex > 0 ? currentCategoryIndex-1 : currentCategoryIndex); + } + else if (deleteMode == MODE_DELETE_MODEL){ + int modelIndex = MODEL_INDEX(); + modelslist.removeModel(currentCategory, currentModel); + s_copyMode = 0; + putEvent(EVT_REFRESH); + if (modelIndex > 0) { + modelIndex--; + } + setCurrentModel(modelIndex); + } + } +} + +void onModelSelectMenu(const char * result) +{ + if (result == STR_SELECT_MODEL) { + // we store the latest changes if any + storageFlushCurrentModel(); + storageCheck(true); + memcpy(g_eeGeneral.currModelFilename, currentModel->modelFilename, LEN_MODEL_FILENAME); + modelslist.setCurrentModel(currentModel); + loadModel(g_eeGeneral.currModelFilename, true); + storageDirty(EE_GENERAL); + storageCheck(true); + chainMenu(menuMainView); + } + else if (result == STR_DELETE_MODEL) { + POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); + SET_WARNING_INFO(currentModel->modelName, LEN_MODEL_NAME, 0); + deleteMode = MODE_DELETE_MODEL; + } + else if (result == STR_CREATE_MODEL) { + storageCheck(true); + modelslist.addModel(currentCategory, createModel()); + selectMode = MODE_SELECT_MODEL; + setCurrentModel(currentCategory->size() - 1); + modelslist.setCurrentModel(currentModel); + modelslist.onNewModelCreated(currentModel, &g_model); +#if defined(LUA) + chainMenu(menuModelWizard); +#endif + } + else if (result == STR_DUPLICATE_MODEL) { + char duplicatedFilename[LEN_MODEL_FILENAME+1]; + memcpy(duplicatedFilename, currentModel->modelFilename, sizeof(duplicatedFilename)); + if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { + sdCopyFile(currentModel->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); + ModelCell* dup_model = modelslist.addModel(currentCategory, duplicatedFilename); + dup_model->fetchRfData(); + setCurrentModel(currentCategory->size() - 1); + } + else { + POPUP_WARNING("Invalid File"); + } + } + else if (result == STR_MOVE_MODEL) { + selectMode = MODE_MOVE_MODEL; + } + else if (result == STR_CREATE_CATEGORY) { + currentCategory = modelslist.createCategory(); + setCurrentCategory(modelslist.getCategories().size() - 1); + } + else if (result == STR_RENAME_CATEGORY) { + selectMode = MODE_RENAME_CATEGORY; + s_editMode = EDIT_MODIFY_STRING; + editNameCursorPos = 0; + } + else if (result == STR_DELETE_CATEGORY) { + if (currentCategory->size() > 0){ + POPUP_WARNING(STR_DELETE_ERROR); + SET_WARNING_INFO(STR_CAT_NOT_EMPTY, sizeof(TR_CAT_NOT_EMPTY), 0); + } + else { + POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); + SET_WARNING_INFO(currentCategory->name, LEN_MODEL_FILENAME, 0); + deleteMode = MODE_DELETE_CATEGORY; + } + } +} + +void initModelsList() +{ + modelslist.load(); + + categoriesVerticalOffset = 0; + bool found = false; + int index = 0; + const std::list& cats = modelslist.getCategories(); + for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { + if (*it == modelslist.getCurrentCategory()) { + setCurrentCategory(index); + found = true; + break; + } + } + if (!found) { + setCurrentCategory(0); + } + + menuVerticalOffset = 0; + found = false; + index = 0; + for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { + if (*it == modelslist.getCurrentModel()) { + setCurrentModel(index); + found = true; + break; + } + } + if (!found) { + setCurrentModel(0); + } +} + +bool menuModelSelect(event_t event) +{ + const std::list& cats = modelslist.getCategories(); + switch(event) { + case 0: + // no need to refresh the screen + return false; + + case EVT_ENTRY: + selectMode = MODE_SELECT_MODEL; + initModelsList(); + break; + + case EVT_KEY_BREAK(KEY_ENTER): + if (selectMode == MODE_MOVE_MODEL) + selectMode = MODE_SELECT_MODEL; + break; + + case EVT_KEY_FIRST(KEY_EXIT): + switch (selectMode) { + case MODE_MOVE_MODEL: + selectMode = MODE_SELECT_MODEL; + break; + case MODE_SELECT_MODEL: + chainMenu(menuMainView); + return false; + } + break; + + case EVT_KEY_FIRST(KEY_PGUP): +#if defined(PCBX10) + case EVT_KEY_LONG(KEY_PGDN): +#endif + if (selectMode == MODE_SELECT_MODEL) { + if (categoriesVerticalPosition == 0) + categoriesVerticalPosition = cats.size() - 1; + else + categoriesVerticalPosition -= 1; + setCurrentCategory(categoriesVerticalPosition); + } + else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition > 0) { + ModelsCategory * previous_category = currentCategory; + ModelCell * model = currentModel; + categoriesVerticalPosition -= 1; + setCurrentCategory(categoriesVerticalPosition); + modelslist.moveModel(model, previous_category, currentCategory); + setCurrentModel(currentCategory->size()-1); + } + killEvents(event); + break; + +#if defined(PCBX12S) + case EVT_KEY_FIRST(KEY_PGDN): +#elif defined(PCBX10) + case EVT_KEY_BREAK(KEY_PGDN): +#endif + if (selectMode == MODE_SELECT_MODEL) { + categoriesVerticalPosition += 1; + if (categoriesVerticalPosition >= cats.size()) + categoriesVerticalPosition = 0; + setCurrentCategory(categoriesVerticalPosition); + } + else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition < cats.size()-1) { + ModelsCategory * previous_category = currentCategory; + ModelCell * model = currentModel; + categoriesVerticalPosition += 1; + setCurrentCategory(categoriesVerticalPosition); + modelslist.moveModel(model, previous_category, currentCategory); + setCurrentModel(currentCategory->size()-1); + } + break; + + case EVT_KEY_LONG(KEY_ENTER): + if (selectMode == MODE_SELECT_MODEL) { + killEvents(event); + if (currentModel && currentModel != modelslist.getCurrentModel()) { + POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL); + } + POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); + if (currentModel) { + POPUP_MENU_ADD_ITEM(STR_DUPLICATE_MODEL); + POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL); + } + // POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); + if (currentModel && currentModel != modelslist.getCurrentModel()) { + POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL); + } + // POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL); + POPUP_MENU_ADD_ITEM(STR_CREATE_CATEGORY); + POPUP_MENU_ADD_ITEM(STR_RENAME_CATEGORY); + if (cats.size() > 1) { + POPUP_MENU_ADD_ITEM(STR_DELETE_CATEGORY); + } + POPUP_MENU_START(onModelSelectMenu); + } + break; + + } + + lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); + lcd->drawBitmap(0, 0, modelselIconBitmap); + + // Categories + int index = 0; + coord_t y = 97; + drawVerticalScrollbar(CATEGORIES_WIDTH-1, y-1, 5*(FH+7)-5, categoriesVerticalOffset, cats.size(), 5); + for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { + if (index >= categoriesVerticalOffset && index < categoriesVerticalOffset+5) { + if (index != categoriesVerticalOffset) { + lcdDrawSolidHorizontalLine(1, y-4, CATEGORIES_WIDTH-10, LINE_COLOR); + } + if (selectMode == MODE_RENAME_CATEGORY && currentCategory == *it) { + lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN+1, CATEGORIES_WIDTH-2, INVERT_LINE_HEIGHT, DEFAULT_BGCOLOR); + editName(MENUS_MARGIN_LEFT, y, currentCategory->name, LEN_MODEL_FILENAME, event, 1, 0); + if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) { + modelslist.save(); + selectMode = MODE_SELECT_MODEL; + putEvent(EVT_REFRESH); + } + } + else { + drawCategory(y, (*it)->name, currentCategory==*it); + } + y += FH+7; + } + } + + // Models + index = 0; + y = 5; + for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { + if (index >= menuVerticalOffset*2 && index < (menuVerticalOffset+4)*2) { + bool selected = ((selectMode==MODE_SELECT_MODEL || selectMode==MODE_MOVE_MODEL) && index==menuVerticalPosition*2+menuHorizontalPosition); + bool current = !strncmp((*it)->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); + if (index & 1) { + drawModel(MODELS_LEFT + MODELS_COLUMN_WIDTH, y, *it, current, selected); + y += 66; + } + else { + drawModel(MODELS_LEFT, y, *it, current, selected); + } + if (selected) { + lcd->drawBitmap(5, LCD_H-FH, modelselModelNameBitmap); + lcdDrawText(22, LCD_H-FH-1, (*it)->modelFilename, SMLSIZE|DEFAULT_COLOR); + } + } + } + + // Navigation + if (currentModel) { + if (selectMode == MODE_SELECT_MODEL) { + if (navigate(event, index, 4, 2) != 0) { + setCurrentModel(MODEL_INDEX()); + putEvent(EVT_REFRESH); + } + } + else if (selectMode == MODE_MOVE_MODEL) { + int8_t direction = navigate(event, index, 4, 2, false); + if (direction) { + modelslist.moveModel(currentCategory, currentModel, direction); + setCurrentModel(MODEL_INDEX()); + putEvent(EVT_REFRESH); + } + } + } + drawVerticalScrollbar(DEFAULT_SCROLLBAR_X + 4, 7, LCD_H - 15, menuVerticalOffset, (index + 1) / 2, 4); + + // Footer + lcd->drawBitmap(5, LCD_H-FH-20, modelselSdFreeBitmap); + uint32_t size = sdGetSize() / 100; + lcdDrawNumber(22, LCD_H-FH-21, size, PREC1|SMLSIZE, 0, NULL, "GB"); + lcd->drawBitmap(70, LCD_H-FH-20, modelselModelQtyBitmap); + lcdDrawNumber(92, LCD_H-FH-21, modelslist.getModelsCount(),SMLSIZE); + + return true; +} diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index d5a6981ee..3d15d3fc1 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -61,7 +61,7 @@ class FilePreview : public Window delete bitmap; const char *ext = getFileExtension(filename); if (ext && isExtensionMatching(ext, BITMAPS_EXT)) { - bitmap = BitmapBuffer::load(filename); + bitmap = BitmapBuffer::loadBitmap(filename); } else { bitmap = nullptr; } diff --git a/radio/src/gui/colorlcd/splash.cpp b/radio/src/gui/colorlcd/splash.cpp index 54170957a..569f2f8c6 100644 --- a/radio/src/gui/colorlcd/splash.cpp +++ b/radio/src/gui/colorlcd/splash.cpp @@ -36,7 +36,7 @@ void drawSplash() bool sd_mounted = sdMounted(); if (!sd_mounted) sdInit(); - splashImg = BitmapBuffer::load(BITMAPS_PATH "/" SPLASH_FILE); + splashImg = BitmapBuffer::loadBitmap(BITMAPS_PATH "/" SPLASH_FILE); loadImgFromSD = false; if (!sd_mounted) sdDone(); diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index 2a51c5bfb..e6ea0031c 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -61,11 +61,11 @@ void ThemeBase::init() const void ThemeBase::load() const { if (!asterisk) - asterisk = BitmapBuffer::load(getFilePath("asterisk.bmp")); + asterisk = BitmapBuffer::loadBitmap(getFilePath("asterisk.bmp")); if (!question) - question = BitmapBuffer::load(getFilePath("question.bmp")); + question = BitmapBuffer::loadBitmap(getFilePath("question.bmp")); if (!busy) - busy = BitmapBuffer::load(getFilePath("busy.bmp")); + busy = BitmapBuffer::loadBitmap(getFilePath("busy.bmp")); } ZoneOptionValue * ThemeBase::getOptionValue(unsigned int index) const @@ -88,7 +88,7 @@ void ThemeBase::drawThumb(BitmapBuffer * dc, coord_t x, coord_t y, uint32_t flag #define THUMB_WIDTH 51 #define THUMB_HEIGHT 31 if (!thumb) { - thumb = BitmapBuffer::load(getFilePath("thumb.bmp")); + thumb = BitmapBuffer::loadBitmap(getFilePath("thumb.bmp")); } lcd->drawBitmap(x, y, thumb); if (flags == LINE_COLOR) { diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index 021760e14..23607b817 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -206,22 +206,22 @@ class FlyskyTheme: public ThemeBase { // Calibration screen delete calibStick; - calibStick = BitmapBuffer::load(getFilePath("stick_pointer.png")); + calibStick = BitmapBuffer::loadBitmap(getFilePath("stick_pointer.png")); delete calibStickBackground; - calibStickBackground = BitmapBuffer::load(getFilePath("stick_background.png")); + calibStickBackground = BitmapBuffer::loadBitmap(getFilePath("stick_background.png")); delete calibTrackpBackground; - calibTrackpBackground = BitmapBuffer::load(getFilePath("trackp_background.png")); + calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); delete calibRadioPict; - calibRadioPict = BitmapBuffer::load(getFilePath("NV14.bmp")); + calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("NV14.bmp")); // Model Selection screen delete modelselIconBitmap; modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::load(getFilePath("modelsel/icon_default.png")); + BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); modelselIconBitmap->drawBitmap(20, 8, bitmap); delete bitmap; } @@ -242,7 +242,7 @@ class FlyskyTheme: public ThemeBase modelselModelMoveIcon = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveico.png")); delete modelselWizardBackground; - modelselWizardBackground = BitmapBuffer::load(getFilePath("wizard/background.png")); + modelselWizardBackground = BitmapBuffer::loadBitmap(getFilePath("wizard/background.png")); // Channels monitor screen delete chanMonLockedBitmap; @@ -297,7 +297,7 @@ class FlyskyTheme: public ThemeBase loadColors(); ThemeBase::load(); if (!backgroundBitmap) { - backgroundBitmap = BitmapBuffer::load(getFilePath("background.png")); + backgroundBitmap = BitmapBuffer::loadBitmap(getFilePath("background.png")); } update(); } diff --git a/radio/src/gui/colorlcd/themes/frsky.cpp b/radio/src/gui/colorlcd/themes/frsky.cpp index aff09b353..936dd67c9 100644 --- a/radio/src/gui/colorlcd/themes/frsky.cpp +++ b/radio/src/gui/colorlcd/themes/frsky.cpp @@ -177,13 +177,13 @@ class FrskyTheme: public ThemeBase { // Calibration screen delete calibStick; - calibStick = BitmapBuffer::load(getFilePath("stick_pointer.png")); + calibStick = BitmapBuffer::loadBitmap(getFilePath("stick_pointer.png")); delete calibStickBackground; - calibStickBackground = BitmapBuffer::load(getFilePath("stick_background.png")); + calibStickBackground = BitmapBuffer::loadBitmap(getFilePath("stick_background.png")); delete calibTrackpBackground; - calibTrackpBackground = BitmapBuffer::load(getFilePath("trackp_background.png")); + calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); delete calibRadioPict; #if defined(PCBX10) @@ -194,14 +194,14 @@ class FrskyTheme: public ThemeBase calibRadioPict = BitmapBuffer::load(getFilePath("X10.bmp")); } #else - calibRadioPict = BitmapBuffer::load(getFilePath("horus.bmp")); + calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("horus.bmp")); #endif // Model Selection screen delete modelselIconBitmap; modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::load(getFilePath("modelsel/icon_default.png")); + BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); modelselIconBitmap->drawBitmap(20, 8, bitmap); delete bitmap; } @@ -222,7 +222,7 @@ class FrskyTheme: public ThemeBase modelselModelMoveIcon = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveico.png")); delete modelselWizardBackground; - modelselWizardBackground = BitmapBuffer::load(getFilePath("wizard/background.png")); + modelselWizardBackground = BitmapBuffer::loadBitmap(getFilePath("wizard/background.png")); // Channels monitor screen delete chanMonLockedBitmap; @@ -277,7 +277,7 @@ class FrskyTheme: public ThemeBase loadColors(); ThemeBase::load(); if (!backgroundBitmap) { - backgroundBitmap = BitmapBuffer::load(getFilePath("background.png")); + backgroundBitmap = BitmapBuffer::loadBitmap(getFilePath("background.png")); } update(); } diff --git a/radio/src/gui/colorlcd/view_main.cpp b/radio/src/gui/colorlcd/view_main.cpp index 08aabf9f1..bf7a85c3b 100644 --- a/radio/src/gui/colorlcd/view_main.cpp +++ b/radio/src/gui/colorlcd/view_main.cpp @@ -23,7 +23,7 @@ #include "menu_radio.h" #include "menu_screen.h" // #include "menu_screens.h" -// #include "model_select.h" + #include "model_select.h" // #include "view_channels.h" // #include "view_statistics.h" #include "opentx.h" @@ -150,12 +150,18 @@ ViewMain::ViewMain(bool icons): return 0; }); -// new FabButton(this, LCD_W - 50, 100, ICON_THEME, -// [=]() -> uint8_t { -// new ScreensMenu(); -// return 0; -// }); + new FabButton(this, LCD_W - 50, 100, ICON_THEME, + [=]() -> uint8_t { + new ScreenMenu(); + return 0; + }); } + + new IconButton(this, {0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH}, ICON_BACK, + [=]() -> uint8_t { + openMenu(); + return 0; + }, BUTTON_NOFOCUS); #endif } @@ -184,46 +190,52 @@ void ViewMain::onEvent(event_t event) case EVT_KEY_LONG(KEY_ENTER): killEvents(event); - Menu * menu = new Menu(); - menu->addLine(STR_MODEL_SELECT, [=]() { - // new ModelselectMenu(); - }); - if (modelHasNotes()) { - menu->addLine(STR_VIEW_NOTES, [=]() { - // TODO - }); - } - menu->addLine(STR_MONITOR_SCREENS, [=]() { - // new ChannelsMonitorMenu(); - }); - menu->addLine(STR_RESET_SUBMENU, [=]() { - Menu * menu = new Menu(); - menu->addLine(STR_RESET_FLIGHT, [=]() { - flightReset(); - }); - menu->addLine(STR_RESET_TIMER1, [=]() { - timerReset(0); - }); - menu->addLine(STR_RESET_TIMER2, [=]() { - timerReset(1); - }); - menu->addLine(STR_RESET_TIMER3, [=]() { - timerReset(2); - }); - menu->addLine(STR_RESET_TELEMETRY, [=]() { - telemetryReset(); - }); - }); - menu->addLine(STR_STATISTICS, [=]() { - // new StatisticsMenu(); - }); - menu->addLine(STR_ABOUT_US, [=]() { - // TODO - }); + openMenu(); + break; } } #endif +void ViewMain::openMenu() +{ + Menu * menu = new Menu(); + menu->addLine(STR_MODEL_SELECT, [=]() { + new ModelselectMenu(); + }); + if (modelHasNotes()) { + menu->addLine(STR_VIEW_NOTES, [=]() { + // TODO + }); + } + menu->addLine(STR_MONITOR_SCREENS, [=]() { + // new ChannelsMonitorMenu(); + }); + menu->addLine(STR_RESET_SUBMENU, [=]() { + Menu * menu = new Menu(); + menu->addLine(STR_RESET_FLIGHT, [=]() { + flightReset(); + }); + menu->addLine(STR_RESET_TIMER1, [=]() { + timerReset(0); + }); + menu->addLine(STR_RESET_TIMER2, [=]() { + timerReset(1); + }); + menu->addLine(STR_RESET_TIMER3, [=]() { + timerReset(2); + }); + menu->addLine(STR_RESET_TELEMETRY, [=]() { + telemetryReset(); + }); + }); + menu->addLine(STR_STATISTICS, [=]() { + // new StatisticsMenu(); + }); + menu->addLine(STR_ABOUT_US, [=]() { + // TODO + }); +} + void ViewMain::checkEvents() { Window::checkEvents(); diff --git a/radio/src/gui/colorlcd/view_main.h b/radio/src/gui/colorlcd/view_main.h index dc1227075..601066375 100644 --- a/radio/src/gui/colorlcd/view_main.h +++ b/radio/src/gui/colorlcd/view_main.h @@ -44,6 +44,7 @@ class ViewMain: public FormWindow { #endif void paint(BitmapBuffer * dc) override; void checkEvents() override; + void openMenu(); }; #endif // _VIEW_MAIN_H_ diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp new file mode 100644 index 000000000..cef111363 --- /dev/null +++ b/radio/src/storage/modelslist.cpp @@ -0,0 +1,572 @@ +/* + * 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 "modelslist.h" +using std::list; + +ModelsList modelslist; + +ModelCell::ModelCell(const char * name) + : buffer(NULL), valid_rfData(false) +{ + strncpy(modelFilename, name, sizeof(modelFilename)); + memset(modelName, 0, sizeof(modelName)); +} + +ModelCell::~ModelCell() +{ + resetBuffer(); +} + +void ModelCell::setModelName(char* name) +{ + zchar2str(modelName, name, LEN_MODEL_NAME); + if (modelName[0] == 0) { + char * tmp; + strncpy(modelName, modelFilename, LEN_MODEL_NAME); + tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); + if (tmp != NULL) + *tmp = 0; + } + + resetBuffer(); +} + +void ModelCell::setModelId(uint8_t moduleIdx, uint8_t id) +{ + modelId[moduleIdx] = id; +} + +void ModelCell::resetBuffer() +{ + if (buffer) { + delete buffer; + buffer = NULL; + } +} + +const BitmapBuffer * ModelCell::getBuffer() +{ + if (!buffer) { + loadBitmap(); + } + return buffer; +} + +void ModelCell::loadBitmap() +{ +/* uint8_t version; + + PACK(struct { + ModelHeader header; + TimerData timers[MAX_TIMERS]; + }) partialmodel; + const char * error = NULL; + + if (strncmp(modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { + memcpy(&partialmodel.header, &g_model.header, sizeof(partialmodel)); + } + else { + error = readModel(modelFilename, (uint8_t *)&partialmodel.header, sizeof(partialmodel), &version); + // LEN_BITMAP_NAME has now 4 bytes more + if (version <= 218) { + memmove(partialmodel.timers, &(partialmodel.header.bitmap[10]), sizeof(TimerData)*MAX_TIMERS); + memclear(&(partialmodel.header.bitmap[10]), 4); + } + } + + if ((modelName[0] == 0) && ! error) + setModelName(partialmodel.header.name); // resets buffer!!! + + buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); + if (buffer == NULL) { + return; + } + buffer->clear(TEXT_BGCOLOR); + + if (error) { + buffer->drawText(5, 2, "(Invalid Model)", TEXT_COLOR); + buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); + } + else { + char timer[LEN_TIMER_STRING]; + buffer->drawSizedText(5, 2, modelName, LEN_MODEL_NAME, SMLSIZE|TEXT_COLOR); + getTimerString(timer, 0); + for (uint8_t i = 0; i < MAX_TIMERS; i++) { + if (partialmodel.timers[i].mode > 0 && partialmodel.timers[i].persistent) { + getTimerString(timer, partialmodel.timers[i].value); + break; + } + } + buffer->drawText(101, 40, timer, TEXT_COLOR); + for (int i=0; i<4; i++) { + buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); + } + GET_FILENAME(filename, BITMAPS_PATH, partialmodel.header.bitmap, ""); + const BitmapBuffer * bitmap = BitmapBuffer::load(filename); + if (bitmap) { + buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32); + delete bitmap; + } + else { + buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); + } + } + buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR);*/ +} + +void ModelCell::save(FIL* file) +{ + f_puts(modelFilename, file); + f_putc('\n', file); +} + +void ModelCell::setRfData(ModelData* model) +{ + for (uint8_t i = 0; i < NUM_MODULES; i++) { + modelId[i] = model->header.modelId[i]; + setRfModuleData(i, &(model->moduleData[i])); + TRACE("<%s/%i> : %X,%X,%X", + strlen(modelName) ? modelName : modelFilename, + i, moduleData[i].type, moduleData[i].rfProtocol, modelId[i]); + } + valid_rfData = true; +} + +void ModelCell::setRfModuleData(uint8_t moduleIdx, ModuleData* modData) +{ + moduleData[moduleIdx].type = modData->type; + if (modData->type != MODULE_TYPE_MULTIMODULE) { + moduleData[moduleIdx].rfProtocol = (uint8_t)modData->rfProtocol; + } + else { + // do we care here about MM_RF_CUSTOM_SELECTED? probably not... + moduleData[moduleIdx].rfProtocol = modData->getMultiProtocol(false); + } +} + +bool ModelCell::fetchRfData() +{ + //TODO: use g_model in case fetching data for current model + // + char buf[256]; + getModelPath(buf, modelFilename); + + FIL file; + uint16_t size; + uint8_t version; + + const char * err = openFile(buf, &file, &size, &version); + if (err || version != EEPROM_VER) return false; + + FSIZE_t start_offset = f_tell(&file); + + UINT read; + if ((f_read(&file, buf, LEN_MODEL_NAME, &read) != FR_OK) || (read != LEN_MODEL_NAME)) + goto error; + + setModelName(buf); + + // 1. fetch modelId: NUM_MODULES @ offsetof(ModelHeader, modelId) + // if (f_lseek(&file, start_offset + offsetof(ModelHeader, modelId)) != FR_OK) + // goto error; + if ((f_read(&file, modelId, NUM_MODULES, &read) != FR_OK) || (read != NUM_MODULES)) + goto error; + + // 2. fetch ModuleData: sizeof(ModuleData)*NUM_MODULES @ offsetof(ModelData, moduleData) + if (f_lseek(&file, start_offset + offsetof(ModelData, moduleData)) != FR_OK) + goto error; + + for(uint8_t i=0; iname, name, sizeof(this->name)); +} + +ModelsCategory::~ModelsCategory() +{ + for (list::iterator it = begin(); it != end(); ++it) { + delete *it; + } +} + + +ModelCell * ModelsCategory::addModel(const char * name) +{ + ModelCell * result = new ModelCell(name); + push_back(result); + return result; +} + +void ModelsCategory::removeModel(ModelCell * model) +{ + delete model; + remove(model); +} + +void ModelsCategory::moveModel(ModelCell * model, int8_t step) +{ + ModelsCategory::iterator current = begin(); + for (; current != end(); current++) { + if (*current == model) { + break; + } + } + + ModelsCategory::iterator new_position = current; + if (step > 0) { + while (step >= 0 && new_position != end()) { + new_position++; + step--; + } + } + else { + while (step < 0 && new_position != begin()) { + new_position--; + step++; + } + } + + insert(new_position, 1, *current); + erase(current); +} + +void ModelsCategory::save(FIL * file) +{ + f_puts("[", file); + f_puts(name, file); + f_puts("]", file); + f_putc('\n', file); + for (list::iterator it = begin(); it != end(); ++it) { + (*it)->save(file); + } +} + +ModelsList::ModelsList() +{ + init(); +} + +ModelsList::~ModelsList() +{ + clear(); +} + +void ModelsList::init() +{ + loaded = false; + currentCategory = NULL; + currentModel = NULL; + modelsCount = 0; +} + +void ModelsList::clear() +{ + for (list::iterator it = categories.begin(); it != categories.end(); ++it) { + delete *it; + } + categories.clear(); + init(); +} + +bool ModelsList::load() +{ + char line[LEN_MODELS_IDX_LINE+1]; + ModelsCategory * category = NULL; + + if (loaded) + return true; + + FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_OPEN_EXISTING | FA_READ); + if (result == FR_OK) { + while (readNextLine(line, LEN_MODELS_IDX_LINE)) { + int len = strlen(line); // TODO could be returned by readNextLine + if (len > 2 && line[0] == '[' && line[len-1] == ']') { + line[len-1] = '\0'; + category = new ModelsCategory(&line[1]); + categories.push_back(category); + } + else if (len > 0) { + + //char* rf_data_str = cutModelFilename(line); + ModelCell * model = new ModelCell(line); + if (!category) { + category = new ModelsCategory("Models"); + categories.push_back(category); + } + category->push_back(model); + if (!strncmp(line, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME)) { + currentCategory = category; + currentModel = model; + } + //parseModulesData(model, rf_data_str); + //TRACE("model=<%s>, valid_rfData=<%i>",model->modelFilename,model->valid_rfData); + model->fetchRfData(); + modelsCount += 1; + } + } + f_close(&file); + + if (!getCurrentModel()) { + TRACE("currentModel is NULL"); + } + } + + if (categories.size() == 0) { + category = new ModelsCategory("Models"); + categories.push_back(category); + } + + loaded = true; + return true; +} + +void ModelsList::save() +{ + FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_CREATE_ALWAYS | FA_WRITE); + if (result != FR_OK) { + return; + } + + for (list::iterator it = categories.begin(); it != categories.end(); ++it) { + (*it)->save(&file); + } + + f_close(&file); +} + +void ModelsList::setCurrentCategorie(ModelsCategory* cat) +{ + currentCategory = cat; +} + +void ModelsList::setCurrentModel(ModelCell* cell) +{ + currentModel = cell; + if (!currentModel->valid_rfData) + currentModel->fetchRfData(); +} + +bool ModelsList::readNextLine(char * line, int maxlen) +{ + if (f_gets(line, maxlen, &file) != NULL) { + int curlen = strlen(line) - 1; + if (line[curlen] == '\n') { // remove unwanted chars if file was edited using windows + if (line[curlen - 1] == '\r') { + line[curlen - 1] = 0; + } + else { + line[curlen] = 0; + } + } + return true; + } + return false; +} + +ModelsCategory * ModelsList::createCategory() +{ + ModelsCategory * result = new ModelsCategory("Category"); + categories.push_back(result); + save(); + return result; +} + +ModelCell * ModelsList::addModel(ModelsCategory * category, const char * name) +{ + ModelCell * result = category->addModel(name); + modelsCount++; + save(); + return result; +} + +void ModelsList::removeCategory(ModelsCategory * category) +{ + modelsCount -= category->size(); + delete category; + categories.remove(category); +} + +void ModelsList::removeModel(ModelsCategory * category, ModelCell * model) +{ + category->removeModel(model); + modelsCount--; + save(); +} + +void ModelsList::moveModel(ModelsCategory * category, ModelCell * model, int8_t step) +{ + category->moveModel(model, step); + save(); +} + +void ModelsList::moveModel(ModelCell * model, ModelsCategory * previous_category, ModelsCategory * new_category) +{ + previous_category->remove(model); + new_category->push_back(model); + save(); +} + +bool ModelsList::isModelIdUnique(uint8_t moduleIdx, char* warn_buf, size_t warn_buf_len) +{ + ModelCell* mod_cell = modelslist.getCurrentModel(); + if (!mod_cell || !mod_cell->valid_rfData) { + // in doubt, pretend it's unique + return true; + } + + uint8_t modelId = mod_cell->modelId[moduleIdx]; + uint8_t type = mod_cell->moduleData[moduleIdx].type; + uint8_t rfProtocol = mod_cell->moduleData[moduleIdx].rfProtocol; + + uint8_t additionalOnes = 0; + char* curr = warn_buf; + curr[0] = 0; + + bool hit_found = false; + const std::list& cats = modelslist.getCategories(); + std::list::const_iterator cat_it = cats.begin(); + for (;cat_it != cats.end(); cat_it++) { + for (ModelsCategory::const_iterator it = (*cat_it)->begin(); it != (*cat_it)->end(); it++) { + if (mod_cell == *it) + continue; + + if (!(*it)->valid_rfData) + continue; + + if ((type != MODULE_TYPE_NONE) && + (type == (*it)->moduleData[moduleIdx].type) && + (rfProtocol == (*it)->moduleData[moduleIdx].rfProtocol) && + (modelId == (*it)->modelId[moduleIdx])) { + + // Hit found! + hit_found = true; + + const char* modelName = (*it)->modelName; + const char* modelFilename = (*it)->modelFilename; + + // you cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 (-2 for the ",") + if ((warn_buf_len - 2 - (curr - warn_buf)) > LEN_MODEL_NAME) { + if (warn_buf[0] != 0) + curr = strAppend(curr, ", "); + if (modelName[0] == 0) { + size_t len = min(strlen(modelFilename),LEN_MODEL_NAME); + curr = strAppendFilename(curr, modelFilename, len); + } + else + curr = strAppend(curr, modelName, LEN_MODEL_NAME); + } + else { + additionalOnes++; + } + } + } + } + + if (additionalOnes && (warn_buf_len - (curr - warn_buf) >= 7)) { + curr = strAppend(curr, " (+"); + curr = strAppendUnsigned(curr, additionalOnes); + curr = strAppend(curr, ")"); + } + + return !hit_found; +} + +uint8_t ModelsList::findNextUnusedModelId(uint8_t moduleIdx) +{ + ModelCell* mod_cell = modelslist.getCurrentModel(); + if (!mod_cell || !mod_cell->valid_rfData) { + return 0; + } + + uint8_t type = mod_cell->moduleData[moduleIdx].type; + uint8_t rfProtocol = mod_cell->moduleData[moduleIdx].rfProtocol; + + // assume 63 is the highest Model ID + // and use 64 bits + uint8_t usedModelIds[8]; + memset(usedModelIds, 0, sizeof(usedModelIds)); + + const std::list& cats = modelslist.getCategories(); + std::list::const_iterator cat_it = cats.begin(); + for (;cat_it != cats.end(); cat_it++) { + for (ModelsCategory::const_iterator it = (*cat_it)->begin(); it != (*cat_it)->end(); it++) { + if (mod_cell == *it) + continue; + + if (!(*it)->valid_rfData) + continue; + + // match module type and RF protocol + if ((type != MODULE_TYPE_NONE) && + (type == (*it)->moduleData[moduleIdx].type) && + (rfProtocol == (*it)->moduleData[moduleIdx].rfProtocol)) { + + uint8_t id = (*it)->modelId[moduleIdx]; + + uint8_t mask = 1; + for (uint8_t i = 1; i < (id & 7); i++) + mask <<= 1; + + usedModelIds[id >> 3] |= mask; + } + } + } + + uint8_t new_id = 1; + uint8_t tst_mask = 1; + for (;new_id < getMaxRxNum(moduleIdx); new_id++) { + if (!(usedModelIds[new_id >> 3] & tst_mask)) { + // found free ID + return new_id; + } + if ((tst_mask <<= 1) == 0) + tst_mask = 1; + } + + // failed finding something... + return 0; +} + +void ModelsList::onNewModelCreated(ModelCell* cell, ModelData* model) +{ + cell->setModelName(model->header.name); + cell->setRfData(model); + + uint8_t new_id = findNextUnusedModelId(INTERNAL_MODULE); + model->header.modelId[INTERNAL_MODULE] = new_id; + cell->setModelId(INTERNAL_MODULE, new_id); +} diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h new file mode 100644 index 000000000..fa0adb540 --- /dev/null +++ b/radio/src/storage/modelslist.h @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#ifndef _MODELSLIST_H_ +#define _MODELSLIST_H_ + +#include +#include +#include "sdcard.h" + +#define MODELCELL_WIDTH 172 +#define MODELCELL_HEIGHT 59 + +// modelXXXXXXX.bin F,FF F,3F,FF\r\n +#define LEN_MODELS_IDX_LINE (LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n")-1) + +struct SimpleModuleData +{ + uint8_t type; + uint8_t rfProtocol; +}; + +class ModelCell +{ +public: + char modelFilename[LEN_MODEL_FILENAME+1]; + char modelName[LEN_MODEL_NAME+1]; + BitmapBuffer * buffer; + + bool valid_rfData; + uint8_t modelId[NUM_MODULES]; + SimpleModuleData moduleData[NUM_MODULES]; + + ModelCell(const char * name); + ~ModelCell(); + + void save(FIL* file); + + void setModelName(char* name); + void setRfData(ModelData* model); + + void setModelId(uint8_t moduleIdx, uint8_t id); + void setRfModuleData(uint8_t moduleIdx, ModuleData* modData); + + bool fetchRfData(); + void loadBitmap(); + const BitmapBuffer * getBuffer(); + void resetBuffer(); +}; + +class ModelsCategory: public std::list +{ +public: + char name[LEN_MODEL_FILENAME+1]; + + ModelsCategory(const char * name); + ~ModelsCategory(); + + ModelCell * addModel(const char * name); + void removeModel(ModelCell * model); + void moveModel(ModelCell * model, int8_t step); + void save(FIL * file); +}; + +class ModelsList +{ + bool loaded; + std::list categories; + ModelsCategory * currentCategory; + ModelCell * currentModel; + unsigned int modelsCount; + + void init(); + +public: + + ModelsList(); + ~ModelsList(); + + bool load(); + void save(); + void clear(); + + const std::list & getCategories() const + { + return categories; + } + + void setCurrentCategorie(ModelsCategory * cat); + + ModelsCategory * getCurrentCategory() const + { + return currentCategory; + } + + void setCurrentModel(ModelCell * cell); + + ModelCell * getCurrentModel() const + { + return currentModel; + } + + unsigned int getModelsCount() const + { + return modelsCount; + } + + bool readNextLine(char * line, int maxlen); + + ModelsCategory * createCategory(); + void removeCategory(ModelsCategory * category); + + ModelCell * addModel(ModelsCategory * category, const char * name); + void removeModel(ModelsCategory * category, ModelCell * model); + void moveModel(ModelsCategory * category, ModelCell * model, int8_t step); + void moveModel(ModelCell * model, ModelsCategory * previous_category, ModelsCategory * new_category); + + bool isModelIdUnique(uint8_t moduleIdx, char* warn_buf, size_t warn_buf_len); + uint8_t findNextUnusedModelId(uint8_t moduleIdx); + + void onNewModelCreated(ModelCell* cell, ModelData* model); + +protected: + FIL file; + +}; + +extern ModelsList modelslist; + +#endif // _MODELSLIST_H_ diff --git a/radio/src/storage/sdcard_raw.cpp b/radio/src/storage/sdcard_raw.cpp index 428620a5b..70f960a53 100644 --- a/radio/src/storage/sdcard_raw.cpp +++ b/radio/src/storage/sdcard_raw.cpp @@ -19,7 +19,7 @@ */ #include "opentx.h" -#include "gui/colorlcd/modelslist.h" +#include "modelslist.h" #include "conversions/conversions.h" void getModelPath(char * path, const char * filename) @@ -231,11 +231,10 @@ void storageReadAll() // Wipe models list in case // it's being reloaded after USB connection - #warning "TODO modelslist.clear() + load()" - // modelslist.clear(); + modelslist.clear(); // and reload the list - // modelslist.load(); + modelslist.load(); } void storageCreateModelsList() diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 6b1e38e2f..bb1ecbfae 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 6b1e38e2f96e95b93e0ce14f47fbe0aa79228fc3 +Subproject commit bb1ecbfae4da55a335aaacde312dbffa5d04f5fc