[Horus] Model selection screen refactoring
This commit is contained in:
@@ -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 <list>
|
||||
#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<ModelCell *>
|
||||
{
|
||||
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<ModelCell *>::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<ModelsCategory *>::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<ModelsCategory *>::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<ModelsCategory *> categories;
|
||||
|
||||
protected:
|
||||
FIL file;
|
||||
ModelsCategory * currentCategory;
|
||||
|
||||
};
|
||||
@@ -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)
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user