status: store status fields seperately
[ncmpcpp.git] / src / menu.h
blob2321953e78047fc4f916013f71582ec880ba6c42
1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #ifndef NCMPCPP_MENU_H
22 #define NCMPCPP_MENU_H
24 #include <cassert>
25 #include <functional>
26 #include <iterator>
27 #include <memory>
28 #include <set>
30 #include "strbuffer.h"
31 #include "window.h"
33 namespace NC {
35 /// This template class is generic menu capable of
36 /// holding any std::vector compatible values.
37 template <typename ItemT> class Menu : public Window
39 struct ItemProxy;
41 public:
42 struct Item
44 typedef ItemT Type;
46 friend class Menu<ItemT>;
48 Item()
49 : m_is_bold(false), m_is_selected(false), m_is_inactive(false), m_is_separator(false) { }
50 Item(ItemT value_, bool is_bold, bool is_inactive)
51 : m_value(value_), m_is_bold(is_bold), m_is_selected(false), m_is_inactive(is_inactive), m_is_separator(false) { }
53 ItemT &value() { return m_value; }
54 const ItemT &value() const { return m_value; }
56 void setBold(bool is_bold) { m_is_bold = is_bold; }
57 void setSelected(bool is_selected) { m_is_selected = is_selected; }
58 void setInactive(bool is_inactive) { m_is_inactive = is_inactive; }
59 void setSeparator(bool is_separator) { m_is_separator = is_separator; }
61 bool isBold() const { return m_is_bold; }
62 bool isSelected() const { return m_is_selected; }
63 bool isInactive() const { return m_is_inactive; }
64 bool isSeparator() const { return m_is_separator; }
66 private:
67 static Item mkSeparator()
69 Item item;
70 item.m_is_separator = true;
71 return item;
74 ItemT m_value;
75 bool m_is_bold;
76 bool m_is_selected;
77 bool m_is_inactive;
78 bool m_is_separator;
81 template <typename ValueT, typename BaseIterator> class ItemIterator
82 : public std::iterator<std::random_access_iterator_tag, ValueT>
84 friend class Menu<ItemT>;
86 BaseIterator m_it;
87 explicit ItemIterator(BaseIterator it) : m_it(it) { }
89 // base iterator's value_type doesn't change between const and non-const
90 // version, so we need to strip const off ValueT too for proper template
91 // version to be instantiated.
92 static const bool referenceValue = !std::is_same<
93 typename std::remove_const<ValueT>::type,
94 typename BaseIterator::value_type::element_type
95 >::value;
96 template <typename Result, bool referenceValue> struct getObject { };
97 template <typename Result> struct getObject<Result, true> {
98 static Result &apply(BaseIterator it) { return (*it)->value(); }
100 template <typename Result> struct getObject<Result, false> {
101 static Result &apply(BaseIterator it) { return **it; }
104 public:
105 ItemIterator() { }
107 ValueT &operator*() const { return getObject<ValueT, referenceValue>::apply(m_it); }
108 ValueT *operator->() const { return &getObject<ValueT, referenceValue>::apply(m_it); }
110 ItemIterator &operator++() { ++m_it; return *this; }
111 ItemIterator operator++(int) { return ItemIterator(m_it++); }
113 ItemIterator &operator--() { --m_it; return *this; }
114 ItemIterator operator--(int) { return ItemIterator(m_it--); }
116 ValueT &operator[](ptrdiff_t n) const {
117 return getObject<ValueT, referenceValue>::apply(m_it + n);
120 ItemIterator &operator+=(ptrdiff_t n) { m_it += n; return *this; }
121 ItemIterator operator+(ptrdiff_t n) const { return ItemIterator(m_it + n); }
123 ItemIterator &operator-=(ptrdiff_t n) { m_it -= n; return *this; }
124 ItemIterator operator-(ptrdiff_t n) const { return ItemIterator(m_it - n); }
126 ptrdiff_t operator-(const ItemIterator &rhs) const { return m_it - rhs.m_it; }
128 template <typename Iterator>
129 bool operator==(const Iterator &rhs) const { return m_it == rhs.m_it; }
130 template <typename Iterator>
131 bool operator!=(const Iterator &rhs) const { return m_it != rhs.m_it; }
132 template <typename Iterator>
133 bool operator<(const Iterator &rhs) const { return m_it < rhs.m_it; }
134 template <typename Iterator>
135 bool operator<=(const Iterator &rhs) const { return m_it <= rhs.m_it; }
136 template <typename Iterator>
137 bool operator>(const Iterator &rhs) const { return m_it > rhs.m_it; }
138 template <typename Iterator>
139 bool operator>=(const Iterator &rhs) const { return m_it >= rhs.m_it; }
141 /// non-const to const conversion
142 template <typename Iterator>
143 operator ItemIterator<typename std::add_const<ValueT>::type, Iterator>() {
144 return ItemIterator<typename std::add_const<ValueT>::type, Iterator>(m_it);
147 const BaseIterator &base() const { return m_it; }
150 typedef ItemIterator<
151 Item, typename std::vector<ItemProxy>::iterator
152 > Iterator;
153 typedef ItemIterator<
154 const Item, typename std::vector<ItemProxy>::const_iterator
155 > ConstIterator;
157 typedef std::reverse_iterator<Iterator> ReverseIterator;
158 typedef std::reverse_iterator<ConstIterator> ConstReverseIterator;
160 typedef ItemIterator<
161 ItemT, typename std::vector<ItemProxy>::iterator
162 > ValueIterator;
163 typedef ItemIterator<
164 typename std::add_const<ItemT>::type, typename std::vector<ItemProxy>::const_iterator
165 > ConstValueIterator;
167 typedef std::reverse_iterator<ValueIterator> ReverseValueIterator;
168 typedef std::reverse_iterator<ConstValueIterator> ConstReverseValueIterator;
170 /// Function helper prototype used to display each option on the screen.
171 /// If not set by setItemDisplayer(), menu won't display anything.
172 /// @see setItemDisplayer()
173 typedef std::function<void(Menu<ItemT> &)> ItemDisplayer;
175 typedef std::function<bool(const Item &)> FilterFunction;
177 Menu() { }
179 Menu(size_t startx, size_t starty, size_t width, size_t height,
180 const std::string &title, Color color, Border border);
182 Menu(const Menu &rhs);
183 Menu(Menu &&rhs);
184 Menu &operator=(Menu rhs);
186 /// Sets helper function that is responsible for displaying items
187 /// @param ptr function pointer that matches the ItemDisplayer prototype
188 void setItemDisplayer(const ItemDisplayer &f) { m_item_displayer = f; }
190 /// Reserves the size for internal container (this just calls std::vector::reserve())
191 /// @param size requested size
192 void reserve(size_t size);
194 /// Resizes the list to given size (adequate to std::vector::resize())
195 /// @param size requested size
196 void resizeList(size_t size);
198 /// Adds new option to list
199 /// @param item object that has to be added
200 /// @param is_bold defines the initial state of bold attribute
201 /// @param is_inactive defines the initial state of static attribute
202 void addItem(ItemT item, bool is_bold = 0, bool is_inactive = 0);
204 /// Adds separator to list
205 void addSeparator();
207 /// Inserts new option to list at given position
208 /// @param pos initial position of inserted item
209 /// @param item object that has to be inserted
210 /// @param is_bold defines the initial state of bold attribute
211 /// @param is_inactive defines the initial state of static attribute
212 void insertItem(size_t pos, const ItemT &Item, bool is_bold = 0, bool is_inactive = 0);
214 /// Inserts separator to list at given position
215 /// @param pos initial position of inserted separator
216 void insertSeparator(size_t pos);
218 /// Deletes item from given position
219 /// @param pos given position of item to be deleted
220 void deleteItem(size_t pos);
222 /// Moves the highlighted position to the given line of window
223 /// @param y Y position of menu window to be highlighted
224 /// @return true if the position is reachable, false otherwise
225 bool Goto(size_t y);
227 /// Highlights given position
228 /// @param pos position to be highlighted
229 void highlight(size_t pos);
231 /// @return currently highlighted position
232 size_t choice() const;
234 void filter(ConstIterator first, ConstIterator last, const FilterFunction &f);
236 void applyCurrentFilter(ConstIterator first, ConstIterator last);
238 bool search(ConstIterator first, ConstIterator last, const FilterFunction &f);
240 /// Clears filter results
241 void clearFilterResults();
243 void clearFilter();
245 /// Clears search results
246 void clearSearchResults();
248 /// Moves current position in the list to the next found one
249 /// @param wrap if true, this function will go to the first
250 /// found pos after the last one, otherwise it'll do nothing.
251 void nextFound(bool wrap);
253 /// Moves current position in the list to the previous found one
254 /// @param wrap if true, this function will go to the last
255 /// found pos after the first one, otherwise it'll do nothing.
256 void prevFound(bool wrap);
258 /// @return const reference to currently used filter function
259 const FilterFunction &getFilter() { return m_filter; }
261 /// @return true if list is currently filtered, false otherwise
262 bool isFiltered() { return m_options_ptr == &m_filtered_options; }
264 /// Turns off filtering
265 void showAll() { m_options_ptr = &m_options; }
267 /// Turns on filtering
268 void showFiltered() { m_options_ptr = &m_filtered_options; }
270 /// Refreshes the menu window
271 /// @see Window::refresh()
272 virtual void refresh() OVERRIDE;
274 /// Scrolls by given amount of lines
275 /// @param where indicated where exactly one wants to go
276 /// @see Window::scroll()
277 virtual void scroll(Scroll where) OVERRIDE;
279 /// Cleares all options, used filters etc. It doesn't reset highlighted position though.
280 /// @see reset()
281 virtual void clear() OVERRIDE;
283 /// Sets highlighted position to 0
284 void reset();
286 /// Sets prefix, that is put before each selected item to indicate its selection
287 /// Note that the passed variable is not deleted along with menu object.
288 /// @param b pointer to buffer that contains the prefix
289 void setSelectedPrefix(const Buffer &b) { m_selected_prefix = b; }
291 /// Sets suffix, that is put after each selected item to indicate its selection
292 /// Note that the passed variable is not deleted along with menu object.
293 /// @param b pointer to buffer that contains the suffix
294 void setSelectedSuffix(const Buffer &b) { m_selected_suffix = b; }
296 /// Sets custom color of highlighted position
297 /// @param col custom color
298 void setHighlightColor(Color color) { m_highlight_color = color; }
300 /// @return state of highlighting
301 bool isHighlighted() { return m_highlight_enabled; }
303 /// Turns on/off highlighting
304 /// @param state state of hihglighting
305 void setHighlighting(bool state) { m_highlight_enabled = state; }
307 /// Turns on/off cyclic scrolling
308 /// @param state state of cyclic scrolling
309 void cyclicScrolling(bool state) { m_cyclic_scroll_enabled = state; }
311 /// Turns on/off centered cursor
312 /// @param state state of centered cursor
313 void centeredCursor(bool state) { m_autocenter_cursor = state; }
315 /// Checks if list is empty
316 /// @return true if list is empty, false otherwise
317 /// @see reallyEmpty()
318 bool empty() const { return m_options_ptr->empty(); }
320 /// Checks if list is really empty since Empty() may not
321 /// be accurate if filter is set)
322 /// @return true if list is empty, false otherwise
323 /// @see Empty()
324 bool reallyEmpty() const { return m_options.empty(); }
326 /// @return size of the list
327 size_t size() const { return m_options_ptr->size(); }
329 /// @return currently drawn item. The result is defined only within
330 /// drawing function that is called by refresh()
331 /// @see refresh()
332 ConstIterator drawn() const { return begin() + m_drawn_position; }
334 /// @return reference to last item on the list
335 /// @throw List::InvalidItem if requested item is separator
336 Menu<ItemT>::Item &back() { return *m_options_ptr->back(); }
338 /// @return const reference to last item on the list
339 /// @throw List::InvalidItem if requested item is separator
340 const Menu<ItemT>::Item &back() const { return *m_options_ptr->back(); }
342 /// @return reference to curently highlighted object
343 Menu<ItemT>::Item &current() { return *(*m_options_ptr)[m_highlight]; }
345 /// @return const reference to curently highlighted object
346 const Menu<ItemT>::Item &current() const { return *(*m_options_ptr)[m_highlight]; }
348 /// @param pos requested position
349 /// @return reference to item at given position
350 /// @throw std::out_of_range if given position is out of range
351 Menu<ItemT>::Item &at(size_t pos) { return *m_options_ptr->at(pos); }
353 /// @param pos requested position
354 /// @return const reference to item at given position
355 /// @throw std::out_of_range if given position is out of range
356 const Menu<ItemT>::Item &at(size_t pos) const { return *m_options_ptr->at(pos); }
358 /// @param pos requested position
359 /// @return const reference to item at given position
360 const Menu<ItemT>::Item &operator[](size_t pos) const { return *(*m_options_ptr)[pos]; }
362 /// @param pos requested position
363 /// @return const reference to item at given position
364 Menu<ItemT>::Item &operator[](size_t pos) { return *(*m_options_ptr)[pos]; }
366 Iterator currentI() { return Iterator(m_options_ptr->begin() + m_highlight); }
367 ConstIterator currentI() const { return ConstIterator(m_options_ptr->begin() + m_highlight); }
368 ValueIterator currentVI() { return ValueIterator(m_options_ptr->begin() + m_highlight); }
369 ConstValueIterator currentVI() const { return ConstValueIterator(m_options_ptr->begin() + m_highlight); }
371 Iterator begin() { return Iterator(m_options_ptr->begin()); }
372 ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); }
373 Iterator end() { return Iterator(m_options_ptr->end()); }
374 ConstIterator end() const { return ConstIterator(m_options_ptr->end()); }
376 ReverseIterator rbegin() { return ReverseIterator(end()); }
377 ConstReverseIterator rbegin() const { return ConstReverseIterator(end()); }
378 ReverseIterator rend() { return ReverseIterator(begin()); }
379 ConstReverseIterator rend() const { return ConstReverseIterator(begin()); }
381 ValueIterator beginV() { return ValueIterator(m_options_ptr->begin()); }
382 ConstValueIterator beginV() const { return ConstValueIterator(m_options_ptr->begin()); }
383 ValueIterator endV() { return ValueIterator(m_options_ptr->end()); }
384 ConstValueIterator endV() const { return ConstValueIterator(m_options_ptr->end()); }
386 ReverseValueIterator rbeginV() { return ReverseValueIterator(endV()); }
387 ConstReverseIterator rbeginV() const { return ConstReverseValueIterator(endV()); }
388 ReverseValueIterator rendV() { return ReverseValueIterator(beginV()); }
389 ConstReverseValueIterator rendV() const { return ConstReverseValueIterator(beginV()); }
391 private:
392 struct ItemProxy
394 typedef Item element_type;
396 ItemProxy() { }
397 ItemProxy(Item item) : m_ptr(std::make_shared<Item>(item)) { }
399 Item &operator*() const { return *m_ptr; }
400 Item *operator->() const { return m_ptr.get(); }
402 bool operator==(const ItemProxy &rhs) const { return m_ptr == rhs.m_ptr; }
404 private:
405 std::shared_ptr<Item> m_ptr;
408 bool isHighlightable(size_t pos)
410 return !(*m_options_ptr)[pos]->isSeparator()
411 && !(*m_options_ptr)[pos]->isInactive();
414 ItemDisplayer m_item_displayer;
416 FilterFunction m_filter;
417 FilterFunction m_searcher;
419 std::vector<ItemProxy> *m_options_ptr;
420 std::vector<ItemProxy> m_options;
421 std::vector<ItemProxy> m_filtered_options;
422 std::set<size_t> m_found_positions;
424 size_t m_beginning;
425 size_t m_highlight;
427 Color m_highlight_color;
428 bool m_highlight_enabled;
429 bool m_cyclic_scroll_enabled;
431 bool m_autocenter_cursor;
433 size_t m_drawn_position;
435 Buffer m_selected_prefix;
436 Buffer m_selected_suffix;
439 template <typename ItemT>
440 Menu<ItemT>::Menu(size_t startx,
441 size_t starty,
442 size_t width,
443 size_t height,
444 const std::string &title,
445 Color color,
446 Border border)
447 : Window(startx, starty, width, height, title, color, border),
448 m_item_displayer(0),
449 m_options_ptr(&m_options),
450 m_beginning(0),
451 m_highlight(0),
452 m_highlight_color(m_base_color),
453 m_highlight_enabled(true),
454 m_cyclic_scroll_enabled(false),
455 m_autocenter_cursor(false)
459 template <typename ItemT>
460 Menu<ItemT>::Menu(const Menu &rhs)
461 : Window(rhs)
462 , m_item_displayer(rhs.m_item_displayer)
463 , m_filter(rhs.m_filter)
464 , m_searcher(rhs.m_searcher)
465 , m_found_positions(rhs.m_found_positions)
466 , m_beginning(rhs.m_beginning)
467 , m_highlight(rhs.m_highlight)
468 , m_highlight_color(rhs.m_highlight_color)
469 , m_highlight_enabled(rhs.m_highlight_enabled)
470 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
471 , m_autocenter_cursor(rhs.m_autocenter_cursor)
472 , m_drawn_position(rhs.m_drawn_position)
473 , m_selected_prefix(rhs.m_selected_prefix)
474 , m_selected_suffix(rhs.m_selected_suffix)
476 // there is no way to properly fill m_filtered_options
477 // (if rhs is filtered), so we just don't do it.
478 m_options.reserve(rhs.m_options.size());
479 for (auto it = rhs.m_options.begin(); it != rhs.m_options.end(); ++it)
480 m_options.push_back(ItemProxy(**it));
481 m_options_ptr = &m_options;
484 template <typename ItemT>
485 Menu<ItemT>::Menu(Menu &&rhs)
486 : Window(rhs)
487 , m_item_displayer(rhs.m_item_displayer)
488 , m_filter(rhs.m_filter)
489 , m_searcher(rhs.m_searcher)
490 , m_options(std::move(rhs.m_options))
491 , m_filtered_options(std::move(rhs.m_filtered_options))
492 , m_found_positions(std::move(rhs.m_found_positions))
493 , m_beginning(rhs.m_beginning)
494 , m_highlight(rhs.m_highlight)
495 , m_highlight_color(rhs.m_highlight_color)
496 , m_highlight_enabled(rhs.m_highlight_enabled)
497 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
498 , m_autocenter_cursor(rhs.m_autocenter_cursor)
499 , m_drawn_position(rhs.m_drawn_position)
500 , m_selected_prefix(std::move(rhs.m_selected_prefix))
501 , m_selected_suffix(std::move(rhs.m_selected_suffix))
503 if (rhs.m_options_ptr == &rhs.m_options)
504 m_options_ptr = &m_options;
505 else
506 m_options_ptr = &m_filtered_options;
509 template <typename ItemT>
510 Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
512 std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs));
513 std::swap(m_item_displayer, rhs.m_item_displayer);
514 std::swap(m_filter, rhs.m_filter);
515 std::swap(m_searcher, rhs.m_searcher);
516 std::swap(m_options, rhs.m_options);
517 std::swap(m_filtered_options, rhs.m_filtered_options);
518 std::swap(m_found_positions, rhs.m_found_positions);
519 std::swap(m_beginning, rhs.m_beginning);
520 std::swap(m_highlight, rhs.m_highlight);
521 std::swap(m_highlight_color, rhs.m_highlight_color);
522 std::swap(m_highlight_enabled, rhs.m_highlight_enabled);
523 std::swap(m_cyclic_scroll_enabled, rhs.m_cyclic_scroll_enabled);
524 std::swap(m_autocenter_cursor, rhs.m_autocenter_cursor);
525 std::swap(m_drawn_position, rhs.m_drawn_position);
526 std::swap(m_selected_prefix, rhs.m_selected_prefix);
527 std::swap(m_selected_suffix, rhs.m_selected_suffix);
528 if (rhs.m_options_ptr == &rhs.m_options)
529 m_options_ptr = &m_options;
530 else
531 m_options_ptr = &m_filtered_options;
532 return *this;
535 template <typename ItemT>
536 void Menu<ItemT>::reserve(size_t size_)
538 m_options.reserve(size_);
541 template <typename ItemT>
542 void Menu<ItemT>::resizeList(size_t new_size)
544 if (new_size > m_options.size())
546 size_t old_size = m_options.size();
547 m_options.resize(new_size);
548 for (size_t i = old_size; i < new_size; ++i)
549 m_options[i] = Item();
551 else
552 m_options.resize(new_size);
555 template <typename ItemT>
556 void Menu<ItemT>::addItem(ItemT item, bool is_bold, bool is_inactive)
558 m_options.push_back(Item(std::forward<ItemT>(item), is_bold, is_inactive));
561 template <typename ItemT>
562 void Menu<ItemT>::addSeparator()
564 m_options.push_back(Item::mkSeparator());
567 template <typename ItemT>
568 void Menu<ItemT>::insertItem(size_t pos, const ItemT &item, bool is_bold, bool is_inactive)
570 m_options.insert(m_options.begin()+pos, Item(item, is_bold, is_inactive));
573 template <typename ItemT>
574 void Menu<ItemT>::insertSeparator(size_t pos)
576 m_options.insert(m_options.begin()+pos, Item::mkSeparator());
579 template <typename ItemT>
580 void Menu<ItemT>::deleteItem(size_t pos)
582 assert(m_options_ptr != &m_filtered_options);
583 assert(pos < m_options.size());
584 m_options.erase(m_options.begin()+pos);
587 template <typename ItemT>
588 bool Menu<ItemT>::Goto(size_t y)
590 if (!isHighlightable(m_beginning+y))
591 return false;
592 m_highlight = m_beginning+y;
593 return true;
596 template <typename ItemT>
597 void Menu<ItemT>::refresh()
599 if (m_options_ptr->empty())
601 Window::clear();
602 Window::refresh();
603 return;
606 size_t max_beginning = 0;
607 if (m_options_ptr->size() > m_height)
608 max_beginning = m_options_ptr->size() - m_height;
609 m_beginning = std::min(m_beginning, max_beginning);
611 // if highlighted position is off the screen, make it visible
612 m_highlight = std::min(m_highlight, m_beginning+m_height-1);
613 // if highlighted position is invalid, correct it
614 m_highlight = std::min(m_highlight, m_options_ptr->size()-1);
616 if (!isHighlightable(m_highlight))
618 scroll(Scroll::Up);
619 if (!isHighlightable(m_highlight))
620 scroll(Scroll::Down);
623 size_t line = 0;
624 m_drawn_position = m_beginning;
625 for (size_t &i = m_drawn_position; i < m_beginning+m_height; ++i, ++line)
627 goToXY(0, line);
628 if (i >= m_options_ptr->size())
630 for (; line < m_height; ++line)
631 mvwhline(m_window, line, 0, KEY_SPACE, m_width);
632 break;
634 if ((*m_options_ptr)[i]->isSeparator())
636 mvwhline(m_window, line, 0, 0, m_width);
637 continue;
639 if ((*m_options_ptr)[i]->isBold())
640 *this << Format::Bold;
641 if (m_highlight_enabled && i == m_highlight)
643 *this << Format::Reverse;
644 *this << m_highlight_color;
646 mvwhline(m_window, line, 0, KEY_SPACE, m_width);
647 if ((*m_options_ptr)[i]->isSelected())
648 *this << m_selected_prefix;
649 if (m_item_displayer)
650 m_item_displayer(*this);
651 if ((*m_options_ptr)[i]->isSelected())
652 *this << m_selected_suffix;
653 if (m_highlight_enabled && i == m_highlight)
655 *this << Color::End;
656 *this << Format::NoReverse;
658 if ((*m_options_ptr)[i]->isBold())
659 *this << Format::NoBold;
661 Window::refresh();
664 template <typename ItemT>
665 void Menu<ItemT>::scroll(Scroll where)
667 if (m_options_ptr->empty())
668 return;
669 size_t max_highlight = m_options_ptr->size()-1;
670 size_t max_beginning = m_options_ptr->size() < m_height ? 0 : m_options_ptr->size()-m_height;
671 size_t max_visible_highlight = m_beginning+m_height-1;
672 switch (where)
674 case Scroll::Up:
676 if (m_highlight <= m_beginning && m_highlight > 0)
677 --m_beginning;
678 if (m_highlight == 0)
680 if (m_cyclic_scroll_enabled)
681 return scroll(Scroll::End);
682 break;
684 else
685 --m_highlight;
686 if (!isHighlightable(m_highlight))
687 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
688 break;
690 case Scroll::Down:
692 if (m_highlight >= max_visible_highlight && m_highlight < max_highlight)
693 ++m_beginning;
694 if (m_highlight == max_highlight)
696 if (m_cyclic_scroll_enabled)
697 return scroll(Scroll::Home);
698 break;
700 else
701 ++m_highlight;
702 if (!isHighlightable(m_highlight))
703 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
704 break;
706 case Scroll::PageUp:
708 if (m_cyclic_scroll_enabled && m_highlight == 0)
709 return scroll(Scroll::End);
710 if (m_highlight < m_height)
711 m_highlight = 0;
712 else
713 m_highlight -= m_height;
714 if (m_beginning < m_height)
715 m_beginning = 0;
716 else
717 m_beginning -= m_height;
718 if (!isHighlightable(m_highlight))
719 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
720 break;
722 case Scroll::PageDown:
724 if (m_cyclic_scroll_enabled && m_highlight == max_highlight)
725 return scroll(Scroll::Home);
726 m_highlight += m_height;
727 m_beginning += m_height;
728 m_beginning = std::min(m_beginning, max_beginning);
729 m_highlight = std::min(m_highlight, max_highlight);
730 if (!isHighlightable(m_highlight))
731 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
732 break;
734 case Scroll::Home:
736 m_highlight = 0;
737 m_beginning = 0;
738 if (!isHighlightable(m_highlight))
739 scroll(Scroll::Down);
740 break;
742 case Scroll::End:
744 m_highlight = max_highlight;
745 m_beginning = max_beginning;
746 if (!isHighlightable(m_highlight))
747 scroll(Scroll::Up);
748 break;
751 if (m_autocenter_cursor)
752 highlight(m_highlight);
755 template <typename ItemT>
756 void Menu<ItemT>::reset()
758 m_highlight = 0;
759 m_beginning = 0;
762 template <typename ItemT>
763 void Menu<ItemT>::clear()
765 clearFilterResults();
766 m_options.clear();
767 m_found_positions.clear();
768 m_options_ptr = &m_options;
771 template <typename ItemT>
772 void Menu<ItemT>::highlight(size_t pos)
774 assert(pos < m_options_ptr->size());
775 m_highlight = pos;
776 size_t half_height = m_height/2;
777 if (pos < half_height)
778 m_beginning = 0;
779 else
780 m_beginning = pos-half_height;
783 template <typename ItemT>
784 size_t Menu<ItemT>::choice() const
786 assert(!empty());
787 return m_highlight;
790 template <typename ItemT>
791 void Menu<ItemT>::filter(ConstIterator first, ConstIterator last, const FilterFunction &f)
793 assert(m_options_ptr != &m_filtered_options);
794 clearFilterResults();
795 m_filter = f;
796 for (auto it = first; it != last; ++it)
797 if (m_filter(*it))
798 m_filtered_options.push_back(*it.base());
799 if (m_filtered_options == m_options)
800 m_filtered_options.clear();
801 else
802 m_options_ptr = &m_filtered_options;
805 template <typename ItemT>
806 void Menu<ItemT>::applyCurrentFilter(ConstIterator first, ConstIterator last)
808 assert(m_filter);
809 filter(first, last, m_filter);
813 template <typename ItemT>
814 void Menu<ItemT>::clearFilterResults()
816 m_filtered_options.clear();
817 m_options_ptr = &m_options;
820 template <typename ItemT>
821 void Menu<ItemT>::clearFilter()
823 m_filter = 0;
826 template <typename ItemT>
827 bool Menu<ItemT>::search(ConstIterator first, ConstIterator last, const FilterFunction &f)
829 m_found_positions.clear();
830 m_searcher = f;
831 for (auto it = first; it != last; ++it)
833 if (m_searcher(*it))
835 size_t pos = it-begin();
836 m_found_positions.insert(pos);
839 return !m_found_positions.empty();
842 template <typename ItemT>
843 void Menu<ItemT>::clearSearchResults()
845 m_found_positions.clear();
848 template <typename ItemT>
849 void Menu<ItemT>::nextFound(bool wrap)
851 if (m_found_positions.empty())
852 return;
853 auto next = m_found_positions.upper_bound(m_highlight);
854 if (next != m_found_positions.end())
855 highlight(*next);
856 else if (wrap)
857 highlight(*m_found_positions.begin());
860 template <typename ItemT>
861 void Menu<ItemT>::prevFound(bool wrap)
863 if (m_found_positions.empty())
864 return;
865 auto prev = m_found_positions.lower_bound(m_highlight);
866 if (prev != m_found_positions.begin())
867 highlight(*--prev);
868 else if (wrap)
869 highlight(*m_found_positions.rbegin());
874 #endif // NCMPCPP_MENU_H