diff options
author | Andrzej Rybczak <electricityispower@gmail.com> | 2016-11-11 16:43:41 +0100 |
---|---|---|
committer | Andrzej Rybczak <electricityispower@gmail.com> | 2016-11-11 16:44:36 +0100 |
commit | cbba364685b25d436e620bebcd37b024149d4171 (patch) | |
tree | fbbb874af4de655e36675eccca0a671de5542c24 | |
parent | 90f9a66ba4d0052dbeb4b31f4d71d3a5562f9322 (diff) |
Prepare for the ressurection of filtering
-rw-r--r-- | doc/bindings | 3 | ||||
-rw-r--r-- | src/actions.cpp | 9 | ||||
-rw-r--r-- | src/actions.h | 10 | ||||
-rw-r--r-- | src/bindings.cpp | 2 | ||||
-rw-r--r-- | src/help.cpp | 1 | ||||
-rw-r--r-- | src/menu.h | 103 | ||||
-rw-r--r-- | src/menu_impl.h | 179 |
7 files changed, 191 insertions, 116 deletions
diff --git a/doc/bindings b/doc/bindings index f586f0b3..db132cc7 100644 --- a/doc/bindings +++ b/doc/bindings @@ -381,6 +381,9 @@ #def_key "ctrl-r" # reverse_playlist # +#def_key "ctrl-f" +# apply_filter +# #def_key "ctrl-_" # select_found_items # diff --git a/src/actions.cpp b/src/actions.cpp index b9045557..aa0a96d6 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1934,6 +1934,14 @@ void ReversePlaylist::run() Statusbar::print("Range reversed"); } +bool ApplyFilter::canBeRun() +{ + return false; +} + +void ApplyFilter::run() +{ } + bool Find::canBeRun() { return myScreen == myHelp @@ -2724,6 +2732,7 @@ void populateActions() insert_action(new Actions::ClearPlaylist()); insert_action(new Actions::SortPlaylist()); insert_action(new Actions::ReversePlaylist()); + insert_action(new Actions::ApplyFilter()); insert_action(new Actions::Find()); insert_action(new Actions::FindItemForward()); insert_action(new Actions::FindItemBackward()); diff --git a/src/actions.h b/src/actions.h index 1a6ee60b..34c504bf 100644 --- a/src/actions.h +++ b/src/actions.h @@ -122,6 +122,7 @@ enum class Type ClearPlaylist, SortPlaylist, ReversePlaylist, + ApplyFilter, Find, FindItemForward, FindItemBackward, @@ -1036,6 +1037,15 @@ private: NC::Menu<MPD::Song>::ConstIterator m_end; }; +struct ApplyFilter: public BaseAction +{ + ApplyFilter(): BaseAction(Type::ApplyFilter, "apply_filter") { } + +private: + virtual bool canBeRun() OVERRIDE; + virtual void run() OVERRIDE; +}; + struct Find: BaseAction { Find(): BaseAction(Type::Find, "find") { } diff --git a/src/bindings.cpp b/src/bindings.cpp index c2c0643f..1ce02f29 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -625,6 +625,8 @@ void BindingsConfiguration::generateDefaults() } if (notBound(k = stringToKey("ctrl-r"))) bind(k, Actions::Type::ReversePlaylist); + if (notBound(k = stringToKey("ctrl-f"))) + bind(k, Actions::Type::ApplyFilter); if (notBound(k = stringToKey("ctrl-_"))) bind(k, Actions::Type::SelectFoundItems); if (notBound(k = stringToKey("/"))) diff --git a/src/help.cpp b/src/help.cpp index 15fc1031..cb1305a9 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -210,6 +210,7 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::UpdateDatabase, "Start music database update"); w << '\n'; key(w, Type::ExecuteCommand, "Execute command"); + key(w, Type::ApplyFilter, "Apply filter"); key(w, Type::FindItemForward, "Find item forward"); key(w, Type::FindItemBackward, "Find item backward"); key(w, Type::PreviousFoundItem, "Jump to previous found item"); @@ -96,6 +96,9 @@ struct List bool isInactive() const { return m_properties & Inactive; } bool isSeparator() const { return m_properties & Separator; } + protected: + unsigned properties() const { return m_properties; } + private: unsigned m_properties; }; @@ -174,17 +177,26 @@ template <typename ItemT> struct Menu : Window, List friend struct Menu<ItemT>; - Item() { } - Item(ItemT value_, Properties::Type properties) + Item() + : m_value(std::make_shared<ItemT>(ItemT())) + { } + + template <typename ValueT> + Item(ValueT &&value_, Properties::Type properties) : Properties(properties) - , m_value(value_) + , m_value(std::make_shared<ItemT>(std::forward<ValueT>(value_))) { } - ItemT &value() { return m_value; } - const ItemT &value() const { return m_value; } + ItemT &value() { return *m_value; } + const ItemT &value() const { return *m_value; } - ItemT &operator*() { return m_value; } - const ItemT &operator*() const { return m_value; } + ItemT &operator*() { return *m_value; } + const ItemT &operator*() const { return *m_value; } + + // Make a deep copy of Item. + Item copy() const { + return Item(*m_value, static_cast<Properties::Type>(properties())); + } private: static Item mkSeparator() @@ -195,9 +207,9 @@ template <typename ItemT> struct Menu : Window, List return item; } - ItemT m_value; + std::shared_ptr<ItemT> m_value; }; - + typedef typename std::vector<Item>::iterator Iterator; typedef typename std::vector<Item>::const_iterator ConstIterator; typedef std::reverse_iterator<Iterator> ReverseIterator; @@ -230,7 +242,7 @@ template <typename ItemT> struct Menu : Window, List /// @see setItemDisplayer() typedef std::function<void(Menu<ItemT> &)> ItemDisplayer; - Menu() { } + Menu(); Menu(size_t startx, size_t starty, size_t width, size_t height, const std::string &title, Color color, Border border); @@ -260,10 +272,6 @@ template <typename ItemT> struct Menu : Window, List /// @param pos initial position of inserted separator void insertSeparator(size_t pos); - /// Deletes item from given position - /// @param pos given position of item to be deleted - void deleteItem(size_t pos); - /// Moves the highlighted position to the given line of window /// @param y Y position of menu window to be highlighted /// @return true if the position is reachable, false otherwise @@ -271,10 +279,10 @@ template <typename ItemT> struct Menu : Window, List /// Checks if list is empty /// @return true if list is empty, false otherwise - virtual bool empty() const OVERRIDE { return m_items.empty(); } + virtual bool empty() const OVERRIDE { return m_items->empty(); } /// @return size of the list - virtual size_t size() const OVERRIDE { return m_items.size(); } + virtual size_t size() const OVERRIDE { return m_items->size(); } /// @return currently highlighted position virtual size_t choice() const OVERRIDE; @@ -298,7 +306,24 @@ template <typename ItemT> struct Menu : Window, List /// Sets highlighted position to 0 void reset(); - + + /// Apply filter predicate to items in the menu and show the ones for which it + /// returned true. + template <typename FilterPredicate> + bool applyFilter(FilterPredicate &&p); + + /// Clear results of applyFilter and show all items. + void clearFilter(); + + /// @return true if menu is filtered. + bool isFiltered() const { return m_items == &m_filtered_items; } + + /// Show all items. + void showAllItems() { m_items = &m_all_items; } + + /// Show filtered items. + void showFilteredItems() { m_items = &m_filtered_items; } + /// Sets prefix, that is put before each selected item to indicate its selection /// Note that the passed variable is not deleted along with menu object. /// @param b pointer to buffer that contains the prefix @@ -336,23 +361,23 @@ template <typename ItemT> struct Menu : Window, List /// @param pos requested position /// @return reference to item at given position /// @throw std::out_of_range if given position is out of range - Menu<ItemT>::Item &at(size_t pos) { return m_items.at(pos); } + Menu<ItemT>::Item &at(size_t pos) { return m_items->at(pos); } /// @param pos requested position /// @return const reference to item at given position /// @throw std::out_of_range if given position is out of range - const Menu<ItemT>::Item &at(size_t pos) const { return m_items.at(pos); } + const Menu<ItemT>::Item &at(size_t pos) const { return m_items->at(pos); } /// @param pos requested position /// @return const reference to item at given position - const Menu<ItemT>::Item &operator[](size_t pos) const { return m_items[pos]; } + const Menu<ItemT>::Item &operator[](size_t pos) const { return (*m_items)[pos]; } /// @param pos requested position /// @return const reference to item at given position - Menu<ItemT>::Item &operator[](size_t pos) { return m_items[pos]; } + Menu<ItemT>::Item &operator[](size_t pos) { return (*m_items)[pos]; } - Iterator current() { return Iterator(m_items.begin() + m_highlight); } - ConstIterator current() const { return ConstIterator(m_items.begin() + m_highlight); } + Iterator current() { return Iterator(m_items->begin() + m_highlight); } + ConstIterator current() const { return ConstIterator(m_items->begin() + m_highlight); } ReverseIterator rcurrent() { if (empty()) return rend(); @@ -366,8 +391,8 @@ template <typename ItemT> struct Menu : Window, List return ConstReverseIterator(++current()); } - ValueIterator currentV() { return ValueIterator(m_items.begin() + m_highlight); } - ConstValueIterator currentV() const { return ConstValueIterator(m_items.begin() + m_highlight); } + ValueIterator currentV() { return ValueIterator(m_items->begin() + m_highlight); } + ConstValueIterator currentV() const { return ConstValueIterator(m_items->begin() + m_highlight); } ReverseValueIterator rcurrentV() { if (empty()) return rendV(); @@ -381,10 +406,10 @@ template <typename ItemT> struct Menu : Window, List return ConstReverseValueIterator(++currentV()); } - Iterator begin() { return Iterator(m_items.begin()); } - ConstIterator begin() const { return ConstIterator(m_items.begin()); } - Iterator end() { return Iterator(m_items.end()); } - ConstIterator end() const { return ConstIterator(m_items.end()); } + Iterator begin() { return Iterator(m_items->begin()); } + ConstIterator begin() const { return ConstIterator(m_items->begin()); } + Iterator end() { return Iterator(m_items->end()); } + ConstIterator end() const { return ConstIterator(m_items->end()); } ReverseIterator rbegin() { return ReverseIterator(end()); } ConstReverseIterator rbegin() const { return ConstReverseIterator(end()); } @@ -402,34 +427,36 @@ template <typename ItemT> struct Menu : Window, List ConstReverseValueIterator rendV() const { return ConstReverseValueIterator(beginV()); } virtual List::Iterator currentP() OVERRIDE { - return List::Iterator(PropertiesIterator(m_items.begin() + m_highlight)); + return List::Iterator(PropertiesIterator(m_items->begin() + m_highlight)); } virtual List::ConstIterator currentP() const OVERRIDE { - return List::ConstIterator(ConstPropertiesIterator(m_items.begin() + m_highlight)); + return List::ConstIterator(ConstPropertiesIterator(m_items->begin() + m_highlight)); } virtual List::Iterator beginP() OVERRIDE { - return List::Iterator(PropertiesIterator(m_items.begin())); + return List::Iterator(PropertiesIterator(m_items->begin())); } virtual List::ConstIterator beginP() const OVERRIDE { - return List::ConstIterator(ConstPropertiesIterator(m_items.begin())); + return List::ConstIterator(ConstPropertiesIterator(m_items->begin())); } virtual List::Iterator endP() OVERRIDE { - return List::Iterator(PropertiesIterator(m_items.end())); + return List::Iterator(PropertiesIterator(m_items->end())); } virtual List::ConstIterator endP() const OVERRIDE { - return List::ConstIterator(ConstPropertiesIterator(m_items.end())); + return List::ConstIterator(ConstPropertiesIterator(m_items->end())); } private: bool isHighlightable(size_t pos) { - return !m_items[pos].isSeparator() - && !m_items[pos].isInactive(); + return !(*m_items)[pos].isSeparator() + && !(*m_items)[pos].isInactive(); } ItemDisplayer m_item_displayer; - std::vector<Item> m_items; + std::vector<Item> *m_items; + std::vector<Item> m_all_items; + std::vector<Item> m_filtered_items; size_t m_beginning; size_t m_highlight; diff --git a/src/menu_impl.h b/src/menu_impl.h index f3eefa6d..33b8a24f 100644 --- a/src/menu_impl.h +++ b/src/menu_impl.h @@ -26,59 +26,72 @@ namespace NC { template <typename ItemT> +Menu<ItemT>::Menu() +{ + m_items = &m_all_items; +} + +template <typename ItemT> Menu<ItemT>::Menu(size_t startx, - size_t starty, - size_t width, - size_t height, - const std::string &title, - Color color, - Border border) - : Window(startx, starty, width, height, title, std::move(color), border), - m_item_displayer(0), - m_beginning(0), - m_highlight(0), - m_highlight_color(m_base_color), - m_highlight_enabled(true), - m_cyclic_scroll_enabled(false), - m_autocenter_cursor(false) + size_t starty, + size_t width, + size_t height, + const std::string &title, + Color color, + Border border) + : Window(startx, starty, width, height, title, std::move(color), border) + , m_item_displayer(0) + , m_beginning(0) + , m_highlight(0) + , m_highlight_color(m_base_color) + , m_highlight_enabled(true) + , m_cyclic_scroll_enabled(false) + , m_autocenter_cursor(false) { + m_items = &m_all_items; } template <typename ItemT> Menu<ItemT>::Menu(const Menu &rhs) -: Window(rhs) -, m_item_displayer(rhs.m_item_displayer) -, m_beginning(rhs.m_beginning) -, m_highlight(rhs.m_highlight) -, m_highlight_color(rhs.m_highlight_color) -, m_highlight_enabled(rhs.m_highlight_enabled) -, m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled) -, m_autocenter_cursor(rhs.m_autocenter_cursor) -, m_drawn_position(rhs.m_drawn_position) -, m_selected_prefix(rhs.m_selected_prefix) -, m_selected_suffix(rhs.m_selected_suffix) + : Window(rhs) + , m_item_displayer(rhs.m_item_displayer) + , m_beginning(rhs.m_beginning) + , m_highlight(rhs.m_highlight) + , m_highlight_color(rhs.m_highlight_color) + , m_highlight_enabled(rhs.m_highlight_enabled) + , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled) + , m_autocenter_cursor(rhs.m_autocenter_cursor) + , m_drawn_position(rhs.m_drawn_position) + , m_selected_prefix(rhs.m_selected_prefix) + , m_selected_suffix(rhs.m_selected_suffix) { - // there is no way to properly fill m_filtered_options - // (if rhs is filtered), so we just don't do it. - m_items.reserve(rhs.m_items.size()); - std::copy(rhs.begin(), rhs.end(), std::back_inserter(m_items)); + // TODO: move filtered items + m_all_items.reserve(rhs.m_all_items.size()); + for (const auto &item : rhs.m_all_items) + m_all_items.push_back(item.copy()); + m_items = &m_all_items; } template <typename ItemT> Menu<ItemT>::Menu(Menu &&rhs) -: Window(rhs) -, m_item_displayer(rhs.m_item_displayer) -, m_items(std::move(rhs.m_items)) -, m_beginning(rhs.m_beginning) -, m_highlight(rhs.m_highlight) -, m_highlight_color(rhs.m_highlight_color) -, m_highlight_enabled(rhs.m_highlight_enabled) -, m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled) -, m_autocenter_cursor(rhs.m_autocenter_cursor) -, m_drawn_position(rhs.m_drawn_position) -, m_selected_prefix(std::move(rhs.m_selected_prefix)) -, m_selected_suffix(std::move(rhs.m_selected_suffix)) + : Window(rhs) + , m_item_displayer(rhs.m_item_displayer) + , m_all_items(std::move(rhs.m_all_items)) + , m_filtered_items(std::move(rhs.m_filtered_items)) + , m_beginning(rhs.m_beginning) + , m_highlight(rhs.m_highlight) + , m_highlight_color(rhs.m_highlight_color) + , m_highlight_enabled(rhs.m_highlight_enabled) + , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled) + , m_autocenter_cursor(rhs.m_autocenter_cursor) + , m_drawn_position(rhs.m_drawn_position) + , m_selected_prefix(std::move(rhs.m_selected_prefix)) + , m_selected_suffix(std::move(rhs.m_selected_suffix)) { + if (rhs.m_items == &rhs.m_all_items) + m_items = &m_all_items; + else + m_items = &m_filtered_items; } template <typename ItemT> @@ -86,7 +99,8 @@ Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs) { std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs)); std::swap(m_item_displayer, rhs.m_item_displayer); - std::swap(m_items, rhs.m_items); + std::swap(m_all_items, rhs.m_all_items); + std::swap(m_filtered_items, rhs.m_filtered_items); std::swap(m_beginning, rhs.m_beginning); std::swap(m_highlight, rhs.m_highlight); std::swap(m_highlight_color, rhs.m_highlight_color); @@ -96,52 +110,41 @@ Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs) std::swap(m_drawn_position, rhs.m_drawn_position); std::swap(m_selected_prefix, rhs.m_selected_prefix); std::swap(m_selected_suffix, rhs.m_selected_suffix); + if (rhs.m_items == &rhs.m_all_items) + m_items = &m_all_items; + else + m_items = &m_filtered_items; return *this; } template <typename ItemT> void Menu<ItemT>::resizeList(size_t new_size) { - if (new_size > m_items.size()) - { - size_t old_size = m_items.size(); - m_items.resize(new_size); - for (size_t i = old_size; i < new_size; ++i) - m_items[i] = Item(); - } - else - m_items.resize(new_size); + m_all_items.resize(new_size); } template <typename ItemT> void Menu<ItemT>::addItem(ItemT item, Properties::Type properties) { - m_items.push_back(Item(std::move(item), properties)); + m_all_items.push_back(Item(std::move(item), properties)); } template <typename ItemT> void Menu<ItemT>::addSeparator() { - m_items.push_back(Item::mkSeparator()); + m_all_items.push_back(Item::mkSeparator()); } template <typename ItemT> void Menu<ItemT>::insertItem(size_t pos, ItemT item, Properties::Type properties) { - m_items.insert(m_items.begin()+pos, Item(std::move(item), properties)); + m_all_items.insert(m_all_items.begin()+pos, Item(std::move(item), properties)); } template <typename ItemT> void Menu<ItemT>::insertSeparator(size_t pos) { - m_items.insert(m_items.begin()+pos, Item::mkSeparator()); -} - -template <typename ItemT> -void Menu<ItemT>::deleteItem(size_t pos) -{ - assert(pos < m_items.size()); - m_items.erase(m_items.begin()+pos); + m_all_items.insert(m_all_items.begin()+pos, Item::mkSeparator()); } template <typename ItemT> @@ -156,7 +159,7 @@ bool Menu<ItemT>::Goto(size_t y) template <typename ItemT> void Menu<ItemT>::refresh() { - if (m_items.empty()) + if (m_items->empty()) { Window::clear(); Window::refresh(); @@ -164,14 +167,14 @@ void Menu<ItemT>::refresh() } size_t max_beginning = 0; - if (m_items.size() > m_height) - max_beginning = m_items.size() - m_height; + if (m_items->size() > m_height) + max_beginning = m_items->size() - m_height; m_beginning = std::min(m_beginning, max_beginning); // if highlighted position is off the screen, make it visible m_highlight = std::min(m_highlight, m_beginning+m_height-1); // if highlighted position is invalid, correct it - m_highlight = std::min(m_highlight, m_items.size()-1); + m_highlight = std::min(m_highlight, m_items->size()-1); if (!isHighlightable(m_highlight)) { @@ -186,18 +189,18 @@ void Menu<ItemT>::refresh() for (; m_drawn_position < end_; ++m_drawn_position, ++line) { goToXY(0, line); - if (m_drawn_position >= m_items.size()) + if (m_drawn_position >= m_items->size()) { for (; line < m_height; ++line) mvwhline(m_window, line, 0, NC::Key::Space, m_width); break; } - if (m_items[m_drawn_position].isSeparator()) + if ((*m_items)[m_drawn_position].isSeparator()) { mvwhline(m_window, line, 0, 0, m_width); continue; } - if (m_items[m_drawn_position].isBold()) + if ((*m_items)[m_drawn_position].isBold()) *this << Format::Bold; if (m_highlight_enabled && m_drawn_position == m_highlight) { @@ -205,18 +208,18 @@ void Menu<ItemT>::refresh() *this << m_highlight_color; } mvwhline(m_window, line, 0, NC::Key::Space, m_width); - if (m_items[m_drawn_position].isSelected()) + if ((*m_items)[m_drawn_position].isSelected()) *this << m_selected_prefix; if (m_item_displayer) m_item_displayer(*this); - if (m_items[m_drawn_position].isSelected()) + if ((*m_items)[m_drawn_position].isSelected()) *this << m_selected_suffix; if (m_highlight_enabled && m_drawn_position == m_highlight) { *this << Color::End; *this << Format::NoReverse; } - if (m_items[m_drawn_position].isBold()) + if ((*m_items)[m_drawn_position].isBold()) *this << Format::NoBold; } Window::refresh(); @@ -225,10 +228,10 @@ void Menu<ItemT>::refresh() template <typename ItemT> void Menu<ItemT>::scroll(Scroll where) { - if (m_items.empty()) + if (m_items->empty()) return; - size_t max_highlight = m_items.size()-1; - size_t max_beginning = m_items.size() < m_height ? 0 : m_items.size()-m_height; + size_t max_highlight = m_items->size()-1; + size_t max_beginning = m_items->size() < m_height ? 0 : m_items->size()-m_height; size_t max_visible_highlight = m_beginning+m_height-1; switch (where) { @@ -323,13 +326,15 @@ void Menu<ItemT>::reset() template <typename ItemT> void Menu<ItemT>::clear() { - m_items.clear(); + m_all_items.clear(); + m_filtered_items.clear(); + m_items = &m_all_items; } template <typename ItemT> void Menu<ItemT>::highlight(size_t pos) { - assert(pos < m_items.size()); + assert(pos < m_items->size()); m_highlight = pos; size_t half_height = m_height/2; if (pos < half_height) @@ -345,6 +350,24 @@ size_t Menu<ItemT>::choice() const return m_highlight; } +template <typename ItemT> template <typename FilterPredicate> +bool Menu<ItemT>::applyFilter(FilterPredicate &&p) +{ + m_filtered_items.clear(); + for (const auto &item : m_all_items) + if (p(item)) + m_filtered_items.push_back(item); + m_items = &m_filtered_items; + return !m_filtered_items.empty(); +} + +template <typename ItemT> +void Menu<ItemT>::clearFilter() +{ + m_filtered_items.clear(); + m_items = &m_all_items; +} + } -#endif // NCMPCPP_MENU_IMPL_H
\ No newline at end of file +#endif // NCMPCPP_MENU_IMPL_H |