Config: reformat all descriptions to fill 80 columns
[ncmpcpp.git] / src / curses / scrollpad.cpp
blob0a1683b135139ec322bc6835d8dc554836af8f1f
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 "scrollpad.h"
27 namespace {
29 template <typename PropT>
30 bool regexSearch(NC::Buffer &buf, PropT begin, const std::string &ws, PropT end, boost::regex::flag_type flags, size_t id)
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.addProperty(first->position(), begin, id);
40 buf.addProperty(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 Window::clear();
132 const std::string &Scrollpad::buffer()
134 return m_buffer.str();
137 void Scrollpad::flush()
139 auto &w = static_cast<Window &>(*this);
140 const auto &s = m_buffer.str();
141 const auto &ps = m_buffer.properties();
142 auto p = ps.begin();
143 size_t i = 0;
145 auto load_properties = [&]() {
146 for (; p != ps.end() && p->first == i; ++p)
147 w << p->second;
149 auto write_whitespace = [&]() {
150 for (; i < s.length() && iswspace(s[i]); ++i)
152 load_properties();
153 w << s[i];
156 auto write_word = [&](bool load_properties_) {
157 for (; i < s.length() && !iswspace(s[i]); ++i)
159 if (load_properties_)
160 load_properties();
161 w << s[i];
164 auto write_buffer = [&](bool generate_height_only) -> size_t {
165 int new_y;
166 size_t height = 1;
167 size_t old_i;
168 auto old_p = p;
169 int x, y;
170 i = 0;
171 p = ps.begin();
172 y = getY();
173 while (i < s.length())
175 // write all whitespaces.
176 write_whitespace();
178 // if we are generating height, check difference
179 // between previous Y coord and current one and
180 // update height accordingly.
181 if (generate_height_only)
183 new_y = getY();
184 height += new_y - y;
185 y = new_y;
188 if (i == s.length())
189 break;
191 // save current string position state and get current
192 // coordinates as we are before the beginning of a word.
193 old_i = i;
194 old_p = p;
195 x = getX();
196 y = getY();
198 // write word to test if it overflows, but do not load properties
199 // yet since if it overflows, we do not want to load them twice.
200 write_word(false);
202 // restore previous indexes state
203 i = old_i;
204 p = old_p;
206 // get new Y coord to see if word overflew into next line.
207 new_y = getY();
208 if (new_y != y)
210 if (generate_height_only)
212 // if it did, let's update height...
213 ++height;
215 else
217 // ...or go to old coordinates, erase
218 // part of the string from previous line...
219 goToXY(x, y);
220 wclrtoeol(m_window);
223 // ...start at the beginning of next line...
224 ++y;
225 goToXY(0, y);
227 // ...write word again, this time with properties...
228 write_word(true);
230 if (generate_height_only)
232 // ... and check for potential
233 // difference in Y coordinates again.
234 new_y = getY();
235 height += new_y - y;
238 else
240 // word didn't overflow, rewrite it with properties.
241 goToXY(x, y);
242 write_word(true);
245 if (generate_height_only)
247 // move to the first line, since when we do
248 // generation, m_real_height = m_height and we
249 // don't have many lines to use.
250 goToXY(getX(), 0);
251 y = 0;
254 // load remaining properties if there are any
255 for (; p != ps.end(); ++p)
256 w << p->second;
257 return height;
259 m_real_height = std::max(write_buffer(true), m_height);
260 if (m_real_height > m_height)
261 recreate(m_width, m_real_height);
262 else
263 werase(m_window);
264 write_buffer(false);
267 void Scrollpad::reset()
269 m_beginning = 0;
272 bool Scrollpad::setProperties(Color begin, const std::string &s, Color end, size_t flags, size_t id)
274 return regexSearch(m_buffer, std::move(begin), s, std::move(end), id, flags);
277 bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t flags, size_t id)
279 return regexSearch(m_buffer, begin, s, end, flags, id);
282 void Scrollpad::removeProperties(size_t id)
284 m_buffer.removeProperties(id);