diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 73b6cebc6..4aa171a48 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -165,6 +165,7 @@ CPPSRC = main.cpp \ ui_console.cpp \ ui_receiver.cpp \ ui_spectrum.cpp \ + recent_entries.cpp \ receiver_model.cpp \ spectrum_color_lut.cpp \ analog_audio_app.cpp \ diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index fa2ff7ca6..25b332998 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -216,92 +216,8 @@ void AISRecentEntry::update(const ais::Packet& packet) { } } -template -const Entry& RecentEntries::on_packet(const Key key, const Packet& packet) { - auto matching_recent = find(key); - if( matching_recent != std::end(entries) ) { - // Found within. Move to front of list, increment counter. - entries.push_front(*matching_recent); - entries.erase(matching_recent); - } else { - entries.emplace_front(key); - truncate_entries(); - } - - auto& entry = entries.front(); - entry.update(packet); - - return entry; -} - -template -typename RecentEntries::const_iterator RecentEntries::find(const Key key) const { - return std::find_if( - std::begin(entries), std::end(entries), - [key](const Entry& e) { return e.key() == key; } - ); -} - -template -void RecentEntries::truncate_entries() { - while(entries.size() > entries_max) { - entries.pop_back(); - } -} - -template -typename RecentEntries::RangeType RecentEntries::range_around( - const_iterator item, const size_t count -) const { - auto start = item; - auto end = item; - size_t i = 0; - - // Move start iterator toward first entry. - while( (start != std::begin(entries)) && (i < count / 2) ) { - std::advance(start, -1); - i++; - } - - // Move end iterator toward last entry. - while( (end != std::end(entries)) && (i < count) ) { - std::advance(end, 1); - i++; - } - - return { start, end }; -} - namespace ui { -template -RecentEntriesView::RecentEntriesView( - Entries& recent -) : recent { recent } -{ - flags.focusable = true; -} - -template -bool RecentEntriesView::on_encoder(const EncoderEvent event) { - advance(event); - return true; -} - -template -bool RecentEntriesView::on_key(const ui::KeyEvent event) { - if( event == ui::KeyEvent::Select ) { - if( on_select ) { - const auto selected = recent.find(selected_key); - if( selected != std::end(recent) ) { - on_select(*selected); - return true; - } - } - } - return false; -} - template<> void RecentEntriesView::draw( const Entry& entry, @@ -323,56 +239,6 @@ void RecentEntriesView::draw( painter.draw_string(target_rect.pos, draw_style, line); } -template -void RecentEntriesView::paint(Painter& painter) { - const auto r = screen_rect(); - const auto& s = style(); - - Rect target_rect { r.pos, { r.width(), s.font.line_height() }}; - const size_t visible_item_count = r.height() / s.font.line_height(); - - auto selected = recent.find(selected_key); - if( selected == std::end(recent) ) { - selected = std::begin(recent); - } - - auto range = recent.range_around(selected, visible_item_count); - - for(auto p = range.first; p != range.second; p++) { - const auto& entry = *p; - const auto is_selected_key = (selected_key == entry.key()); - draw(entry, target_rect, painter, s, (has_focus() && is_selected_key)); - target_rect.pos.y += target_rect.height(); - } -} - -template -void RecentEntriesView::advance(const int32_t amount) { - auto selected = recent.find(selected_key); - if( selected == std::end(recent) ) { - if( recent.empty() ) { - selected_key = Entry::invalid_key; - } else { - selected_key = recent.front().key(); - } - } else { - if( amount < 0 ) { - if( selected != std::begin(recent) ) { - std::advance(selected, -1); - } - } - if( amount > 0 ) { - std::advance(selected, 1); - if( selected == std::end(recent) ) { - return; - } - } - selected_key = selected->key(); - } - - set_dirty(); -} - AISRecentEntryDetailView::AISRecentEntryDetailView() { add_children({ { &button_done, diff --git a/firmware/application/ais_app.hpp b/firmware/application/ais_app.hpp index 5dfd0f6bf..2c7a435c4 100644 --- a/firmware/application/ais_app.hpp +++ b/firmware/application/ais_app.hpp @@ -38,6 +38,8 @@ using namespace lpc43xx; #include +#include "recent_entries.hpp" + struct AISPosition { rtc::RTC timestamp { }; ais::Latitude latitude; @@ -82,47 +84,6 @@ struct AISRecentEntry { void update(const ais::Packet& packet); }; -template -class RecentEntries { -public: - using EntryType = Entry; - using Key = typename Entry::Key; - using ContainerType = std::list; - using const_reference = typename ContainerType::const_reference; - using const_iterator = typename ContainerType::const_iterator; - using RangeType = std::pair; - - const Entry& on_packet(const Key key, const Packet& packet); - - const_reference front() const { - return entries.front(); - } - - const_iterator find(const Key key) const; - - const_iterator begin() const { - return entries.begin(); - } - - const_iterator end() const { - return entries.end(); - } - - bool empty() const { - return entries.empty(); - } - - RangeType range_around( - const_iterator, const size_t count - ) const; - -private: - ContainerType entries; - const size_t entries_max = 64; - - void truncate_entries(); -}; - using AISRecentEntries = RecentEntries; class AISLogger { @@ -135,37 +96,6 @@ private: namespace ui { -template -class RecentEntriesView : public View { -public: - using Entry = typename Entries::EntryType; - - std::function on_select; - - RecentEntriesView(Entries& recent); - - void paint(Painter& painter) override; - - bool on_encoder(const EncoderEvent event) override; - bool on_key(const ui::KeyEvent event) override; - -private: - Entries& recent; - - using EntryKey = typename Entry::Key; - EntryKey selected_key; - - void advance(const int32_t amount); - - void draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style, - const bool is_selected - ); -}; - using AISRecentEntriesView = RecentEntriesView; class AISRecentEntryDetailView : public View { diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp new file mode 100644 index 000000000..bfb21aeef --- /dev/null +++ b/firmware/application/recent_entries.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "recent_entries.hpp" diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp new file mode 100644 index 000000000..a04545863 --- /dev/null +++ b/firmware/application/recent_entries.hpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __RECENT_ENTRIES_H__ +#define __RECENT_ENTRIES_H__ + +#include "ui_widget.hpp" + +#include +#include +#include +#include +#include +#include +#include + +template +class RecentEntries { +public: + using EntryType = Entry; + using Key = typename Entry::Key; + using ContainerType = std::list; + using const_reference = typename ContainerType::const_reference; + using const_iterator = typename ContainerType::const_iterator; + using RangeType = std::pair; + + const Entry& on_packet(const Key key, const Packet& packet) { + auto matching_recent = find(key); + if( matching_recent != std::end(entries) ) { + // Found within. Move to front of list, increment counter. + entries.push_front(*matching_recent); + entries.erase(matching_recent); + } else { + entries.emplace_front(key); + truncate_entries(); + } + + auto& entry = entries.front(); + entry.update(packet); + + return entry; + } + + const_reference front() const { + return entries.front(); + } + + const_iterator find(const Key key) const { + return std::find_if( + std::begin(entries), std::end(entries), + [key](const Entry& e) { return e.key() == key; } + ); + } + + const_iterator begin() const { + return entries.begin(); + } + + const_iterator end() const { + return entries.end(); + } + + bool empty() const { + return entries.empty(); + } + + RangeType range_around( + const_iterator item, const size_t count + ) const { + auto start = item; + auto end = item; + size_t i = 0; + + // Move start iterator toward first entry. + while( (start != std::begin(entries)) && (i < count / 2) ) { + std::advance(start, -1); + i++; + } + + // Move end iterator toward last entry. + while( (end != std::end(entries)) && (i < count) ) { + std::advance(end, 1); + i++; + } + + return { start, end }; + } + +private: + ContainerType entries; + const size_t entries_max = 64; + + void truncate_entries() { + while(entries.size() > entries_max) { + entries.pop_back(); + } + } +}; + +namespace ui { + +template +class RecentEntriesView : public View { +public: + using Entry = typename Entries::EntryType; + + std::function on_select; + + RecentEntriesView( + Entries& recent + ) : recent { recent } + { + flags.focusable = true; + } + + void paint(Painter& painter) override { + const auto r = screen_rect(); + const auto& s = style(); + + Rect target_rect { r.pos, { r.width(), s.font.line_height() }}; + const size_t visible_item_count = r.height() / s.font.line_height(); + + auto selected = recent.find(selected_key); + if( selected == std::end(recent) ) { + selected = std::begin(recent); + } + + auto range = recent.range_around(selected, visible_item_count); + + for(auto p = range.first; p != range.second; p++) { + const auto& entry = *p; + const auto is_selected_key = (selected_key == entry.key()); + draw(entry, target_rect, painter, s, (has_focus() && is_selected_key)); + target_rect.pos.y += target_rect.height(); + } + } + + bool on_encoder(const EncoderEvent event) override { + advance(event); + return true; + } + + bool on_key(const ui::KeyEvent event) override { + if( event == ui::KeyEvent::Select ) { + if( on_select ) { + const auto selected = recent.find(selected_key); + if( selected != std::end(recent) ) { + on_select(*selected); + return true; + } + } + } + return false; + } + +private: + Entries& recent; + + using EntryKey = typename Entry::Key; + EntryKey selected_key; + + void advance(const int32_t amount) { + auto selected = recent.find(selected_key); + if( selected == std::end(recent) ) { + if( recent.empty() ) { + selected_key = Entry::invalid_key; + } else { + selected_key = recent.front().key(); + } + } else { + if( amount < 0 ) { + if( selected != std::begin(recent) ) { + std::advance(selected, -1); + } + } + if( amount > 0 ) { + std::advance(selected, 1); + if( selected == std::end(recent) ) { + return; + } + } + selected_key = selected->key(); + } + + set_dirty(); + } + + void draw( + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style, + const bool is_selected + ); +}; + +} /* namespace ui */ + +#endif/*__RECENT_ENTRIES_H__*/