Update NEWS and bump version to 0.8.2_dev
[ncmpcpp.git] / src / curses / menu_impl.h
blobbcb4fd82ee642e174154c9a411b22bac166c4d77
1 /***************************************************************************
2 * Copyright (C) 2008-2017 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_IMPL_H
22 #define NCMPCPP_MENU_IMPL_H
24 #include "menu.h"
26 namespace NC {
28 template <typename ItemT>
29 Menu<ItemT>::Menu()
31 m_items = &m_all_items;
34 template <typename ItemT>
35 Menu<ItemT>::Menu(size_t startx,
36 size_t starty,
37 size_t width,
38 size_t height,
39 const std::string &title,
40 Color color,
41 Border border)
42 : Window(startx, starty, width, height, title, color, border)
43 , m_item_displayer(nullptr)
44 , m_filter_predicate(nullptr)
45 , m_beginning(0)
46 , m_highlight(0)
47 , m_highlight_enabled(true)
48 , m_cyclic_scroll_enabled(false)
49 , m_autocenter_cursor(false)
51 auto fc = FormattedColor(m_base_color, {Format::Reverse});
52 m_highlight_prefix << fc;
53 m_highlight_suffix << FormattedColor::End<>(fc);
54 m_items = &m_all_items;
57 template <typename ItemT>
58 Menu<ItemT>::Menu(const Menu &rhs)
59 : Window(rhs)
60 , m_item_displayer(rhs.m_item_displayer)
61 , m_filter_predicate(rhs.m_filter_predicate)
62 , m_beginning(rhs.m_beginning)
63 , m_highlight(rhs.m_highlight)
64 , m_highlight_enabled(rhs.m_highlight_enabled)
65 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
66 , m_autocenter_cursor(rhs.m_autocenter_cursor)
67 , m_drawn_position(rhs.m_drawn_position)
68 , m_highlight_prefix(rhs.m_highlight_prefix)
69 , m_highlight_suffix(rhs.m_highlight_suffix)
70 , m_selected_prefix(rhs.m_selected_prefix)
71 , m_selected_suffix(rhs.m_selected_suffix)
73 // TODO: move filtered items
74 m_all_items.reserve(rhs.m_all_items.size());
75 for (const auto &item : rhs.m_all_items)
76 m_all_items.push_back(item.copy());
77 m_items = &m_all_items;
80 template <typename ItemT>
81 Menu<ItemT>::Menu(Menu &&rhs)
82 : Window(rhs)
83 , m_item_displayer(std::move(rhs.m_item_displayer))
84 , m_filter_predicate(std::move(rhs.m_filter_predicate))
85 , m_all_items(std::move(rhs.m_all_items))
86 , m_filtered_items(std::move(rhs.m_filtered_items))
87 , m_beginning(rhs.m_beginning)
88 , m_highlight(rhs.m_highlight)
89 , m_highlight_enabled(rhs.m_highlight_enabled)
90 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
91 , m_autocenter_cursor(rhs.m_autocenter_cursor)
92 , m_drawn_position(rhs.m_drawn_position)
93 , m_highlight_prefix(std::move(rhs.m_highlight_prefix))
94 , m_highlight_suffix(std::move(rhs.m_highlight_suffix))
95 , m_selected_prefix(std::move(rhs.m_selected_prefix))
96 , m_selected_suffix(std::move(rhs.m_selected_suffix))
98 if (rhs.m_items == &rhs.m_all_items)
99 m_items = &m_all_items;
100 else
101 m_items = &m_filtered_items;
104 template <typename ItemT>
105 Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
107 std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs));
108 std::swap(m_item_displayer, rhs.m_item_displayer);
109 std::swap(m_filter_predicate, rhs.m_filter_predicate);
110 std::swap(m_all_items, rhs.m_all_items);
111 std::swap(m_filtered_items, rhs.m_filtered_items);
112 std::swap(m_beginning, rhs.m_beginning);
113 std::swap(m_highlight, rhs.m_highlight);
114 std::swap(m_highlight_enabled, rhs.m_highlight_enabled);
115 std::swap(m_cyclic_scroll_enabled, rhs.m_cyclic_scroll_enabled);
116 std::swap(m_autocenter_cursor, rhs.m_autocenter_cursor);
117 std::swap(m_drawn_position, rhs.m_drawn_position);
118 std::swap(m_highlight_prefix, rhs.m_highlight_prefix);
119 std::swap(m_highlight_suffix, rhs.m_highlight_suffix);
120 std::swap(m_selected_prefix, rhs.m_selected_prefix);
121 std::swap(m_selected_suffix, rhs.m_selected_suffix);
122 if (rhs.m_items == &rhs.m_all_items)
123 m_items = &m_all_items;
124 else
125 m_items = &m_filtered_items;
126 return *this;
129 template <typename ItemT> template <typename ItemDisplayerT>
130 void Menu<ItemT>::setItemDisplayer(ItemDisplayerT &&displayer)
132 m_item_displayer = std::forward<ItemDisplayerT>(displayer);
135 template <typename ItemT>
136 void Menu<ItemT>::resizeList(size_t new_size)
138 m_all_items.resize(new_size);
141 template <typename ItemT>
142 void Menu<ItemT>::addItem(ItemT item, Properties::Type properties)
144 m_all_items.push_back(Item(std::move(item), properties));
147 template <typename ItemT>
148 void Menu<ItemT>::addSeparator()
150 m_all_items.push_back(Item::mkSeparator());
153 template <typename ItemT>
154 void Menu<ItemT>::insertItem(size_t pos, ItemT item, Properties::Type properties)
156 m_all_items.insert(m_all_items.begin()+pos, Item(std::move(item), properties));
159 template <typename ItemT>
160 void Menu<ItemT>::insertSeparator(size_t pos)
162 m_all_items.insert(m_all_items.begin()+pos, Item::mkSeparator());
165 template <typename ItemT>
166 bool Menu<ItemT>::Goto(size_t y)
168 if (!isHighlightable(m_beginning+y))
169 return false;
170 m_highlight = m_beginning+y;
171 return true;
174 template <typename ItemT>
175 void Menu<ItemT>::refresh()
177 if (m_items->empty())
179 Window::clear();
180 Window::refresh();
181 return;
184 size_t max_beginning = 0;
185 if (m_items->size() > m_height)
186 max_beginning = m_items->size() - m_height;
187 m_beginning = std::min(m_beginning, max_beginning);
189 // if highlighted position is off the screen, make it visible
190 m_highlight = std::min(m_highlight, m_beginning+m_height-1);
191 // if highlighted position is invalid, correct it
192 m_highlight = std::min(m_highlight, m_items->size()-1);
194 if (!isHighlightable(m_highlight))
196 scroll(Scroll::Up);
197 if (!isHighlightable(m_highlight))
198 scroll(Scroll::Down);
201 size_t line = 0;
202 const size_t end_ = m_beginning+m_height;
203 m_drawn_position = m_beginning;
204 for (; m_drawn_position < end_; ++m_drawn_position, ++line)
206 goToXY(0, line);
207 if (m_drawn_position >= m_items->size())
209 for (; line < m_height; ++line)
210 mvwhline(m_window, line, 0, NC::Key::Space, m_width);
211 break;
213 if ((*m_items)[m_drawn_position].isSeparator())
215 mvwhline(m_window, line, 0, 0, m_width);
216 continue;
218 if (m_highlight_enabled && m_drawn_position == m_highlight)
219 *this << m_highlight_prefix;
220 if ((*m_items)[m_drawn_position].isSelected())
221 *this << m_selected_prefix;
222 *this << NC::TermManip::ClearToEOL;
223 if (m_item_displayer)
224 m_item_displayer(*this);
225 if ((*m_items)[m_drawn_position].isSelected())
226 *this << m_selected_suffix;
227 if (m_highlight_enabled && m_drawn_position == m_highlight)
228 *this << m_highlight_suffix;
230 Window::refresh();
233 template <typename ItemT>
234 void Menu<ItemT>::scroll(Scroll where)
236 if (m_items->empty())
237 return;
238 size_t max_highlight = m_items->size()-1;
239 size_t max_beginning = m_items->size() < m_height ? 0 : m_items->size()-m_height;
240 size_t max_visible_highlight = m_beginning+m_height-1;
241 switch (where)
243 case Scroll::Up:
245 if (m_highlight <= m_beginning && m_highlight > 0)
246 --m_beginning;
247 if (m_highlight == 0)
249 if (m_cyclic_scroll_enabled)
250 return scroll(Scroll::End);
251 break;
253 else
254 --m_highlight;
255 if (!isHighlightable(m_highlight))
256 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
257 break;
259 case Scroll::Down:
261 if (m_highlight >= max_visible_highlight && m_highlight < max_highlight)
262 ++m_beginning;
263 if (m_highlight == max_highlight)
265 if (m_cyclic_scroll_enabled)
266 return scroll(Scroll::Home);
267 break;
269 else
270 ++m_highlight;
271 if (!isHighlightable(m_highlight))
272 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
273 break;
275 case Scroll::PageUp:
277 if (m_cyclic_scroll_enabled && m_highlight == 0)
278 return scroll(Scroll::End);
279 if (m_highlight < m_height)
280 m_highlight = 0;
281 else
282 m_highlight -= m_height;
283 if (m_beginning < m_height)
284 m_beginning = 0;
285 else
286 m_beginning -= m_height;
287 if (!isHighlightable(m_highlight))
288 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
289 break;
291 case Scroll::PageDown:
293 if (m_cyclic_scroll_enabled && m_highlight == max_highlight)
294 return scroll(Scroll::Home);
295 m_highlight += m_height;
296 m_beginning += m_height;
297 m_beginning = std::min(m_beginning, max_beginning);
298 m_highlight = std::min(m_highlight, max_highlight);
299 if (!isHighlightable(m_highlight))
300 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
301 break;
303 case Scroll::Home:
305 m_highlight = 0;
306 m_beginning = 0;
307 if (!isHighlightable(m_highlight))
308 scroll(Scroll::Down);
309 break;
311 case Scroll::End:
313 m_highlight = max_highlight;
314 m_beginning = max_beginning;
315 if (!isHighlightable(m_highlight))
316 scroll(Scroll::Up);
317 break;
320 if (m_autocenter_cursor)
321 highlight(m_highlight);
324 template <typename ItemT>
325 void Menu<ItemT>::reset()
327 m_highlight = 0;
328 m_beginning = 0;
331 template <typename ItemT>
332 void Menu<ItemT>::clear()
334 // Don't clear filter related stuff here.
335 m_all_items.clear();
336 m_filtered_items.clear();
339 template <typename ItemT>
340 void Menu<ItemT>::highlight(size_t pos)
342 assert(pos < m_items->size());
343 m_highlight = pos;
344 size_t half_height = m_height/2;
345 if (pos < half_height)
346 m_beginning = 0;
347 else
348 m_beginning = pos-half_height;
351 template <typename ItemT>
352 size_t Menu<ItemT>::choice() const
354 assert(!empty());
355 return m_highlight;
358 template <typename ItemT> template <typename PredicateT>
359 void Menu<ItemT>::applyFilter(PredicateT &&pred)
361 m_filter_predicate = std::forward<PredicateT>(pred);
362 m_filtered_items.clear();
364 for (const auto &item : m_all_items)
365 if (m_filter_predicate(item))
366 m_filtered_items.push_back(item);
368 m_items = &m_filtered_items;
371 template <typename ItemT>
372 void Menu<ItemT>::reapplyFilter()
374 applyFilter(m_filter_predicate);
377 template <typename ItemT> template <typename TargetT>
378 const TargetT *Menu<ItemT>::filterPredicate() const
380 return m_filter_predicate.template target<TargetT>();
383 template <typename ItemT>
384 void Menu<ItemT>::clearFilter()
386 m_filter_predicate = nullptr;
387 m_filtered_items.clear();
388 m_items = &m_all_items;
393 #endif // NCMPCPP_MENU_IMPL_H