Move ncurses related files to curses directory
[ncmpcpp.git] / src / curses / menu_impl.h
blob451886b719595e89e8a98ca98df818183c66293d
1 /***************************************************************************
2 * Copyright (C) 2008-2016 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, std::move(color), border)
43 , m_item_displayer(nullptr)
44 , m_filter_predicate(nullptr)
45 , m_beginning(0)
46 , m_highlight(0)
47 , m_highlight_color(m_base_color)
48 , m_highlight_enabled(true)
49 , m_cyclic_scroll_enabled(false)
50 , m_autocenter_cursor(false)
52 m_items = &m_all_items;
55 template <typename ItemT>
56 Menu<ItemT>::Menu(const Menu &rhs)
57 : Window(rhs)
58 , m_item_displayer(rhs.m_item_displayer)
59 , m_filter_predicate(rhs.m_filter_predicate)
60 , m_beginning(rhs.m_beginning)
61 , m_highlight(rhs.m_highlight)
62 , m_highlight_color(rhs.m_highlight_color)
63 , m_highlight_enabled(rhs.m_highlight_enabled)
64 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
65 , m_autocenter_cursor(rhs.m_autocenter_cursor)
66 , m_drawn_position(rhs.m_drawn_position)
67 , m_selected_prefix(rhs.m_selected_prefix)
68 , m_selected_suffix(rhs.m_selected_suffix)
70 // TODO: move filtered items
71 m_all_items.reserve(rhs.m_all_items.size());
72 for (const auto &item : rhs.m_all_items)
73 m_all_items.push_back(item.copy());
74 m_items = &m_all_items;
77 template <typename ItemT>
78 Menu<ItemT>::Menu(Menu &&rhs)
79 : Window(rhs)
80 , m_item_displayer(std::move(rhs.m_item_displayer))
81 , m_filter_predicate(std::move(rhs.m_filter_predicate))
82 , m_all_items(std::move(rhs.m_all_items))
83 , m_filtered_items(std::move(rhs.m_filtered_items))
84 , m_beginning(rhs.m_beginning)
85 , m_highlight(rhs.m_highlight)
86 , m_highlight_color(rhs.m_highlight_color)
87 , m_highlight_enabled(rhs.m_highlight_enabled)
88 , m_cyclic_scroll_enabled(rhs.m_cyclic_scroll_enabled)
89 , m_autocenter_cursor(rhs.m_autocenter_cursor)
90 , m_drawn_position(rhs.m_drawn_position)
91 , m_selected_prefix(std::move(rhs.m_selected_prefix))
92 , m_selected_suffix(std::move(rhs.m_selected_suffix))
94 if (rhs.m_items == &rhs.m_all_items)
95 m_items = &m_all_items;
96 else
97 m_items = &m_filtered_items;
100 template <typename ItemT>
101 Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
103 std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs));
104 std::swap(m_item_displayer, rhs.m_item_displayer);
105 std::swap(m_filter_predicate, rhs.m_filter_predicate);
106 std::swap(m_all_items, rhs.m_all_items);
107 std::swap(m_filtered_items, rhs.m_filtered_items);
108 std::swap(m_beginning, rhs.m_beginning);
109 std::swap(m_highlight, rhs.m_highlight);
110 std::swap(m_highlight_color, rhs.m_highlight_color);
111 std::swap(m_highlight_enabled, rhs.m_highlight_enabled);
112 std::swap(m_cyclic_scroll_enabled, rhs.m_cyclic_scroll_enabled);
113 std::swap(m_autocenter_cursor, rhs.m_autocenter_cursor);
114 std::swap(m_drawn_position, rhs.m_drawn_position);
115 std::swap(m_selected_prefix, rhs.m_selected_prefix);
116 std::swap(m_selected_suffix, rhs.m_selected_suffix);
117 if (rhs.m_items == &rhs.m_all_items)
118 m_items = &m_all_items;
119 else
120 m_items = &m_filtered_items;
121 return *this;
124 template <typename ItemT> template <typename ItemDisplayerT>
125 void Menu<ItemT>::setItemDisplayer(ItemDisplayerT &&displayer)
127 m_item_displayer = std::forward<ItemDisplayerT>(displayer);
130 template <typename ItemT>
131 void Menu<ItemT>::resizeList(size_t new_size)
133 m_all_items.resize(new_size);
136 template <typename ItemT>
137 void Menu<ItemT>::addItem(ItemT item, Properties::Type properties)
139 m_all_items.push_back(Item(std::move(item), properties));
142 template <typename ItemT>
143 void Menu<ItemT>::addSeparator()
145 m_all_items.push_back(Item::mkSeparator());
148 template <typename ItemT>
149 void Menu<ItemT>::insertItem(size_t pos, ItemT item, Properties::Type properties)
151 m_all_items.insert(m_all_items.begin()+pos, Item(std::move(item), properties));
154 template <typename ItemT>
155 void Menu<ItemT>::insertSeparator(size_t pos)
157 m_all_items.insert(m_all_items.begin()+pos, Item::mkSeparator());
160 template <typename ItemT>
161 bool Menu<ItemT>::Goto(size_t y)
163 if (!isHighlightable(m_beginning+y))
164 return false;
165 m_highlight = m_beginning+y;
166 return true;
169 template <typename ItemT>
170 void Menu<ItemT>::refresh()
172 if (m_items->empty())
174 Window::clear();
175 Window::refresh();
176 return;
179 size_t max_beginning = 0;
180 if (m_items->size() > m_height)
181 max_beginning = m_items->size() - m_height;
182 m_beginning = std::min(m_beginning, max_beginning);
184 // if highlighted position is off the screen, make it visible
185 m_highlight = std::min(m_highlight, m_beginning+m_height-1);
186 // if highlighted position is invalid, correct it
187 m_highlight = std::min(m_highlight, m_items->size()-1);
189 if (!isHighlightable(m_highlight))
191 scroll(Scroll::Up);
192 if (!isHighlightable(m_highlight))
193 scroll(Scroll::Down);
196 size_t line = 0;
197 const size_t end_ = m_beginning+m_height;
198 m_drawn_position = m_beginning;
199 for (; m_drawn_position < end_; ++m_drawn_position, ++line)
201 goToXY(0, line);
202 if (m_drawn_position >= m_items->size())
204 for (; line < m_height; ++line)
205 mvwhline(m_window, line, 0, NC::Key::Space, m_width);
206 break;
208 if ((*m_items)[m_drawn_position].isSeparator())
210 mvwhline(m_window, line, 0, 0, m_width);
211 continue;
213 if ((*m_items)[m_drawn_position].isBold())
214 *this << Format::Bold;
215 if (m_highlight_enabled && m_drawn_position == m_highlight)
217 *this << Format::Reverse;
218 *this << m_highlight_color;
220 mvwhline(m_window, line, 0, NC::Key::Space, m_width);
221 if ((*m_items)[m_drawn_position].isSelected())
222 *this << m_selected_prefix;
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)
229 *this << Color::End;
230 *this << Format::NoReverse;
232 if ((*m_items)[m_drawn_position].isBold())
233 *this << Format::NoBold;
235 Window::refresh();
238 template <typename ItemT>
239 void Menu<ItemT>::scroll(Scroll where)
241 if (m_items->empty())
242 return;
243 size_t max_highlight = m_items->size()-1;
244 size_t max_beginning = m_items->size() < m_height ? 0 : m_items->size()-m_height;
245 size_t max_visible_highlight = m_beginning+m_height-1;
246 switch (where)
248 case Scroll::Up:
250 if (m_highlight <= m_beginning && m_highlight > 0)
251 --m_beginning;
252 if (m_highlight == 0)
254 if (m_cyclic_scroll_enabled)
255 return scroll(Scroll::End);
256 break;
258 else
259 --m_highlight;
260 if (!isHighlightable(m_highlight))
261 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
262 break;
264 case Scroll::Down:
266 if (m_highlight >= max_visible_highlight && m_highlight < max_highlight)
267 ++m_beginning;
268 if (m_highlight == max_highlight)
270 if (m_cyclic_scroll_enabled)
271 return scroll(Scroll::Home);
272 break;
274 else
275 ++m_highlight;
276 if (!isHighlightable(m_highlight))
277 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
278 break;
280 case Scroll::PageUp:
282 if (m_cyclic_scroll_enabled && m_highlight == 0)
283 return scroll(Scroll::End);
284 if (m_highlight < m_height)
285 m_highlight = 0;
286 else
287 m_highlight -= m_height;
288 if (m_beginning < m_height)
289 m_beginning = 0;
290 else
291 m_beginning -= m_height;
292 if (!isHighlightable(m_highlight))
293 scroll(m_highlight == 0 && !m_cyclic_scroll_enabled ? Scroll::Down : Scroll::Up);
294 break;
296 case Scroll::PageDown:
298 if (m_cyclic_scroll_enabled && m_highlight == max_highlight)
299 return scroll(Scroll::Home);
300 m_highlight += m_height;
301 m_beginning += m_height;
302 m_beginning = std::min(m_beginning, max_beginning);
303 m_highlight = std::min(m_highlight, max_highlight);
304 if (!isHighlightable(m_highlight))
305 scroll(m_highlight == max_highlight && !m_cyclic_scroll_enabled ? Scroll::Up : Scroll::Down);
306 break;
308 case Scroll::Home:
310 m_highlight = 0;
311 m_beginning = 0;
312 if (!isHighlightable(m_highlight))
313 scroll(Scroll::Down);
314 break;
316 case Scroll::End:
318 m_highlight = max_highlight;
319 m_beginning = max_beginning;
320 if (!isHighlightable(m_highlight))
321 scroll(Scroll::Up);
322 break;
325 if (m_autocenter_cursor)
326 highlight(m_highlight);
329 template <typename ItemT>
330 void Menu<ItemT>::reset()
332 m_highlight = 0;
333 m_beginning = 0;
336 template <typename ItemT>
337 void Menu<ItemT>::clear()
339 // Don't clear filter related stuff here.
340 m_all_items.clear();
341 m_filtered_items.clear();
344 template <typename ItemT>
345 void Menu<ItemT>::highlight(size_t pos)
347 assert(pos < m_items->size());
348 m_highlight = pos;
349 size_t half_height = m_height/2;
350 if (pos < half_height)
351 m_beginning = 0;
352 else
353 m_beginning = pos-half_height;
356 template <typename ItemT>
357 size_t Menu<ItemT>::choice() const
359 assert(!empty());
360 return m_highlight;
363 template <typename ItemT> template <typename PredicateT>
364 void Menu<ItemT>::applyFilter(PredicateT &&pred)
366 m_filter_predicate = std::forward<PredicateT>(pred);
367 m_filtered_items.clear();
369 for (const auto &item : m_all_items)
370 if (m_filter_predicate(item))
371 m_filtered_items.push_back(item);
373 m_items = &m_filtered_items;
376 template <typename ItemT>
377 void Menu<ItemT>::reapplyFilter()
379 applyFilter(m_filter_predicate);
382 template <typename ItemT> template <typename TargetT>
383 const TargetT *Menu<ItemT>::filterPredicate() const
385 return m_filter_predicate.template target<TargetT>();
388 template <typename ItemT>
389 void Menu<ItemT>::clearFilter()
391 m_filter_predicate = nullptr;
392 m_filtered_items.clear();
393 m_items = &m_all_items;
398 #endif // NCMPCPP_MENU_IMPL_H