From f75fedcb0dccacf053df8e7b05fdefdf037ba677 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 9 Mar 2016 07:06:36 +0100 Subject: [PATCH] [Horus] Model selection screen refactoring --- radio/src/gui/horus/bitmapbuffer.h | 9 +- radio/src/gui/horus/lcd.cpp | 5 - radio/src/gui/horus/lcd.h | 7 +- radio/src/gui/horus/menu_model_select.cpp | 221 ++++++++++----------- radio/src/gui/horus/themes/default.cpp | 8 +- radio/src/sdcard.cpp | 4 +- radio/src/sdcard.h | 5 + radio/src/storage/modelslist.h | 226 ++++++++++++++++++++++ radio/src/storage/sdcard_raw.cpp | 225 +-------------------- radio/src/storage/sdcard_raw.h | 23 +-- radio/src/translations.cpp | 1 + radio/src/translations.h | 5 +- radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 1 + radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + 23 files changed, 375 insertions(+), 375 deletions(-) create mode 100644 radio/src/storage/modelslist.h diff --git a/radio/src/gui/horus/bitmapbuffer.h b/radio/src/gui/horus/bitmapbuffer.h index 430e44b72..913d39e28 100644 --- a/radio/src/gui/horus/bitmapbuffer.h +++ b/radio/src/gui/horus/bitmapbuffer.h @@ -110,9 +110,9 @@ class BitmapBuffer: public BitmapBufferBase this->format = format; } - inline void clear() + inline void clear(LcdFlags flags=0) { - drawSolidFilledRect(0, 0, width, height, 0); + drawSolidFilledRect(0, 0, width, height, flags); } inline void drawPixel(display_t * p, display_t value) @@ -178,6 +178,11 @@ class BitmapBuffer: public BitmapBufferBase void drawFontPattern(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags); + void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags) + { + drawSizedText(x, y, s, 255, flags); + } + void drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags); template diff --git a/radio/src/gui/horus/lcd.cpp b/radio/src/gui/horus/lcd.cpp index 0f641e18f..df1dd9f48 100644 --- a/radio/src/gui/horus/lcd.cpp +++ b/radio/src/gui/horus/lcd.cpp @@ -104,11 +104,6 @@ int getTextWidth(const char * s, int len, LcdFlags flags) return result; } -void lcdDrawText(coord_t x, coord_t y, const pm_char * s, LcdFlags flags) -{ - lcdDrawSizedText(x, y, s, 255, flags); -} - void lcdDrawTextAtIndex(coord_t x, coord_t y, const pm_char * s, uint8_t idx, LcdFlags flags) { uint8_t length; diff --git a/radio/src/gui/horus/lcd.h b/radio/src/gui/horus/lcd.h index 7470a9d48..d38de74d6 100644 --- a/radio/src/gui/horus/lcd.h +++ b/radio/src/gui/horus/lcd.h @@ -93,7 +93,7 @@ extern display_t displayBuf[DISPLAY_BUFFER_SIZE]; extern coord_t lcdNextPos; void lcdDrawChar(coord_t x, coord_t y, const unsigned char c, LcdFlags attr=0); -void lcdDrawText(coord_t x, coord_t y, const pm_char * s, LcdFlags attr=0); + void lcdDrawTextAtIndex(coord_t x, coord_t y, const pm_char * s, uint8_t idx, LcdFlags attr=0); inline void lcdClear() @@ -101,6 +101,11 @@ inline void lcdClear() lcd->clear(); } +inline void lcdDrawText(coord_t x, coord_t y, const char * s, LcdFlags attr=0) +{ + lcd->drawText(x, y, s, attr); +} + inline void lcdDrawSizedText(coord_t x, coord_t y, const pm_char * s, uint8_t len, LcdFlags attr=0) { lcd->drawSizedText(x, y, s, len, attr); diff --git a/radio/src/gui/horus/menu_model_select.cpp b/radio/src/gui/horus/menu_model_select.cpp index dcdf5508f..4bfb3ac05 100644 --- a/radio/src/gui/horus/menu_model_select.cpp +++ b/radio/src/gui/horus/menu_model_select.cpp @@ -19,10 +19,9 @@ */ #include "opentx.h" +#include "storage/modelslist.h" #define CATEGORIES_WIDTH 140 -#define MODELCELL_WIDTH 153 -#define MODELCELL_HEIGHT 61 enum ModelSelectMode { MODE_SELECT_CATEGORY, @@ -31,9 +30,11 @@ enum ModelSelectMode { }; uint8_t selectMode; -uint8_t currentCategory; -char selectedFilename[LEN_MODEL_FILENAME+1]; -char selectedCategory[LEN_MODEL_FILENAME+1]; +ModelsList modelslist; + +ModelsCategory * currentCategory; +int currentCategoryIndex; +ModelCell * currentModel; void drawCategory(coord_t y, const char * name, bool selected) { @@ -47,50 +48,46 @@ void drawCategory(coord_t y, const char * name, bool selected) lcdDrawText(MENUS_MARGIN_LEFT, y, name, MENU_TITLE_COLOR); } -void drawModel(coord_t x, coord_t y, const char * name, bool selected, bool current) +void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selected) { - ModelHeader header; - const char * error = readModel(name, (uint8_t *)&header, sizeof(header)); - if (error) { - lcdDrawText(x+5, y+2, "(Invalid Model)", TEXT_COLOR); - lcdDrawBitmapPattern(x+5, y+23, LBM_LIBRARY_SLOT, TEXT_COLOR); - } - else { - lcdDrawSizedText(x+5, y+2, header.name, LEN_MODEL_NAME, ZCHAR|TEXT_COLOR); - putsTimer(x+104, y+41, 0, TEXT_COLOR|LEFT); - for (int i=0; i<4; i++) { - lcdDrawBitmapPattern(x+104+i*11, y+25, LBM_SCORE0, TITLE_BGCOLOR); - } - GET_FILENAME(filename, BITMAPS_PATH, header.bitmap, BITMAPS_EXT); - const BitmapBuffer * bitmap = BitmapBuffer::load(filename); - if (bitmap) { - lcd->drawScaledBitmap(bitmap, x+5, y+24, 64, 32); - delete bitmap; - } - else { - lcdDrawBitmapPattern(x+5, y+23, LBM_LIBRARY_SLOT, TEXT_COLOR); - } - } - lcdDrawSolidHorizontalLine(x+5, y+19, 143, LINE_COLOR); + lcd->drawBitmap(x+1, y+1, &model->buffer); if (current) { - lcdDrawBitmapPattern(x+66, y+42, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); + lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); } if (selected) { - lcdDrawSolidRect(x, y, MODELCELL_WIDTH, MODELCELL_HEIGHT, 1, TITLE_BGCOLOR); - drawShadow(x, y, MODELCELL_WIDTH, MODELCELL_HEIGHT); + lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR); + drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); } } +#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) + +void setCurrentCategory(unsigned int index) +{ + currentCategoryIndex = index; + std::list::iterator it = modelslist.categories.begin(); + std::advance(it, index); + currentCategory = *it; +} + +void setCurrentModel(unsigned int index) +{ + std::list::iterator it = currentCategory->begin(); + std::advance(it, MODEL_INDEX()); + currentModel = *it; +} + void onCategorySelectMenu(const char * result) { if (result == STR_CREATE_MODEL) { storageCheck(true); - createModel(currentCategory); + currentModel = modelslist.addModel(currentCategory, createModel()); selectMode = MODE_SELECT_MODEL; - menuVerticalPosition = 255; + menuVerticalPosition = currentCategory->size() - 1; } else if (result == STR_CREATE_CATEGORY) { - storageInsertCategory("Category", -1); + currentCategory = modelslist.createCategory(); + menuVerticalPosition = currentCategoryIndex = modelslist.categories.size() - 1; } else if (result == STR_RENAME_CATEGORY) { selectMode = MODE_RENAME_CATEGORY; @@ -98,14 +95,15 @@ void onCategorySelectMenu(const char * result) editNameCursorPos = 0; } else if (result == STR_DELETE_CATEGORY) { - storageRemoveCategory(currentCategory--); + modelslist.removeCategory(currentCategory); + setCurrentCategory(currentCategoryIndex-1); } } void onModelSelectMenu(const char * result) { if (result == STR_SELECT_MODEL) { - memcpy(g_eeGeneral.currModelFilename, selectedFilename, LEN_MODEL_FILENAME); + memcpy(g_eeGeneral.currModelFilename, currentModel->name, LEN_MODEL_FILENAME); storageDirty(EE_GENERAL); storageCheck(true); loadModel(g_eeGeneral.currModelFilename); @@ -113,35 +111,39 @@ void onModelSelectMenu(const char * result) } else if (result == STR_DELETE_MODEL) { POPUP_CONFIRMATION(STR_DELETEMODEL); - SET_WARNING_INFO(selectedFilename, LEN_MODEL_FILENAME, 0); + SET_WARNING_INFO(currentModel->name, LEN_MODEL_FILENAME, 0); + } + else if (result == STR_CHANGE_CATEGORY) { + } else if (result == STR_DUPLICATE_MODEL) { char duplicatedFilename[LEN_MODEL_FILENAME+1]; - memcpy(duplicatedFilename, selectedFilename, sizeof(duplicatedFilename)); + memcpy(duplicatedFilename, currentModel->name, sizeof(duplicatedFilename)); if (findNextFileIndex(duplicatedFilename, MODELS_PATH)) { - sdCopyFile(selectedFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); - storageInsertModel(duplicatedFilename, currentCategory, -1); + sdCopyFile(currentModel->name, MODELS_PATH, duplicatedFilename, MODELS_PATH); + modelslist.addModel(currentCategory, duplicatedFilename); + menuVerticalPosition = currentCategory->size() - 1; + setCurrentModel(menuVerticalPosition); } else { - POPUP_WARNING("Invalid Filename"); + POPUP_WARNING("Invalid File"); } } } -#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) - bool menuModelSelect(evt_t event) { if (warningResult) { warningResult = 0; int modelIndex = MODEL_INDEX(); - storageRemoveModel(currentCategory, modelIndex); + modelslist.removeModel(currentCategory, currentModel); s_copyMode = 0; event = EVT_REFRESH; if (modelIndex > 0) { modelIndex--; menuVerticalPosition = modelIndex / 2; menuHorizontalPosition = modelIndex & 1; + setCurrentModel(modelIndex); } } @@ -152,7 +154,10 @@ bool menuModelSelect(evt_t event) case EVT_ENTRY: selectMode = MODE_SELECT_CATEGORY; - currentCategory = menuVerticalPosition = 0; + modelslist.load(); + menuVerticalPosition = 0; + setCurrentCategory(0); + setCurrentModel(0); break; case EVT_KEY_FIRST(KEY_EXIT): @@ -162,7 +167,7 @@ bool menuModelSelect(evt_t event) return false; case MODE_SELECT_MODEL: selectMode = MODE_SELECT_CATEGORY; - menuVerticalPosition = currentCategory; + menuVerticalPosition = currentCategoryIndex; break; } break; @@ -173,6 +178,7 @@ bool menuModelSelect(evt_t event) case MODE_SELECT_CATEGORY: selectMode = MODE_SELECT_MODEL; menuVerticalPosition = 0; + setCurrentModel(menuVerticalPosition); break; } break; @@ -183,7 +189,7 @@ bool menuModelSelect(evt_t event) POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); POPUP_MENU_ADD_ITEM(STR_CREATE_CATEGORY); POPUP_MENU_ADD_ITEM(STR_RENAME_CATEGORY); - if (currentCategory > 0) { + if (currentCategoryIndex > 0) { POPUP_MENU_ADD_ITEM(STR_DELETE_CATEGORY); } popupMenuHandler = onCategorySelectMenu; @@ -191,15 +197,16 @@ bool menuModelSelect(evt_t event) else if (selectMode == MODE_SELECT_MODEL) { killEvents(event); ModelHeader header; - const char * error = readModel(selectedFilename, (uint8_t *)&header, sizeof(header)); + const char * error = readModel(currentModel->name, (uint8_t *)&header, sizeof(header)); if (!error) { - if (strncmp(selectedFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) != 0) { + if (strncmp(currentModel->name, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) != 0) { POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL); } POPUP_MENU_ADD_ITEM(STR_DUPLICATE_MODEL); } // POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); // POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL); + POPUP_MENU_ADD_ITEM(STR_CHANGE_CATEGORY); POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL); // POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); // POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL); @@ -219,85 +226,63 @@ bool menuModelSelect(evt_t event) lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, LCD_W, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR); // Categories - StorageModelsList storage; - const char * error = storageOpenModelsList(&storage); - if (!error) { - bool result = true; - coord_t y = MENU_HEADER_HEIGHT+10; - int index = 0; - while (1) { - char line[LEN_MODEL_FILENAME+1]; - result = storageReadNextCategory(&storage, line, LEN_MODEL_FILENAME); - if (!result) - break; - if (y < LCD_H) { - if (selectMode == MODE_RENAME_CATEGORY && currentCategory == index) { - lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH, INVERT_LINE_HEIGHT, TEXT_BGCOLOR); - editName(MENUS_MARGIN_LEFT, y, selectedCategory, LEN_MODEL_FILENAME, event, 1, 0); - if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) { - storageRenameCategory(currentCategory, selectedCategory); - selectMode = MODE_SELECT_CATEGORY; - putEvent(EVT_REFRESH); - } - } - else { - if (currentCategory == index) { - memset(selectedCategory, 0, sizeof(selectedCategory)); - strncpy(selectedCategory, line, sizeof(selectedCategory)); - } - drawCategory(y, line, currentCategory==index); + coord_t y = MENU_HEADER_HEIGHT+4; + for (std::list::iterator it = modelslist.categories.begin(); it != modelslist.categories.end(); ++it, y+=FH) { + if (y < MENU_FOOTER_TOP - 5) { + if (selectMode == MODE_RENAME_CATEGORY && currentCategory == *it) { + lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH, INVERT_LINE_HEIGHT, TEXT_BGCOLOR); + editName(MENUS_MARGIN_LEFT, y, currentCategory->name, LEN_MODEL_FILENAME, event, 1, 0); + if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) { + // storageRenameCategory(currentCategory, selectedCategory); + selectMode = MODE_SELECT_CATEGORY; + putEvent(EVT_REFRESH); } } - y += FH; - index++; + else { + drawCategory(y, (*it)->name, currentCategory==*it); + } } - if (selectMode == MODE_SELECT_CATEGORY) { - if (navigate(event, index, 9)) { - putEvent(EVT_REFRESH); - currentCategory = menuVerticalPosition; - } + } + + if (selectMode == MODE_SELECT_CATEGORY) { + if (navigate(event, modelslist.categories.size(), 9)) { + putEvent(EVT_REFRESH); + setCurrentCategory(menuVerticalPosition); } } // Models - if (!error) { - bool result = storageSeekCategory(&storage, currentCategory); - coord_t y = MENU_HEADER_HEIGHT+7; - int count = 0; - while (result) { - char line[LEN_MODEL_FILENAME+1]; - result = storageReadNextModel(&storage, line, LEN_MODEL_FILENAME); - if (!result) { - break; + int index = 0; + y = MENU_HEADER_HEIGHT+7; + for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { + if (index >= menuVerticalOffset*2 && index < (menuVerticalOffset+3)*2) { + bool selected = (selectMode==MODE_SELECT_MODEL && index==menuVerticalPosition*2+menuHorizontalPosition); + bool current = !strncmp((*it)->name, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); + if (index & 1) { + drawModel(CATEGORIES_WIDTH+MENUS_MARGIN_LEFT+162, y, *it, current, selected); + y += 66; } - if (count >= menuVerticalOffset*2 && count < (menuVerticalOffset+3)*2) { - bool selected = (selectMode==MODE_SELECT_MODEL && menuVerticalPosition*2+menuHorizontalPosition==count); - bool current = (strncmp(line, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0); - if (count & 1) { - drawModel(CATEGORIES_WIDTH+MENUS_MARGIN_LEFT+162, y, line, selected, current); - y += 66; - } - else { - drawModel(CATEGORIES_WIDTH+MENUS_MARGIN_LEFT+1, y, line, selected, current); - } - if (selected) { - memcpy(selectedFilename, line, sizeof(selectedFilename)); - } - } - count++; - } - if (selectMode == MODE_SELECT_MODEL) { - if (count == 0) { - selectMode = MODE_SELECT_CATEGORY; - menuVerticalPosition = currentCategory; - putEvent(EVT_REFRESH); - } - else if (navigate(event, count, 3, 2)) { - putEvent(EVT_REFRESH); + else { + drawModel(CATEGORIES_WIDTH+MENUS_MARGIN_LEFT+1, y, *it, current, selected); } } - drawVerticalScrollbar(DEFAULT_SCROLLBAR_X, MENU_HEADER_HEIGHT+7, MENU_FOOTER_TOP-MENU_HEADER_HEIGHT-15, menuVerticalOffset, (count+1)/2, 3); } + if (selectMode == MODE_SELECT_MODEL) { + if (index == 0) { + selectMode = MODE_SELECT_CATEGORY; + menuVerticalPosition = currentCategoryIndex; + putEvent(EVT_REFRESH); + } + else if (navigate(event, index, 3, 2)) { + putEvent(EVT_REFRESH); + setCurrentModel(MODEL_INDEX()); + } + if (currentModel) { + lcd->drawText(CATEGORIES_WIDTH+MENUS_MARGIN_LEFT, MENU_FOOTER_TOP+2, currentModel->name, TEXT_INVERTED_COLOR); + } + } + + drawVerticalScrollbar(DEFAULT_SCROLLBAR_X, MENU_HEADER_HEIGHT+7, MENU_FOOTER_TOP-MENU_HEADER_HEIGHT-15, menuVerticalOffset, (index+1)/2, 3); return true; } diff --git a/radio/src/gui/horus/themes/default.cpp b/radio/src/gui/horus/themes/default.cpp index 5667e8887..d92463d70 100644 --- a/radio/src/gui/horus/themes/default.cpp +++ b/radio/src/gui/horus/themes/default.cpp @@ -35,9 +35,10 @@ class DefaultTheme: public Theme DefaultTheme(): Theme("Default", OPTIONS_THEME_DEFAULT) { + loadColors(); } - virtual void load() const + void loadColors() const { lcdColorTable[TEXT_COLOR_INDEX] = BLACK; lcdColorTable[TEXT_BGCOLOR_INDEX] = WHITE; @@ -64,9 +65,12 @@ class DefaultTheme: public Theme lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = RED; lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RED; lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK; + } + virtual void load() const + { + loadColors(); Theme::load(); - if (!backgroundBitmap) backgroundBitmap = BitmapBuffer::load(getThemePath("mainbg.bmp")); if (!aboutBackgroundBitmap) aboutBackgroundBitmap = BitmapBuffer::load(getThemePath("aboutbg.bmp")); } diff --git a/radio/src/sdcard.cpp b/radio/src/sdcard.cpp index 372cbfdca..e8517766a 100644 --- a/radio/src/sdcard.cpp +++ b/radio/src/sdcard.cpp @@ -60,11 +60,13 @@ char * getFileIndex(char * filename, unsigned int & value) char * pos = getFileExtension(filename); if (!pos || pos == filename) return NULL; + int multiplier = 1; while (pos > filename) { pos--; char c = *pos; if (c >= '0' && c <= '9') { - value = (value * 10) + (c - '\0'); + value += multiplier * (c - '0'); + multiplier *= 10; } else { return pos+1; diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index eb18ef517..ae722e328 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -45,6 +45,11 @@ #define SCRIPTS_FUNCS_PATH SCRIPTS_PATH "/FUNCTIONS" #define SCRIPTS_TELEM_PATH SCRIPTS_PATH "/TELEMETRY" +#if defined(COLORLCD) +const char RADIO_MODELSLIST_PATH[] = RADIO_PATH "/models.txt"; +const char RADIO_SETTINGS_PATH[] = RADIO_PATH "/radio.bin"; +#endif + #define MODELS_EXT ".bin" #define LOGS_EXT ".csv" #define SOUNDS_EXT ".wav" diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h new file mode 100644 index 000000000..3a5e83b2e --- /dev/null +++ b/radio/src/storage/modelslist.h @@ -0,0 +1,226 @@ +/* + * 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 +#include "sdcard.h" + +#define MODELCELL_WIDTH 151 +#define MODELCELL_HEIGHT 59 + +class ModelCell +{ + public: + ModelCell(const char * name): + buffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT) + { + buffer.clear(TEXT_BGCOLOR); + + strncpy(this->name, name, sizeof(this->name)); + + ModelHeader header; + const char * error = readModel(name, (uint8_t *)&header, sizeof(header)); + if (error) { + buffer.drawText(5, 2, "(Invalid Model)", TEXT_COLOR); + buffer.drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); + } + else { + buffer.drawSizedText(5, 2, header.name, LEN_MODEL_NAME, ZCHAR|TEXT_COLOR); + // TODO putsTimer(x+104, y+41, 0, TEXT_COLOR|LEFT); + for (int i=0; i<4; i++) { + buffer.drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); + } + GET_FILENAME(filename, BITMAPS_PATH, header.bitmap, BITMAPS_EXT); + 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); + + // if (selected) { + // lcdDrawSolidRect(x, y, MODELCELL_WIDTH, MODELCELL_HEIGHT, 1, TITLE_BGCOLOR); + // drawShadow(x, y, MODELCELL_WIDTH, MODELCELL_HEIGHT); + // } + } + + char name[LEN_MODEL_FILENAME+1]; + BitmapBuffer buffer; +}; + +class ModelsCategory: public std::list +{ + public: + ModelsCategory(const char * name) + { + strncpy(this->name, name, sizeof(this->name)); + } + + + ModelCell * addModel(const char * name) + { + ModelCell * result = new ModelCell(name); + push_back(result); + return result; + } + + void removeModel(ModelCell * model) + { + delete model; + remove(model); + } + + void save(FIL * file) + { + f_puts("[", file); + f_puts(name, file); + f_puts("]", file); + f_putc('\n', file); + for (std::list::iterator it = begin(); it != end(); ++it) { + f_puts((*it)->name, file); + f_putc('\n', file); + } + } + + char name[LEN_MODEL_FILENAME+1]; +}; + +class ModelsList +{ + public: + ModelsList() + { + } + + ~ModelsList() + { + clear(); + } + + void clear() + { + for (std::list::iterator it = categories.begin(); it != categories.end(); ++it) { + delete *it; + } + categories.clear(); + } + + bool load() + { + char line[LEN_MODEL_FILENAME+1]; + + clear(); + + FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return false; + } + + while (readNextLine(line, LEN_MODEL_FILENAME)) { + int len = strlen(line); // TODO could be returned by readNextLine + if (len > 2 && line[0] == '[' && line[len-1] == ']') { + line[len-1] = '\0'; + currentCategory = new ModelsCategory(&line[1]); + categories.push_back(currentCategory); + } + else if (len > 0) { + ModelCell * model = new ModelCell(line); + currentCategory->push_back(model); + } + } + + f_close(&file); + return true; + } + + void save() + { + FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_CREATE_ALWAYS | FA_WRITE); + if (result != FR_OK) { + return; + } + + for (std::list::iterator it = categories.begin(); it != categories.end(); ++it) { + (*it)->save(&file); + } + + f_close(&file); + } + + bool readNextLine(char * line, int maxlen) + { + char c; + unsigned int read; + int len=0; + while (1) { + FRESULT result = f_read(&file, (uint8_t *)&c, 1, &read); + if (result != FR_OK || read != 1) { + line[len] = '\0'; + return false; + } + if (c == '\n') { + if (len > 0) { + // we skip empty lines + line[len] = '\0'; + return true; + } + } + else if (c != '\r' && len < maxlen) { + line[len++] = c; + } + } + } + + ModelsCategory * createCategory() + { + ModelsCategory * result = new ModelsCategory("Category"); + categories.push_back(result); + save(); + return result; + } + + ModelCell * addModel(ModelsCategory * category, const char * name) + { + ModelCell * result = category->addModel(name); + save(); + return result; + } + + void removeCategory(ModelsCategory * category) + { + delete category; + categories.remove(category); + } + + void removeModel(ModelsCategory * category, ModelCell * model) + { + category->removeModel(model); + } + + std::list categories; + + protected: + FIL file; + ModelsCategory * currentCategory; + +}; diff --git a/radio/src/storage/sdcard_raw.cpp b/radio/src/storage/sdcard_raw.cpp index 9867ce32d..2f9b29ff9 100644 --- a/radio/src/storage/sdcard_raw.cpp +++ b/radio/src/storage/sdcard_raw.cpp @@ -133,9 +133,6 @@ const char * loadModel(const char * filename) return error; } -const char RADIO_SETTINGS_PATH[] = RADIO_PATH "/radio.bin"; -const char RADIO_MODELSLIST_PATH[] = RADIO_PATH "/models.txt"; - const char * loadGeneralSettings() { const char * error = loadFile(RADIO_SETTINGS_PATH, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral)); @@ -212,224 +209,7 @@ void storageFormat() storageCreateModelsList(); } -const char * storageOpenModelsList(StorageModelsList * storage) -{ - FRESULT result = f_open(&storage->file, RADIO_MODELSLIST_PATH, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - return NULL; -} - -bool storageReadNextLine(StorageModelsList * storage, char * line, int maxlen) -{ - char c; - unsigned int read; - int len=0; - while (1) { - FRESULT result = f_read(&storage->file, (uint8_t *)&c, 1, &read); - if (result != FR_OK || read != 1) { - line[len] = '\0'; - return false; - } - if (c == '\n') { - if (len > 0) { - // we skip empty lines - line[len] = '\0'; - return true; - } - } - else if (c != '\r' && len < maxlen) { - line[len++] = c; - } - } -} - -int storageGetCategoryLength(const char * line) -{ - int len = strlen(line); - if (len > 2 && line[0] == '[' && line[len-1] == ']') { - return len-2; - } - else { - return 0; - } -} - -bool storageReadNextCategory(StorageModelsList * storage, char * line, int maxlen) -{ - bool result = true; - while (result) { - result = storageReadNextLine(storage, line, maxlen); - int len = storageGetCategoryLength(line); - if (len > 0) { - memmove(line, &line[1], len); - line[len] = '\0'; - return result; - } - } - line[0] = '\0'; - return false; -} - -bool storageSeekCategory(StorageModelsList * storage, int category) -{ - f_lseek(&storage->file, 0); - char line[256] = ""; - int result = true; - for (int i=0; result && i<=category; i++) { - result = storageReadNextCategory(storage, line, sizeof(line)-1); - } - return line[0] != '\0'; -} - -bool storageReadNextModel(StorageModelsList * storage, char * line, int maxlen) -{ - bool result = true; - while (result) { - result = storageReadNextLine(storage, line, maxlen); - if (line[0] == '[') - return false; - else if (line[0] != '\0') - return result; - } - line[0] = '\0'; - return false; -} - -enum StorageOperation { - STORAGE_INSERT_MODEL, - STORAGE_REMOVE_MODEL, - STORAGE_RENAME_MODEL, - STORAGE_INSERT_CATEGORY, - STORAGE_RENAME_CATEGORY, - STORAGE_REMOVE_CATEGORY -}; - -const char * storageModifyLine(unsigned int operation, int category, int position, const char * name="") -{ - StorageModelsList storage; - FIL file; - const char * error = storageOpenModelsList(&storage); - if (error) { - return error; - } - - { - FRESULT result = f_open(&file, RADIO_PATH "/models.tmp", FA_CREATE_ALWAYS | FA_WRITE); - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - } - - bool operationdone = false; - bool result = true; - int categoryindex = -1; - int modelindex = 0; - - while (result) { - char line[256]; - result = storageReadNextLine(&storage, line, sizeof(line)-1); - if (!operationdone) { - int len = storageGetCategoryLength(line); - if (len > 0) { - if (category >= 0) { - if (categoryindex == category && operation == STORAGE_INSERT_MODEL) { - f_puts(name, &file); - f_putc('\n', &file); - operationdone = true; - } - categoryindex++; - if (categoryindex == category) { - if (operation == STORAGE_RENAME_CATEGORY) { - f_puts("[", &file); - f_puts(name, &file); - f_puts("]", &file); - f_putc('\n', &file); - operationdone = true; - continue; - } - else if (operation == STORAGE_REMOVE_CATEGORY) { - operationdone = true; - continue; - } - } - } - } - else if (categoryindex == category) { - if (modelindex++ == position) { - operationdone = true; - if (operation == STORAGE_INSERT_MODEL) { - f_puts(name, &file); - f_putc('\n', &file); - } - else if (operation == STORAGE_RENAME_MODEL) { - f_puts(name, &file); - f_putc('\n', &file); - continue; - } - else if (operation == STORAGE_REMOVE_MODEL) { - TRACE("skip %s", line); - continue; - } - } - } - } - f_puts(line, &file); - f_putc('\n', &file); - } - - if (!operationdone) { - if (categoryindex>=0 && operation==STORAGE_INSERT_MODEL) { - f_puts(name, &file); - f_putc('\n', &file); - } - else if (operation==STORAGE_INSERT_CATEGORY) { - f_puts("[", &file); - f_puts(name, &file); - f_puts("]", &file); - f_putc('\n', &file); - } - } - - f_close(&file); - f_unlink(RADIO_PATH "/models.old"); - f_rename(RADIO_MODELSLIST_PATH, RADIO_PATH "/models.old"); - f_rename(RADIO_PATH "/models.tmp", RADIO_MODELSLIST_PATH); - return NULL; -} - -const char * storageInsertCategory(const char * name, int position) -{ - return storageModifyLine(STORAGE_INSERT_CATEGORY, position, -1, name); -} - -const char * storageRenameCategory(int category, const char * name) -{ - return storageModifyLine(STORAGE_RENAME_CATEGORY, category, -1, name); -} - -const char * storageRemoveCategory(int category) -{ - return storageModifyLine(STORAGE_REMOVE_CATEGORY, category, -1); -} - -const char * storageInsertModel(const char * name, int category, int position) -{ - return storageModifyLine(STORAGE_INSERT_MODEL, category, position, name); -} - -const char * storageRemoveModel(int category, int position) -{ - return storageModifyLine(STORAGE_REMOVE_MODEL, category, position); -} - -const char * storageRenameModel(const char * name, int category, int position) -{ - return storageModifyLine(STORAGE_RENAME_MODEL, category, position, name); -} - -const char * createModel(int category) +const char * createModel() { preModelLoad(); @@ -444,11 +224,10 @@ const char * createModel(int category) storageDirty(EE_GENERAL); storageDirty(EE_MODEL); storageCheck(true); - storageInsertModel(filename, category, -1); } postModelLoad(true); - return NULL; + return g_eeGeneral.currModelFilename; } void storageEraseAll(bool warn) diff --git a/radio/src/storage/sdcard_raw.h b/radio/src/storage/sdcard_raw.h index a7f0eb7ce..d5a21b461 100644 --- a/radio/src/storage/sdcard_raw.h +++ b/radio/src/storage/sdcard_raw.h @@ -2,7 +2,7 @@ * Copyright (C) OpenTX * * Based on code named - * th9x - http://code.google.com/p/th9x + * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * @@ -28,25 +28,6 @@ const char * readModel(const char * filename, uint8_t * buffer, uint32_t size); const char * loadModel(const char * filename); -const char * createModel(int category); - - -struct StorageModelsList { - FIL file; -}; - -const char * storageOpenModelsList(StorageModelsList * storage); -bool storageReadNextModel(StorageModelsList * storage, char * line, int maxlen); -const char * storageInsertModel(const char * name, int category, int position); -const char * storageRemoveModel(int category, int position); -const char * storageRenameModel(const char * name, int category, int position); - -const char * storageInsertCategory(const char * name, int position); -const char * storageRenameCategory(int category, const char * name); -const char * storageRemoveCategory(int category); -bool storageReadNextCategory(StorageModelsList * storage, char * line, int maxlen); -bool storageSeekCategory(StorageModelsList * storage, int category); +const char * createModel(); #endif // _SDCARD_RAW_H_ - - diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 894498c89..9912c63e7 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -411,6 +411,7 @@ const pm_char STR_RENAME_CATEGORY[] PROGMEM = TR_RENAME_CATEGORY; const pm_char STR_DELETE_CATEGORY[] PROGMEM = TR_DELETE_CATEGORY; const pm_char STR_CREATE_MODEL[] PROGMEM = TR_CREATE_MODEL; const pm_char STR_DUPLICATE_MODEL[] PROGMEM = TR_DUPLICATE_MODEL; +const pm_char STR_CHANGE_CATEGORY[] PROGMEM = TR_CHANGE_CATEGORY; const pm_char STR_COPY_MODEL[] PROGMEM = TR_COPY_MODEL; const pm_char STR_MOVE_MODEL[] PROGMEM = TR_MOVE_MODEL; const pm_char STR_DELETE_MODEL[] PROGMEM = TR_DELETE_MODEL; diff --git a/radio/src/translations.h b/radio/src/translations.h index 40bf579d5..d679589c7 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -587,9 +587,10 @@ extern const pm_char STR_CURRENT[]; extern const pm_char STR_SELECT_MODEL[]; extern const pm_char STR_CREATE_CATEGORY[]; extern const pm_char STR_RENAME_CATEGORY[]; -extern const pm_char STR_DELETE_CATEGORY[]; + extern const pm_char STR_DELETE_CATEGORY[]; extern const pm_char STR_CREATE_MODEL[]; extern const pm_char STR_DUPLICATE_MODEL[]; + extern const pm_char STR_CHANGE_CATEGORY[]; extern const pm_char STR_COPY_MODEL[]; extern const pm_char STR_MOVE_MODEL[]; extern const pm_char STR_DELETE_MODEL[]; @@ -608,7 +609,7 @@ extern const pm_char STR_DELETE_CATEGORY[]; extern const pm_char STR_RESET_TELEMETRY[]; extern const pm_char STR_STATISTICS[]; extern const pm_char STR_ABOUT_US[]; -extern const pm_char STR_SETUP_SCREENS[]; + extern const pm_char STR_SETUP_SCREENS[]; #endif extern const pm_char STR_RESET_BTN[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 82274b226..af803d1ab 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -811,6 +811,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Nový model" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Kopírovat" #define TR_MOVE_MODEL "Přesunout" #define TR_BACKUP_MODEL "Zálohovat na SD" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 906ecf68c..4d2ebf62a 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -815,6 +815,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Neues Modell" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Kopiere Modell" #define TR_MOVE_MODEL "Verschiebe Modell" #define TR_BACKUP_MODEL "Modell auf SD-Karte" //9XR-Pro diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index d069b009e..5aa317a05 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -836,6 +836,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Create Model" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copy Model" #define TR_MOVE_MODEL "Move Model" #define TR_BACKUP_MODEL "Backup Model" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index a917ff71f..2c621f062 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -767,6 +767,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Crear Modelo" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copiar Modelo" #define TR_MOVE_MODEL "Mover Modelo" #define TR_BACKUP_MODEL "Copia Sgdad Mod." diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 5b624ccd6..4c037d4dd 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -767,6 +767,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Create Model" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copy Model" #define TR_MOVE_MODEL "Move Model" #define TR_BACKUP_MODEL "Backup Model" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 8bde4204e..f0cd22d6d 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -802,6 +802,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Créer Modèle" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copier Modèle" #define TR_MOVE_MODEL "Déplacer Modèle" #define TR_BACKUP_MODEL "Archiver Modèle" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 135c464d1..35a072e23 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -813,6 +813,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Crea Modello" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copia Modello" #define TR_MOVE_MODEL "Sposta Modello" #define TR_BACKUP_MODEL "Salva Modello" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 0a7ac9d08..0ef3b6874 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -832,6 +832,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Nieuw Model" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Kopieer Model" #define TR_MOVE_MODEL "Verplaats Model" #define TR_BACKUP_MODEL "Backup Model" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index dad85fbec..e94ba8818 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -814,6 +814,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Nowy model" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Skopiuj model" #define TR_MOVE_MODEL "Przenieś model" #define TR_BACKUP_MODEL "Zbackupuj model" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 9543da5a2..e31a219a1 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -761,6 +761,7 @@ #define TR_DELETE_CATEGORY "Delete Category" #define TR_CREATE_MODEL "Criar Modelo" #define TR_DUPLICATE_MODEL "Duplicate Model" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Copy Model" #define TR_MOVE_MODEL "Move Model" #define TR_BACKUP_MODEL "Salvar Modelo" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 0a554a408..8cfcabd5c 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -833,6 +833,7 @@ #define TR_DELETE_CATEGORY "Ta Bort Kategori" #define TR_CREATE_MODEL "Skapa Modell" #define TR_DUPLICATE_MODEL "Dupl. Modell" +#define TR_CHANGE_CATEGORY "Change category" #define TR_COPY_MODEL "Kopiera Modell" #define TR_MOVE_MODEL "Flytta Modell" #define TR_BACKUP_MODEL "Modell-backup"