status: make idle events handling independent of the order of their arrival
[ncmpcpp.git] / src / scrollpad.cpp
blobe865ceebe380533bf7d25cc8244d720a008bbdb7
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 #include <cassert>
22 #include <boost/regex.hpp>
24 #include "scrollpad.h"
26 namespace {//
28 template <typename PropT>
29 bool regexSearch(NC::Buffer &buf, PropT begin, const std::string &ws, PropT end, size_t id, boost::regex::flag_type flags)
31 try {
32 boost::regex rx(ws, flags);
33 auto first = boost::sregex_iterator(buf.str().begin(), buf.str().end(), rx);
34 auto last = boost::sregex_iterator();
35 bool success = first != last;
36 for (; first != last; ++first)
38 buf.setProperty(first->position(), begin, id);
39 buf.setProperty(first->position() + first->length(), end, id);
41 return success;
42 } catch (boost::bad_expression &e) {
43 std::cerr << "regexSearch: bad_expression: " << e.what() << "\n";
44 return false;
50 namespace NC {//
52 Scrollpad::Scrollpad(size_t startx,
53 size_t starty,
54 size_t width,
55 size_t height,
56 const std::string &title,
57 Color color,
58 Border border)
59 : Window(startx, starty, width, height, title, color, border),
60 m_beginning(0),
61 m_real_height(height)
65 void Scrollpad::refresh()
67 assert(m_real_height >= m_height);
68 size_t max_beginning = m_real_height - m_height;
69 m_beginning = std::min(m_beginning, max_beginning);
70 prefresh(m_window, m_beginning, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
73 void Scrollpad::resize(size_t new_width, size_t new_height)
75 adjustDimensions(new_width, new_height);
76 recreate(new_width, new_height);
77 flush();
80 void Scrollpad::scroll(Scroll where)
82 assert(m_real_height >= m_height);
83 size_t max_beginning = m_real_height - m_height;
84 switch (where)
86 case Scroll::Up:
88 if (m_beginning > 0)
89 --m_beginning;
90 break;
92 case Scroll::Down:
94 if (m_beginning < max_beginning)
95 ++m_beginning;
96 break;
98 case Scroll::PageUp:
100 if (m_beginning > m_height)
101 m_beginning -= m_height;
102 else
103 m_beginning = 0;
104 break;
106 case Scroll::PageDown:
108 m_beginning = std::min(m_beginning + m_height, max_beginning);
109 break;
111 case Scroll::Home:
113 m_beginning = 0;
114 break;
116 case Scroll::End:
118 m_beginning = max_beginning;
119 break;
124 void Scrollpad::clear()
126 m_real_height = m_height;
127 m_buffer.clear();
128 werase(m_window);
129 delwin(m_window);
130 m_window = newpad(m_height, m_width);
131 setTimeout(m_window_timeout);
132 setColor(m_color, m_bg_color);
133 keypad(m_window, 1);
136 const std::string &Scrollpad::buffer()
138 return m_buffer.str();
141 void Scrollpad::flush()
143 auto &w = static_cast<Window &>(*this);
144 const auto &s = m_buffer.str();
145 const auto &ps = m_buffer.properties();
146 auto p = ps.begin();
147 size_t i = 0;
149 auto load_properties = [&]() {
150 for (; p != ps.end() && p->position() == i; ++p)
151 w << *p;
153 auto write_whitespace = [&]() {
154 for (; i < s.length() && iswspace(s[i]); ++i)
156 load_properties();
157 w << s[i];
160 auto write_word = [&](bool load_properties_) {
161 for (; i < s.length() && !iswspace(s[i]); ++i)
163 if (load_properties_)
164 load_properties();
165 w << s[i];
168 auto write_buffer = [&](bool generate_height_only) -> size_t {
169 int new_y;
170 size_t height = 1;
171 size_t old_i;
172 auto old_p = p;
173 int x, y;
174 i = 0;
175 p = ps.begin();
176 y = getY();
177 while (i < s.length())
179 // write all whitespaces.
180 write_whitespace();
182 // if we are generating height, check difference
183 // between previous Y coord and current one and
184 // update height accordingly.
185 if (generate_height_only)
187 new_y = getY();
188 height += new_y - y;
189 y = new_y;
192 if (i == s.length())
193 break;
195 // save current string position state and get current
196 // coordinates as we are before the beginning of a word.
197 old_i = i;
198 old_p = p;
199 x = getX();
200 y = getY();
202 // write word to test if it overflows, but do not load properties
203 // yet since if it overflows, we do not want to load them twice.
204 write_word(false);
206 // restore previous indexes state
207 i = old_i;
208 p = old_p;
210 // get new Y coord to see if word overflew into next line.
211 new_y = getY();
212 if (new_y != y)
214 if (generate_height_only)
216 // if it did, let's update height...
217 ++height;
219 else
221 // ...or go to old coordinates, erase
222 // part of the string from previous line...
223 goToXY(x, y);
224 wclrtoeol(m_window);
227 // ...start at the beginning of next line...
228 ++y;
229 goToXY(0, y);
231 // ...write word again, this time with properties...
232 write_word(true);
234 if (generate_height_only)
236 // ... and check for potential
237 // difference in Y coordinates again.
238 new_y = getY();
239 height += new_y - y;
242 else
244 // word didn't overflow, rewrite it with properties.
245 goToXY(x, y);
246 write_word(true);
249 if (generate_height_only)
251 // move to the first line, since when we do
252 // generation, m_real_height = m_height and we
253 // don't have many lines to use.
254 goToXY(getX(), 0);
255 y = 0;
258 // load remaining properties if there are any
259 for (; p != ps.end(); ++p)
260 w << *p;
261 return height;
263 m_real_height = std::max(write_buffer(true), m_height);
264 if (m_real_height > m_height)
265 recreate(m_width, m_real_height);
266 else
267 werase(m_window);
268 write_buffer(false);
271 void Scrollpad::reset()
273 m_beginning = 0;
276 bool Scrollpad::setProperties(Color begin, const std::string &s, Color end, size_t id, boost::regex::flag_type flags)
278 return regexSearch(m_buffer, begin, s, end, id, flags);
281 bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t id, boost::regex::flag_type flags)
283 return regexSearch(m_buffer, begin, s, end, id, flags);
286 void Scrollpad::removeProperties(size_t id)
288 m_buffer.removeProperties(id);