From 08391bba4f4730cd2d0ad46447d9e83caeddb3cb Mon Sep 17 00:00:00 2001 From: furrtek Date: Thu, 22 Jun 2017 09:08:37 +0100 Subject: [PATCH] Support for frequency manager categories (as files) Base class for frequency manager views Menuview clear/add bugfix --- firmware/application/freqman.cpp | 142 ++++------ firmware/application/freqman.hpp | 14 +- firmware/application/ui_freqman.cpp | 365 ++++++++++++------------- firmware/application/ui_freqman.hpp | 152 +++++----- firmware/application/ui_menu.cpp | 72 ++--- firmware/application/ui_menu.hpp | 6 +- firmware/application/ui_navigation.cpp | 2 +- firmware/application/ui_soundboard.cpp | 8 +- 8 files changed, 351 insertions(+), 410 deletions(-) diff --git a/firmware/application/freqman.cpp b/firmware/application/freqman.cpp index 840d3a28..a9c3eea4 100644 --- a/firmware/application/freqman.cpp +++ b/firmware/application/freqman.cpp @@ -23,129 +23,107 @@ #include "freqman.hpp" #include -#define FREQMAN_MAX_PER_CAT 64 -#define FREQMAN_CAT_MAX_LEN 8 -#define FREQMAN_DESC_MAX_LEN 32 +#define FREQMAN_DESC_MAX_LEN 30 +#define FREQMAN_MAX_PER_FILE 256 -bool load_freqman_file(freqman_db &db) { - File freqs_file; - size_t length, span_end, n = 0; - uint64_t seek_pos = 0; - char * line_end; - int32_t category_id; - char * pos; - char desc_buffer[FREQMAN_DESC_MAX_LEN + 1] = { 0 }; - char category_buffer[FREQMAN_CAT_MAX_LEN + 1] = { 0 }; - std::string description; - rf::Frequency value; - std::vector::iterator category_find; +std::vector get_freqman_files() { + std::vector file_list; - char file_buffer[256]; + auto files = scan_root_files(u"FREQMAN", u"*.TXT"); - while (freqs_file.open("freqman.txt").is_valid()) { - auto result = freqs_file.create("freqman.txt"); - if (result.is_valid()) - return false; + for (auto file : files) { + file_list.emplace_back(file.stem().string()); } - freqs_file.read(file_buffer, 256); + return file_list; +}; + +bool load_freqman_file(std::string& file_stem, freqman_db &db) { + File freqman_file; + size_t length, span_end, n = 0; + uint64_t seek_pos = 0; + char * pos; + char * line_end; + std::string description; + rf::Frequency value; + char file_data[256]; - while ((pos = strstr(file_buffer, "f=")) && (n < FREQMAN_MAX_PER_CAT)) { + db.entries.clear(); + + auto result = freqman_file.open("FREQMAN/" + file_stem + ".TXT"); + if (result.is_valid()) + return false; + + freqman_file.read(file_data, 256); + + while ((pos = strstr(file_data, "f=")) && (n < FREQMAN_MAX_PER_FILE)) { - // Cut buffer at end of line - line_end = file_buffer; - do { - span_end = strcspn(line_end, "\x0D\x0A"); - line_end += (span_end + 1); - } while (span_end); + // Trim buffer at end of last complete line + line_end = file_data; + span_end = strcspn(line_end, "\x0A"); + if (span_end) { + if (span_end == strlen(line_end)) + return true; + } + line_end += (span_end + 1); *line_end = (char)0; + // Read frequency pos += 2; - value = strtol(pos, nullptr, 10); // Read frequency + value = strtol(pos, nullptr, 10); - pos = strstr(file_buffer, "d="); + // Read description until , or LF + pos = strstr(file_data, "d="); if (pos) { pos += 2; - length = strcspn(pos, ",\x0D\x0A"); // Read description until , or CR LF - if (length > FREQMAN_DESC_MAX_LEN) length = FREQMAN_DESC_MAX_LEN; - memcpy(desc_buffer, pos, length); - desc_buffer[length] = (char)0; - description = desc_buffer; + length = std::min(strcspn(pos, ",\x0A"), (size_t)FREQMAN_DESC_MAX_LEN); + description = string(pos, length); } else { description = "-"; } - pos = strstr(file_buffer, "c="); - if (pos) { - pos += 2; - length = strcspn(pos, ",\x0D\x0A"); // Read category name until , or CR LF - if (length > FREQMAN_CAT_MAX_LEN) length = FREQMAN_CAT_MAX_LEN; - memcpy(category_buffer, pos, length); - category_buffer[length] = (char)0; - - // See if we already know that category - category_find = find(db.categories.begin(), db.categories.end(), category_buffer); - if (category_find == db.categories.end()) { - // Not found: add to list - db.categories.push_back(category_buffer); - category_id = db.categories.size() - 1; - } else { - // Found - category_id = category_find - db.categories.begin(); - } - } else { - category_id = -1; // Uncategorized - } - - db.entries.push_back({ value, "", description, category_id }); + db.entries.push_back({ value, "", description }); n++; - seek_pos += (line_end - file_buffer); + seek_pos += (line_end - file_data); - if (freqs_file.seek(seek_pos).value() == seek_pos) + if (freqman_file.seek(seek_pos).value() == seek_pos) break; else - freqs_file.read(file_buffer, 256); + freqman_file.read(file_data, 256); } - //chHeapFree(file_buffer); - return true; } -bool save_freqman_file(freqman_db &db) { - File freqs_file; - size_t n; +bool save_freqman_file(std::string& file_stem, freqman_db &db) { + File freqman_file; std::string item_string; - int32_t category_id; - if (!create_freqman_file(freqs_file)) return false; + if (!create_freqman_file(file_stem, freqman_file)) + return false; - for (n = 0; n < db.entries.size(); n++) { + for (size_t n = 0; n < db.entries.size(); n++) { item_string = "f=" + to_string_dec_uint(db.entries[n].value); if (db.entries[n].description.size()) item_string += ",d=" + db.entries[n].description; - category_id = db.entries[n].category_id; - if ((category_id >= 0) && (category_id < (int32_t)db.categories.size())) - item_string += ",c=" + db.categories[db.entries[n].category_id]; - - freqs_file.write_line(item_string); + freqman_file.write_line(item_string); } return true; } -bool create_freqman_file(File &freqs_file) { - auto result = freqs_file.create("freqman.txt"); +bool create_freqman_file(std::string& file_stem, File& freqman_file) { + auto result = freqman_file.create("FREQMAN/" + file_stem + ".TXT"); if (result.is_valid()) return false; return true; } -std::string freqman_item_string(freqman_entry &entry) { +std::string freqman_item_string(freqman_entry &entry, size_t max_length) { std::string item_string, frequency_str, description; rf::Frequency value; @@ -153,14 +131,10 @@ std::string freqman_item_string(freqman_entry &entry) { entry.frequency_str = to_string_dec_int(value / 1000000, 4) + "." + to_string_dec_int((value / 100) % 10000, 4, '0'); - item_string = entry.frequency_str + "M: "; + item_string = entry.frequency_str + "M: " + entry.description; - if (entry.description.size() <= 19) { - item_string += entry.description; - } else { - // Cut if too long - item_string += entry.description.substr(0, 16) + "..."; - } + if (entry.description.size() > max_length) + return item_string.substr(0, max_length - 3) + "..."; return item_string; } diff --git a/firmware/application/freqman.hpp b/firmware/application/freqman.hpp index fcfaf50b..53c9fe03 100644 --- a/firmware/application/freqman.hpp +++ b/firmware/application/freqman.hpp @@ -30,11 +30,12 @@ #define __FREQMAN_H__ using namespace ui; +using namespace std; enum freqman_error { NO_ERROR = 0, ERROR_ACCESS, - ERROR_EMPTY, + ERROR_NOFILES, ERROR_DUPLICATE }; @@ -42,17 +43,16 @@ struct freqman_entry { rf::Frequency value; std::string frequency_str; std::string description; - int32_t category_id; }; struct freqman_db { std::vector entries; - std::vector categories; }; -bool load_freqman_file(freqman_db &db); -bool save_freqman_file(freqman_db &db); -bool create_freqman_file(File &freqs_file); -std::string freqman_item_string(freqman_entry &item); +std::vector get_freqman_files(); +bool load_freqman_file(std::string& file_stem, freqman_db &db); +bool save_freqman_file(std::string& file_stem, freqman_db &db); +bool create_freqman_file(std::string& file_stem, File& freqman_file); +std::string freqman_item_string(freqman_entry &item, size_t max_length); #endif/*__FREQMAN_H__*/ diff --git a/firmware/application/ui_freqman.cpp b/firmware/application/ui_freqman.cpp index 7f2bfae6..2c432804 100644 --- a/firmware/application/ui_freqman.cpp +++ b/firmware/application/ui_freqman.cpp @@ -29,27 +29,121 @@ using namespace portapack; namespace ui { +FreqManBaseView::FreqManBaseView( + NavigationView& nav, + Widget& default_focus_widget +) : nav_ (nav), + default_focus_widget_ (default_focus_widget) +{ + file_list = get_freqman_files(); + + if (!file_list.size()) { + error_ = ERROR_NOFILES; + return; + } + + add_children({ + &label_category, + &options_category, + &button_exit + }); + + // Populate categories OptionsField + populate_categories(); + + on_change_category = [this](int32_t category_id) { + change_category(category_id); + }; + //change_category(0); + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; +}; + +void FreqManBaseView::focus() { + /*if (error_ == ERROR_ACCESS) { + nav_.display_modal("Error", "File acces error", ABORT, nullptr); + } else if (error_ == ERROR_DUPLICATE) { + nav_.display_modal("Error", "Frequency already saved", INFO, nullptr); + error_ = NO_ERROR; + }*/ + + if (error_ == ERROR_NOFILES) { + nav_.display_modal("Error", "No database files", ABORT, nullptr); + } else { + default_focus_widget_.focus(); + } +} + +bool FreqManBaseView::populate_categories() { + size_t n; + + categories.clear(); + + for (n = 0; n < file_list.size(); n++) + categories.emplace_back(std::make_pair(file_list[n], n)); + + options_category.set_options(categories); + options_category.set_selected_index(0); + + options_category.on_change = [this](size_t, int32_t category_id) { + if (on_change_category) + on_change_category(category_id); + }; + + return true; +} + +void FreqManBaseView::change_category(int32_t category_id) { + current_category_id = category_id; + + if (!load_freqman_file(file_list[current_category_id], database)) + error_ = ERROR_ACCESS; // Todo + else + refresh_list(); +} + +void FreqManBaseView::refresh_list() { + if (!database.entries.size()) { + menu_view.hidden(true); + text_empty.hidden(false); + display.fill_rectangle(menu_view.screen_rect(), Color::black()); + return; + } else { + menu_view.hidden(false); + text_empty.hidden(true); + + menu_view.clear(); + + for (size_t n = 0; n < database.entries.size(); n++) { + menu_view.add_item({ + freqman_item_string(database.entries[n], 26), + ui::Color::light_grey(), + nullptr, + [this](){ + if (on_select_frequency) + on_select_frequency(); + } + }); + } + + menu_view.set_highlighted(0); // Refresh + } +} + void FrequencySaveView::on_save_name() { - text_prompt(nav_, &desc_buffer, 7, [this](std::string * buffer) { - database.entries.push_back({ value_, "", *buffer, (int32_t)options_category.selected_index_value() }); - nav_.pop(); - }); + text_prompt(nav_, &desc_buffer, 28, [this](std::string * buffer) { + database.entries.push_back({ value_, "", *buffer }); + save_freqman_file(file_list[current_category_id], database); + nav_.pop(); + }); } void FrequencySaveView::on_save_timestamp() { - database.entries.push_back({ value_, "", str_timestamp, (int32_t)options_category.selected_index_value() }); + database.entries.push_back({ value_, "", str_timestamp }); nav_.pop(); -} - -void FrequencySaveView::focus() { - if (error == ERROR_ACCESS) { - nav_.display_modal("Error", "File acces error", ABORT, nullptr); - } else if (error == ERROR_DUPLICATE) { - nav_.display_modal("Error", "Frequency already saved", INFO, nullptr); - error = NO_ERROR; - } else { - button_save_timestamp.focus(); - } + save_freqman_file(file_list[current_category_id], database); } void FrequencySaveView::on_tick_second() { @@ -61,38 +155,23 @@ void FrequencySaveView::on_tick_second() { FrequencySaveView::~FrequencySaveView() { rtc_time::signal_tick_second -= signal_token_tick_second; - save_freqman_file(database); } FrequencySaveView::FrequencySaveView( NavigationView& nav, const rf::Frequency value -) : nav_ (nav), +) : FreqManBaseView(nav, options_category), value_ (value) { - using name_t = std::string; - using value_t = int32_t; - using option_t = std::pair; - using options_t = std::vector; - options_t categories; - File freqs_file; - size_t n; - desc_buffer.reserve(28); - if (!load_freqman_file(database)) { - if (!create_freqman_file(freqs_file)) { - error = ERROR_ACCESS; - return; - } - } - - for (n = 0; n < database.entries.size(); n++) { + // Todo: add back ? + /*for (size_t n = 0; n < database.entries.size(); n++) { if (database.entries[n].value == value_) { - error = ERROR_DUPLICATE; + error_ = ERROR_DUPLICATE; break; } - } + }*/ signal_token_tick_second = rtc_time::signal_tick_second += [this]() { this->on_tick_second(); @@ -103,18 +182,8 @@ FrequencySaveView::FrequencySaveView( &text_save, &button_save_name, &button_save_timestamp, - &text_timestamp, - &text_category, - &options_category, - &button_cancel + &text_timestamp }); - - // Populate categories OptionsField - categories.emplace_back(std::make_pair("No cat.", -1)); - for (n = 0; n < database.categories.size(); n++) - categories.emplace_back(std::make_pair(database.categories[n], n)); - options_category.set_options(categories); - options_category.set_selected_index(0); on_tick_second(); @@ -126,180 +195,95 @@ FrequencySaveView::FrequencySaveView( button_save_timestamp.on_select = [this, &nav](Button&) { on_save_timestamp(); }; - - button_cancel.on_select = [this, &nav](Button&) { - nav.pop(); - }; -} - -void FrequencyLoadView::setup_list() { - size_t n; - - menu_view.clear(); - - for (n = 0; n < database.entries.size(); n++) { - menu_view.add_item({ - freqman_item_string(database.entries[n]), - ui::Color::white(), - nullptr, - [this](){ - on_frequency_select(); - } - }); - } - - menu_view.set_highlighted(menu_view.highlighted()); // Refresh -} - -void FrequencyLoadView::on_frequency_select() { - nav_.pop(); - if (on_changed) on_changed(database.entries[menu_view.highlighted()].value); -} - -void FrequencyLoadView::focus() { - if (error == ERROR_ACCESS) - nav_.display_modal("Error", "File acces error", ABORT, nullptr); - else if (error == ERROR_EMPTY) - nav_.display_modal("Error", "Frequency DB empty", ABORT, nullptr); - else - menu_view.focus(); } FrequencyLoadView::FrequencyLoadView( NavigationView& nav -) : nav_ (nav) +) : FreqManBaseView(nav, options_category) { - if (!load_freqman_file(database)) { - error = ERROR_ACCESS; - return; - } - - if (database.entries.size() == 0) { - error = ERROR_EMPTY; - return; - } - add_children({ &menu_view, - &button_cancel + &text_empty }); - setup_list(); - // Just to allow exit on left - menu_view.on_left = [this]() { - on_frequency_select(); - }; - - button_cancel.on_select = [this, &nav](Button&) { + menu_view.on_left = [&nav, this]() { nav.pop(); }; + + text_empty.hidden(true); + change_category(0); + refresh_list(); + + on_select_frequency = [&nav, this]() { + nav_.pop(); + if (on_changed) + on_changed(database.entries[menu_view.highlighted()].value); + }; } -void FreqManView::on_frequency_select() { - options_category.set_selected_index(database.entries[menu_view.highlighted()].category_id + 1); - button_edit_freq.focus(); -} - -void FreqManView::on_edit_freq(rf::Frequency f) { +void FrequencyManagerView::on_edit_freq(rf::Frequency f) { database.entries[menu_view.highlighted()].value = f; - //setup_list(); + save_freqman_file(file_list[current_category_id], database); + refresh_list(); } -void FreqManView::on_edit_desc(NavigationView& nav) { +void FrequencyManagerView::on_edit_desc(NavigationView& nav) { text_prompt(nav, &desc_buffer, 28, [this](std::string * buffer) { - database.entries[menu_view.highlighted()].description = *buffer; - //setup_list(); - }); + database.entries[menu_view.highlighted()].description = *buffer; + refresh_list(); + save_freqman_file(file_list[current_category_id], database); + }); } -void FreqManView::on_delete() { +void FrequencyManagerView::on_new_category(NavigationView& nav) { + text_prompt(nav, &desc_buffer, 8, [this](std::string * buffer) { + File freqman_file; + create_freqman_file(*buffer, freqman_file); + }); + populate_categories(); + refresh_list(); +} + +void FrequencyManagerView::on_delete() { database.entries.erase(database.entries.begin() + menu_view.highlighted()); - //setup_list(); + save_freqman_file(file_list[current_category_id], database); + refresh_list(); } -void FreqManView::on_edit_category(int32_t category_id) { - database.entries[menu_view.highlighted()].category_id = category_id; +FrequencyManagerView::~FrequencyManagerView() { + //save_freqman_file(file_list[current_category_id], database); } -void FreqManView::setup_list() { - size_t n; - - menu_view.clear(); - - for (n = 0; n < database.entries.size(); n++) { - menu_view.add_item({ - freqman_item_string(database.entries[n]), - ui::Color::white(), - nullptr, - [this](){ - on_frequency_select(); - } - }); - } - - menu_view.set_highlighted(0); // Refresh -} - -void FreqManView::focus() { - if (error == ERROR_ACCESS) - nav_.display_modal("Error", "File acces error", ABORT, nullptr); - else if (error == ERROR_EMPTY) - nav_.display_modal("Error", "Frequency DB empty", ABORT, nullptr); - else - menu_view.focus(); -} - -FreqManView::~FreqManView() { - save_freqman_file(database); -} - -FreqManView::FreqManView( +FrequencyManagerView::FrequencyManagerView( NavigationView& nav -) : nav_ (nav) +) : FreqManBaseView(nav, options_category) { - using name_t = std::string; - using value_t = int32_t; - using option_t = std::pair; - using options_t = std::vector; - options_t categories; - size_t n; - - if (!load_freqman_file(database)) { - error = ERROR_ACCESS; - return; - } - - if (database.entries.size() == 0) { - error = ERROR_EMPTY; - return; - } - add_children({ + &labels, + &button_new_category, &menu_view, + &text_empty, &button_edit_freq, &button_edit_desc, - &options_category, - &button_del, - &button_exit + &button_delete }); - setup_list(); - - // Populate categories OptionsField - categories.emplace_back(std::make_pair("No cat.", -1)); - for (n = 0; n < database.categories.size(); n++) - categories.emplace_back(std::make_pair(database.categories[n], n)); - options_category.set_options(categories); - options_category.set_selected_index(0); - - options_category.on_change = [this](size_t, int32_t category_id) { - on_edit_category(category_id); + // Just to allow exit on left + menu_view.on_left = [&nav, this]() { + nav.pop(); }; - // Just to allow exit on left - menu_view.on_left = [this]() { - on_frequency_select(); + text_empty.hidden(true); + change_category(0); + refresh_list(); + + on_select_frequency = [this]() { + button_edit_freq.focus(); + }; + + button_new_category.on_select = [this, &nav](Button&) { + on_new_category(nav); }; button_edit_freq.on_select = [this, &nav](Button&) { @@ -314,19 +298,14 @@ FreqManView::FreqManView( on_edit_desc(nav); }; - button_del.on_select = [this, &nav](Button&) { + button_delete.on_select = [this, &nav](Button&) { nav.push("Confirm", "Are you sure ?", YESNO, [this](bool choice) { - if (choice) { + if (choice) on_delete(); - } } ); }; - - button_exit.on_select = [this, &nav](Button&) { - nav.pop(); - }; } } diff --git a/firmware/application/ui_freqman.hpp b/firmware/application/ui_freqman.hpp index 50bc7306..6e8c6096 100644 --- a/firmware/application/ui_freqman.hpp +++ b/firmware/application/ui_freqman.hpp @@ -31,31 +31,76 @@ #include "rtc_time.hpp" namespace ui { + +class FreqManBaseView : public View { +public: + FreqManBaseView( + NavigationView& nav, Widget& default_focus_widget + ); -class FrequencySaveView : public View { + void focus() override; + + std::string title() const override { return "Freq. manager"; }; + +protected: + using option_t = std::pair; + using options_t = std::vector; + + NavigationView& nav_; + freqman_error error_ { NO_ERROR }; + Widget& default_focus_widget_; + options_t categories { }; + std::function on_change_category { nullptr }; + std::function on_select_frequency { nullptr }; + std::vector file_list { }; + int32_t current_category_id { 0 }; + + bool populate_categories(); + void change_category(int32_t category_id); + void refresh_list(); + + freqman_db database { }; + + Labels label_category { + { { 0, 4 }, "Category:", Color::light_grey() } + }; + + OptionsField options_category { + { 9 * 8, 4 }, + 8, + { } + }; + + MenuView menu_view { + { 0, 3 * 8, 240, 23 * 8 }, + true + }; + Text text_empty { + { 7 * 8, 12 * 8, 16 * 8, 16 }, + "Empty category !", + }; + + Button button_exit { + { 20 * 8, 34 * 8, 10 * 8, 4 * 8 }, + "Exit" + }; +}; + +class FrequencySaveView : public FreqManBaseView { public: FrequencySaveView(NavigationView& nav, const rf::Frequency value); ~FrequencySaveView(); - - void focus() override; - - std::string title() const override { return "Save frequency"; }; private: - NavigationView& nav_; - freqman_error error { NO_ERROR }; std::string desc_buffer { }; rtc::RTC datetime { }; rf::Frequency value_ { }; std::string str_timestamp { }; - //int32_t category_id_ { -1 }; void on_save_name(); void on_save_timestamp(); void on_tick_second(); - freqman_db database { }; - SignalToken signal_token_tick_second { }; BigFrequency big_display { @@ -79,111 +124,52 @@ private: { 17 * 8, 24 * 8, 11 * 8, 16 }, "MM/DD HH:MM", }; - - Text text_category { - { 4 * 8, 28 * 8, 12 * 8, 16 }, - "In category:", - }; - OptionsField options_category { - { 17 * 8, 28 * 8 }, - 8, - { } - }; - - Button button_cancel { - { 72, 264, 96, 32 }, - "Cancel" - }; }; -class FrequencyLoadView : public View { +class FrequencyLoadView : public FreqManBaseView { public: std::function on_changed { }; FrequencyLoadView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Load frequency"; }; private: - NavigationView& nav_; - freqman_error error { NO_ERROR }; - - void on_frequency_select(); - void setup_list(); - - freqman_db database { }; - - MenuView menu_view { - { 0, 0, 240, 216 }, - false - }; - - Button button_cancel { - { 72, 264, 96, 32 }, - "Cancel" - }; }; -class FreqManView : public View { +class FrequencyManagerView : public FreqManBaseView { public: - FreqManView(NavigationView& nav); - ~FreqManView(); - - void focus() override; - - std::string title() const override { return "Freq. manager"; }; + FrequencyManagerView(NavigationView& nav); + ~FrequencyManagerView(); private: - NavigationView& nav_; - - freqman_error error { NO_ERROR }; std::string desc_buffer { }; - void on_frequency_select(); void on_edit_freq(rf::Frequency f); void on_edit_desc(NavigationView& nav); + void on_new_category(NavigationView& nav); void on_delete(); - void on_edit_category(int32_t category_id); - void setup_list(); - - freqman_db database { }; - MenuView menu_view { - { 0, 0, 240, 168 }, - true + Labels labels { + { { 4 * 8 + 4, 26 * 8 }, "Edit:", Color::light_grey() } }; - Labels label { - { { 2 * 8, 24 * 8 }, "Edit:", Color::light_grey() }, - { { 2 * 8, 35 * 8 }, "Category:", Color::light_grey() } + Button button_new_category { + { 18 * 8, 2, 12 * 8, 20 }, + "Create new" }; Button button_edit_freq { - { 2 * 8, 26 * 8, 14 * 8, 32 }, + { 0 * 8, 29 * 8, 14 * 8, 32 }, "Frequency" }; Button button_edit_desc { - { 2 * 8, 30 * 8 + 4, 14 * 8, 32 }, + { 0 * 8, 34 * 8, 14 * 8, 32 }, "Description" }; - - OptionsField options_category { - { 12 * 8, 35 * 8 }, - 8, - { } - }; - Button button_del { - { 20 * 8, 24 * 8, 9 * 8, 48 }, + Button button_delete { + { 18 * 8, 27 * 8, 12 * 8, 32 }, "Delete" }; - - Button button_exit { - { 20 * 8, 33 * 8, 9 * 8, 40 }, - "Exit" - }; }; } /* namespace ui */ diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 5f32cdbd..22c4e519 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -52,8 +52,8 @@ void MenuItemView::paint(Painter& painter) { const auto font_height = paint_style.font.line_height(); - ui::Color final_item_color = (highlighted() && parent()->has_focus()) ? paint_style.foreground : item.color; - ui::Color final_bg_color = (highlighted() && parent()->has_focus()) ? item.color : paint_style.background; + ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight_)) ? paint_style.foreground : item.color; + ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight_)) ? item.color : paint_style.background; if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground; @@ -105,18 +105,15 @@ MenuView::MenuView( }; add_child(&arrow_more); - arrow_more.id = 1; // Special flag arrow_more.set_focusable(false); arrow_more.set_foreground(Color::black()); } MenuView::~MenuView() { - for (auto child : children_) { - if (!child->id) { - delete child; - } - } rtc_time::signal_tick_second -= signal_token_tick_second; + for (auto item : menu_items_) { + delete item; + } } void MenuView::on_tick_second() { @@ -131,20 +128,26 @@ void MenuView::on_tick_second() { } void MenuView::clear() { - for (auto child : children_) { - if (!child->id) { - remove_child(child); - } + for (auto item : menu_items_) { + remove_child(item); + delete item; } -} - -void MenuView::add_item(MenuItem item) { - add_child(new MenuItemView { item, keep_highlight_ }); + menu_items_.clear(); + update_items(); } -void MenuView::add_items(std::initializer_list items) { - for(auto item : items) { +void MenuView::add_item(MenuItem new_item) { + auto item = new MenuItemView { new_item, keep_highlight_ }; + + menu_items_.push_back(item); + add_child(item); + + update_items(); +} + +void MenuView::add_items(std::initializer_list new_items) { + for (auto item : new_items) { add_item(item); } } @@ -153,33 +156,30 @@ void MenuView::update_items() { size_t i = 0; int32_t y_pos; - if ((children_.size() - 1) > displayed_max_ + offset_) { + if (menu_items_.size() > displayed_max_ + offset_) { more_ = true; blink_ = true; } else more_ = false; - for (auto child : children_) { - if (!child->id) { - y_pos = (i - offset_ - 1) * item_height; - child->set_parent_rect({ - { 0, y_pos }, - { size().width(), (Coord)item_height } - }); - if ((y_pos < 0) || (y_pos > (Coord)(screen_rect().size().height() - item_height))) - child->hidden(true); - else - child->hidden(false); - } + for (auto item : menu_items_) { + y_pos = (i - offset_) * item_height; + item->set_parent_rect({ + { 0, y_pos }, + { size().width(), (Coord)item_height } + }); + if ((y_pos < 0) || (y_pos > (Coord)(screen_rect().size().height() - item_height))) + item->hidden(true); + else + item->hidden(false); i++; } + + set_dirty(); } MenuItemView* MenuView::item_view(size_t index) const { - /* TODO: Terrible cast! Take it as a sign I must be doing something - * shamefully wrong here, right? - */ - return static_cast(children_[index + 1]); + return menu_items_[index]; } size_t MenuView::highlighted() const { @@ -187,7 +187,7 @@ size_t MenuView::highlighted() const { } bool MenuView::set_highlighted(int32_t new_value) { - int32_t item_count = (int32_t)children_.size() - 1; + int32_t item_count = (int32_t)menu_items_.size(); if (new_value < 0) return false; diff --git a/firmware/application/ui_menu.hpp b/firmware/application/ui_menu.hpp index eebada5e..c974117c 100644 --- a/firmware/application/ui_menu.hpp +++ b/firmware/application/ui_menu.hpp @@ -75,8 +75,8 @@ public: ~MenuView(); - void add_item(MenuItem item); - void add_items(std::initializer_list items); + void add_item(MenuItem new_item); + void add_items(std::initializer_list new_items); void clear(); MenuItemView* item_view(size_t index) const; @@ -89,6 +89,7 @@ public: bool on_key(const KeyEvent event) override; bool on_encoder(const EncoderEvent event) override; + private: void update_items(); void on_tick_second(); @@ -96,6 +97,7 @@ private: bool keep_highlight_ { false }; SignalToken signal_token_tick_second { }; + std::vector menu_items_ { }; Image arrow_more { { 228, 320 - 8, 8, 8 }, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 83289c33..3a187ce0 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -323,7 +323,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) { add_items({ - { "Frequency manager", ui::Color::green(), &bitmap_icon_freqman, [&nav](){ nav.push(); } }, + { "Frequency manager", ui::Color::green(), &bitmap_icon_freqman, [&nav](){ nav.push(); } }, { "CW generator", ui::Color::green(), &bitmap_icon_cwgen, [&nav](){ nav.push(); } }, { "Whip antenna length", ui::Color::yellow(),nullptr, [&nav](){ nav.push(); } }, { "Notepad", ui::Color::grey(), &bitmap_icon_notepad, [&nav](){ nav.push(); } }, diff --git a/firmware/application/ui_soundboard.cpp b/firmware/application/ui_soundboard.cpp index 1729b161..4fce6379 100644 --- a/firmware/application/ui_soundboard.cpp +++ b/firmware/application/ui_soundboard.cpp @@ -93,7 +93,7 @@ void SoundBoardView::focus() { buttons[0].focus(); if (!max_sound) - nav_.display_modal("No files", "No files in /wav/ directory", ABORT, nullptr); + nav_.display_modal("No files", "No files in /WAV/ directory", ABORT, nullptr); } void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) { @@ -201,11 +201,11 @@ SoundBoardView::SoundBoardView( reader = std::make_unique(); - file_list = scan_root_files(u"wav", u"*.WAV"); + file_list = scan_root_files(u"WAV", u"*.WAV"); c = 0; for (auto& path : file_list) { - if (reader->open(u"wav/" + path.native())) { + if (reader->open(u"WAV/" + path.native())) { if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) { sounds[c].size = reader->data_size(); sounds[c].sample_duration = reader->data_size(); // / (reader->bits_per_sample() / 8); @@ -215,7 +215,7 @@ SoundBoardView::SoundBoardView( else sounds[c].sixteenbit = false;*/ sounds[c].ms_duration = reader->ms_duration(); - sounds[c].path = u"wav/" + path.native(); + sounds[c].path = u"WAV/" + path.native(); title = reader->title().substr(0, 20); if (title != "") sounds[c].title = title;