set SIGWINCH handler before initializing ncurses to avoid races
[ncmpcpp.git] / src / scrollpad.cpp
blob064b03fa035be4718af05647299691be3df21613
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>
23 #include <iostream>
25 #include "scrollpad.h"
27 namespace {//
29 template <typename PropT>
30 bool regexSearch(NC::Buffer &buf, PropT begin, const std::string &ws, PropT end, size_t id, boost::regex::flag_type flags)
32 try {
33 boost::regex rx(ws, flags);
34 auto first = boost::sregex_iterator(buf.str().begin(), buf.str().end(), rx);
35 auto last = boost::sregex_iterator();
36 bool success = first != last;
37 for (; first != last; ++first)
39 buf.setProperty(first->position(), begin, id);
40 buf.setProperty(first->position() + first->length(), end, id);
42 return success;
43 } catch (boost::bad_expression &e) {
44 std::cerr << "regexSearch: bad_expression: " << e.what() << "\n";
45 return false;
51 namespace NC {//
53 Scrollpad::Scrollpad(size_t startx,
54 size_t starty,
55 size_t width,
56 size_t height,
57 const std::string &title,
58 Color color,
59 Border border)
60 : Window(startx, starty, width, height, title, color, border),
61 m_beginning(0),
62 m_real_height(height)
66 void Scrollpad::refresh()
68 assert(m_real_height >= m_height);
69 size_t max_beginning = m_real_height - m_height;
70 m_beginning = std::min(m_beginning, max_beginning);
71 prefresh(m_window, m_beginning, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
74 void Scrollpad::resize(size_t new_width, size_t new_height)
76 adjustDimensions(new_width, new_height);
77 recreate(new_width, new_height);
78 flush();
81 void Scrollpad::scroll(Scroll where)
83 assert(m_real_height >= m_height);
84 size_t max_beginning = m_real_height - m_height;
85 switch (where)
87 case Scroll::Up:
89 if (m_beginning > 0)
90 --m_beginning;
91 break;
93 case Scroll::Down:
95 if (m_beginning < max_beginning)
96 ++m_beginning;
97 break;
99 case Scroll::PageUp:
101 if (m_beginning > m_height)
102 m_beginning -= m_height;
103 else
104 m_beginning = 0;
105 break;
107 case Scroll::PageDown:
109 m_beginning = std::min(m_beginning + m_height, max_beginning);
110 break;
112 case Scroll::Home:
114 m_beginning = 0;
115 break;
117 case Scroll::End:
119 m_beginning = max_beginning;
120 break;
125 void Scrollpad::clear()
127 m_real_height = m_height;
128 m_buffer.clear();
129 werase(m_window);
130 delwin(m_window);
131 m_window = newpad(m_height, m_width);
132 setTimeout(m_window_timeout);
133 setColor(m_color, m_bg_color);
134 keypad(m_window, 1);
137 const std::string &Scrollpad::buffer()
139 return m_buffer.str();
142 void Scrollpad::flush()
144 auto &w = static_cast<Window &>(*this);
145 const auto &s = m_buffer.str();
146 const auto &ps = m_buffer.properties();
147 auto p = ps.begin();
148 size_t i = 0;
150 auto load_properties = [&]() {
151 for (; p != ps.end() && p->position() == i; ++p)
152 w << *p;
154 auto write_whitespace = [&]() {
155 for (; i < s.length() && iswspace(s[i]); ++i)
157 load_properties();
158 w << s[i];
161 auto write_word = [&](bool load_properties_) {
162 for (; i < s.length() && !iswspace(s[i]); ++i)
164 if (load_properties_)
165 load_properties();
166 w << s[i];
169 auto write_buffer = [&](bool generate_height_only) -> size_t {
170 int new_y;
171 size_t height = 1;
172 size_t old_i;
173 auto old_p = p;
174 int x, y;
175 i = 0;
176 p = ps.begin();
177 y = getY();
178 while (i < s.length())
180 // write all whitespaces.
181 write_whitespace();
183 // if we are generating height, check difference
184 // between previous Y coord and current one and
185 // update height accordingly.
186 if (generate_height_only)
188 new_y = getY();
189 height += new_y - y;
190 y = new_y;
193 if (i == s.length())
194 break;
196 // save current string position state and get current
197 // coordinates as we are before the beginning of a word.
198 old_i = i;
199 old_p = p;
200 x = getX();
201 y = getY();
203 // write word to test if it overflows, but do not load properties
204 // yet since if it overflows, we do not want to load them twice.
205 write_word(false);
207 // restore previous indexes state
208 i = old_i;
209 p = old_p;
211 // get new Y coord to see if word overflew into next line.
212 new_y = getY();
213 if (new_y != y)
215 if (generate_height_only)
217 // if it did, let's update height...
218 ++height;
220 else
222 // ...or go to old coordinates, erase
223 // part of the string from previous line...
224 goToXY(x, y);
225 wclrtoeol(m_window);
228 // ...start at the beginning of next line...
229 ++y;
230 goToXY(0, y);
232 // ...write word again, this time with properties...
233 write_word(true);
235 if (generate_height_only)
237 // ... and check for potential
238 // difference in Y coordinates again.
239 new_y = getY();
240 height += new_y - y;
243 else
245 // word didn't overflow, rewrite it with properties.
246 goToXY(x, y);
247 write_word(true);
250 if (generate_height_only)
252 // move to the first line, since when we do
253 // generation, m_real_height = m_height and we
254 // don't have many lines to use.
255 goToXY(getX(), 0);
256 y = 0;
259 // load remaining properties if there are any
260 for (; p != ps.end(); ++p)
261 w << *p;
262 return height;
264 m_real_height = std::max(write_buffer(true), m_height);
265 if (m_real_height > m_height)
266 recreate(m_width, m_real_height);
267 else
268 werase(m_window);
269 write_buffer(false);
272 void Scrollpad::reset()
274 m_beginning = 0;
277 bool Scrollpad::setProperties(Color begin, const std::string &s, Color end, size_t id, boost::regex::flag_type flags)
279 return regexSearch(m_buffer, begin, s, end, id, flags);
282 bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t id, boost::regex::flag_type flags)
284 return regexSearch(m_buffer, begin, s, end, id, flags);
287 void Scrollpad::removeProperties(size_t id)
289 m_buffer.removeProperties(id);