Move ncurses related files to curses directory
[ncmpcpp.git] / src / curses / scrollpad.cpp
blob24f45afab61fbad19430d6de784cbb0abc793e19
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 #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 werase(m_window);
130 delwin(m_window);
131 m_window = newpad(m_height, m_width);
132 setTimeout(m_window_timeout);
133 setColor(m_color);
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->first == i; ++p)
151 w << p->second;
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->second;
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 flags, size_t id)
278 return regexSearch(m_buffer, std::move(begin), s, std::move(end), id, flags);
281 bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t flags, size_t id)
283 return regexSearch(m_buffer, begin, s, end, flags, id);
286 void Scrollpad::removeProperties(size_t id)
288 m_buffer.removeProperties(id);