1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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
30 #include "strbuffer.h"
35 /// This template class is generic menu capable of
36 /// holding any std::vector compatible values.
37 template <typename ItemT
> class Menu
: public Window
46 friend class Menu
<ItemT
>;
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
; }
67 static Item
mkSeparator()
70 item
.m_is_separator
= true;
81 template <typename ValueT
, typename BaseIterator
> class ItemIterator
82 : public std::iterator
<std::random_access_iterator_tag
, ValueT
>
84 friend class Menu
<ItemT
>;
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
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
; }
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
153 typedef ItemIterator
<
154 const Item
, typename
std::vector
<ItemProxy
>::const_iterator
157 typedef std::reverse_iterator
<Iterator
> ReverseIterator
;
158 typedef std::reverse_iterator
<ConstIterator
> ConstReverseIterator
;
160 typedef ItemIterator
<
161 ItemT
, typename
std::vector
<ItemProxy
>::iterator
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
;
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
);
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
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
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();
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.
281 virtual void clear() OVERRIDE
;
283 /// Sets highlighted position to 0
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
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()
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
¤t() { return *(*m_options_ptr
)[m_highlight
]; }
345 /// @return const reference to curently highlighted object
346 const Menu
<ItemT
>::Item
¤t() 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()); }
394 typedef Item element_type
;
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
; }
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
;
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
,
444 const std::string
&title
,
447 : Window(startx
, starty
, width
, height
, title
, color
, border
),
449 m_options_ptr(&m_options
),
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
)
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
)
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
;
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
;
531 m_options_ptr
= &m_filtered_options
;
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();
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
))
592 m_highlight
= m_beginning
+y
;
596 template <typename ItemT
>
597 void Menu
<ItemT
>::refresh()
599 if (m_options_ptr
->empty())
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
))
619 if (!isHighlightable(m_highlight
))
620 scroll(Scroll::Down
);
624 m_drawn_position
= m_beginning
;
625 for (size_t &i
= m_drawn_position
; i
< m_beginning
+m_height
; ++i
, ++line
)
628 if (i
>= m_options_ptr
->size())
630 for (; line
< m_height
; ++line
)
631 mvwhline(m_window
, line
, 0, KEY_SPACE
, m_width
);
634 if ((*m_options_ptr
)[i
]->isSeparator())
636 mvwhline(m_window
, line
, 0, 0, m_width
);
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
)
656 *this << Format::NoReverse
;
658 if ((*m_options_ptr
)[i
]->isBold())
659 *this << Format::NoBold
;
664 template <typename ItemT
>
665 void Menu
<ItemT
>::scroll(Scroll where
)
667 if (m_options_ptr
->empty())
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;
676 if (m_highlight
<= m_beginning
&& m_highlight
> 0)
678 if (m_highlight
== 0)
680 if (m_cyclic_scroll_enabled
)
681 return scroll(Scroll::End
);
686 if (!isHighlightable(m_highlight
))
687 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
692 if (m_highlight
>= max_visible_highlight
&& m_highlight
< max_highlight
)
694 if (m_highlight
== max_highlight
)
696 if (m_cyclic_scroll_enabled
)
697 return scroll(Scroll::Home
);
702 if (!isHighlightable(m_highlight
))
703 scroll(m_highlight
== max_highlight
&& !m_cyclic_scroll_enabled
? Scroll::Up
: Scroll::Down
);
708 if (m_cyclic_scroll_enabled
&& m_highlight
== 0)
709 return scroll(Scroll::End
);
710 if (m_highlight
< m_height
)
713 m_highlight
-= m_height
;
714 if (m_beginning
< m_height
)
717 m_beginning
-= m_height
;
718 if (!isHighlightable(m_highlight
))
719 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
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
);
738 if (!isHighlightable(m_highlight
))
739 scroll(Scroll::Down
);
744 m_highlight
= max_highlight
;
745 m_beginning
= max_beginning
;
746 if (!isHighlightable(m_highlight
))
751 if (m_autocenter_cursor
)
752 highlight(m_highlight
);
755 template <typename ItemT
>
756 void Menu
<ItemT
>::reset()
762 template <typename ItemT
>
763 void Menu
<ItemT
>::clear()
765 clearFilterResults();
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());
776 size_t half_height
= m_height
/2;
777 if (pos
< half_height
)
780 m_beginning
= pos
-half_height
;
783 template <typename ItemT
>
784 size_t Menu
<ItemT
>::choice() const
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();
796 for (auto it
= first
; it
!= last
; ++it
)
798 m_filtered_options
.push_back(*it
.base());
799 if (m_filtered_options
== m_options
)
800 m_filtered_options
.clear();
802 m_options_ptr
= &m_filtered_options
;
805 template <typename ItemT
>
806 void Menu
<ItemT
>::applyCurrentFilter(ConstIterator first
, ConstIterator last
)
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()
826 template <typename ItemT
>
827 bool Menu
<ItemT
>::search(ConstIterator first
, ConstIterator last
, const FilterFunction
&f
)
829 m_found_positions
.clear();
831 for (auto it
= first
; it
!= last
; ++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())
853 auto next
= m_found_positions
.upper_bound(m_highlight
);
854 if (next
!= m_found_positions
.end())
857 highlight(*m_found_positions
.begin());
860 template <typename ItemT
>
861 void Menu
<ItemT
>::prevFound(bool wrap
)
863 if (m_found_positions
.empty())
865 auto prev
= m_found_positions
.lower_bound(m_highlight
);
866 if (prev
!= m_found_positions
.begin())
869 highlight(*m_found_positions
.rbegin());
874 #endif // NCMPCPP_MENU_H