Update NEWS and bump version to 0.8.2_dev
[ncmpcpp.git] / src / curses / scrollpad.cpp
blobac21047ffaa9f85fbe88c9a3830343ab441b4846
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 #include <cassert>
22 #include <boost/regex.hpp>
23 #include <iostream>
25 #include "curses/scrollpad.h"
26 #include "utility/storage_kind.h"
28 namespace {
30 template <typename BeginT, typename EndT>
31 bool regexSearch(NC::Buffer &buf, const BeginT &begin, const std::string &ws,
32 const EndT &end, boost::regex::flag_type flags, size_t id)
34 try {
35 boost::regex rx(ws, flags);
36 auto first = boost::sregex_iterator(buf.str().begin(), buf.str().end(), rx);
37 auto last = boost::sregex_iterator();
38 bool success = first != last;
39 for (; first != last; ++first)
41 buf.addProperty(first->position(), begin, id);
42 buf.addProperty(first->position() + first->length(), end, id);
44 return success;
45 } catch (boost::bad_expression &e) {
46 std::cerr << "regexSearch: bad_expression: " << e.what() << "\n";
47 return false;
53 namespace NC {
55 Scrollpad::Scrollpad(size_t startx,
56 size_t starty,
57 size_t width,
58 size_t height,
59 const std::string &title,
60 Color color,
61 Border border)
62 : Window(startx, starty, width, height, title, color, border),
63 m_beginning(0),
64 m_real_height(height)
68 void Scrollpad::refresh()
70 assert(m_real_height >= m_height);
71 size_t max_beginning = m_real_height - m_height;
72 m_beginning = std::min(m_beginning, max_beginning);
73 prefresh(m_window, m_beginning, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
76 void Scrollpad::resize(size_t new_width, size_t new_height)
78 adjustDimensions(new_width, new_height);
79 recreate(new_width, new_height);
80 flush();
83 void Scrollpad::scroll(Scroll where)
85 assert(m_real_height >= m_height);
86 size_t max_beginning = m_real_height - m_height;
87 switch (where)
89 case Scroll::Up:
91 if (m_beginning > 0)
92 --m_beginning;
93 break;
95 case Scroll::Down:
97 if (m_beginning < max_beginning)
98 ++m_beginning;
99 break;
101 case Scroll::PageUp:
103 if (m_beginning > m_height)
104 m_beginning -= m_height;
105 else
106 m_beginning = 0;
107 break;
109 case Scroll::PageDown:
111 m_beginning = std::min(m_beginning + m_height, max_beginning);
112 break;
114 case Scroll::Home:
116 m_beginning = 0;
117 break;
119 case Scroll::End:
121 m_beginning = max_beginning;
122 break;
127 void Scrollpad::clear()
129 m_real_height = m_height;
130 m_buffer.clear();
131 Window::clear();
134 const std::string &Scrollpad::buffer()
136 return m_buffer.str();
139 void Scrollpad::flush()
141 auto &w = static_cast<Window &>(*this);
142 const auto &s = m_buffer.str();
143 const auto &ps = m_buffer.properties();
144 auto p = ps.begin();
145 size_t i = 0;
147 auto load_properties = [&]() {
148 for (; p != ps.end() && p->first == i; ++p)
149 w << p->second;
151 auto write_whitespace = [&]() {
152 for (; i < s.length() && iswspace(s[i]); ++i)
154 load_properties();
155 w << s[i];
158 auto write_word = [&](bool load_properties_) {
159 for (; i < s.length() && !iswspace(s[i]); ++i)
161 if (load_properties_)
162 load_properties();
163 w << s[i];
166 auto write_buffer = [&](bool generate_height_only) -> size_t {
167 int new_y;
168 size_t height = 1;
169 size_t old_i;
170 auto old_p = p;
171 int x, y;
172 i = 0;
173 p = ps.begin();
174 y = getY();
175 while (i < s.length())
177 // write all whitespaces.
178 write_whitespace();
180 // if we are generating height, check difference
181 // between previous Y coord and current one and
182 // update height accordingly.
183 if (generate_height_only)
185 new_y = getY();
186 height += new_y - y;
187 y = new_y;
190 if (i == s.length())
191 break;
193 // save current string position state and get current
194 // coordinates as we are before the beginning of a word.
195 old_i = i;
196 old_p = p;
197 x = getX();
198 y = getY();
200 // write word to test if it overflows, but do not load properties
201 // yet since if it overflows, we do not want to load them twice.
202 write_word(false);
204 // restore previous indexes state
205 i = old_i;
206 p = old_p;
208 // get new Y coord to see if word overflew into next line.
209 new_y = getY();
210 if (new_y != y)
212 if (generate_height_only)
214 // if it did, let's update height...
215 ++height;
217 else
219 // ...or go to old coordinates, erase
220 // part of the string from previous line...
221 goToXY(x, y);
222 wclrtoeol(m_window);
225 // ...start at the beginning of next line...
226 ++y;
227 goToXY(0, y);
229 // ...write word again, this time with properties...
230 write_word(true);
232 if (generate_height_only)
234 // ... and check for potential
235 // difference in Y coordinates again.
236 new_y = getY();
237 height += new_y - y;
240 else
242 // word didn't overflow, rewrite it with properties.
243 goToXY(x, y);
244 write_word(true);
247 if (generate_height_only)
249 // move to the first line, since when we do
250 // generation, m_real_height = m_height and we
251 // don't have many lines to use.
252 goToXY(getX(), 0);
253 y = 0;
256 // load remaining properties if there are any
257 for (; p != ps.end(); ++p)
258 w << p->second;
259 return height;
261 m_real_height = std::max(write_buffer(true), m_height);
262 if (m_real_height > m_height)
263 recreate(m_width, m_real_height);
264 else
265 werase(m_window);
266 write_buffer(false);
269 void Scrollpad::reset()
271 m_beginning = 0;
274 bool Scrollpad::setProperties(const Color &begin, const std::string &s,
275 const Color &end, size_t flags, size_t id)
277 return regexSearch(m_buffer, std::move(begin), s, std::move(end), id, flags);
280 bool Scrollpad::setProperties(const Format &begin, const std::string &s,
281 const Format &end, size_t flags, size_t id)
283 return regexSearch(m_buffer, begin, s, end, flags, id);
286 bool Scrollpad::setProperties(const FormattedColor &fc, const std::string &s,
287 size_t flags, size_t id)
289 return regexSearch(m_buffer,
292 FormattedColor::End<StorageKind::Value>(fc),
293 flags,
294 id);
297 void Scrollpad::removeProperties(size_t id)
299 m_buffer.removeProperties(id);