293 lines
7.0 KiB
C++
293 lines
7.0 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2008-2014 by Andrzej Rybczak *
|
|
* electricityispower@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
***************************************************************************/
|
|
|
|
#include <cassert>
|
|
#include <boost/regex.hpp>
|
|
#include <iostream>
|
|
|
|
#include "scrollpad.h"
|
|
|
|
namespace {//
|
|
|
|
template <typename PropT>
|
|
bool regexSearch(NC::Buffer &buf, PropT begin, const std::string &ws, PropT end, boost::regex::flag_type flags, size_t id)
|
|
{
|
|
try {
|
|
boost::regex rx(ws, flags);
|
|
auto first = boost::sregex_iterator(buf.str().begin(), buf.str().end(), rx);
|
|
auto last = boost::sregex_iterator();
|
|
bool success = first != last;
|
|
for (; first != last; ++first)
|
|
{
|
|
buf.setProperty(first->position(), begin, id);
|
|
buf.setProperty(first->position() + first->length(), end, id);
|
|
}
|
|
return success;
|
|
} catch (boost::bad_expression &e) {
|
|
std::cerr << "regexSearch: bad_expression: " << e.what() << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
namespace NC {//
|
|
|
|
Scrollpad::Scrollpad(size_t startx,
|
|
size_t starty,
|
|
size_t width,
|
|
size_t height,
|
|
const std::string &title,
|
|
Color color,
|
|
Border border)
|
|
: Window(startx, starty, width, height, title, color, border),
|
|
m_beginning(0),
|
|
m_real_height(height)
|
|
{
|
|
}
|
|
|
|
void Scrollpad::refresh()
|
|
{
|
|
assert(m_real_height >= m_height);
|
|
size_t max_beginning = m_real_height - m_height;
|
|
m_beginning = std::min(m_beginning, max_beginning);
|
|
prefresh(m_window, m_beginning, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
|
|
}
|
|
|
|
void Scrollpad::resize(size_t new_width, size_t new_height)
|
|
{
|
|
adjustDimensions(new_width, new_height);
|
|
recreate(new_width, new_height);
|
|
flush();
|
|
}
|
|
|
|
void Scrollpad::scroll(Scroll where)
|
|
{
|
|
assert(m_real_height >= m_height);
|
|
size_t max_beginning = m_real_height - m_height;
|
|
switch (where)
|
|
{
|
|
case Scroll::Up:
|
|
{
|
|
if (m_beginning > 0)
|
|
--m_beginning;
|
|
break;
|
|
}
|
|
case Scroll::Down:
|
|
{
|
|
if (m_beginning < max_beginning)
|
|
++m_beginning;
|
|
break;
|
|
}
|
|
case Scroll::PageUp:
|
|
{
|
|
if (m_beginning > m_height)
|
|
m_beginning -= m_height;
|
|
else
|
|
m_beginning = 0;
|
|
break;
|
|
}
|
|
case Scroll::PageDown:
|
|
{
|
|
m_beginning = std::min(m_beginning + m_height, max_beginning);
|
|
break;
|
|
}
|
|
case Scroll::Home:
|
|
{
|
|
m_beginning = 0;
|
|
break;
|
|
}
|
|
case Scroll::End:
|
|
{
|
|
m_beginning = max_beginning;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Scrollpad::clear()
|
|
{
|
|
m_real_height = m_height;
|
|
m_buffer.clear();
|
|
werase(m_window);
|
|
delwin(m_window);
|
|
m_window = newpad(m_height, m_width);
|
|
setTimeout(m_window_timeout);
|
|
setColor(m_color, m_bg_color);
|
|
keypad(m_window, 1);
|
|
}
|
|
|
|
const std::string &Scrollpad::buffer()
|
|
{
|
|
return m_buffer.str();
|
|
}
|
|
|
|
void Scrollpad::flush()
|
|
{
|
|
auto &w = static_cast<Window &>(*this);
|
|
const auto &s = m_buffer.str();
|
|
const auto &ps = m_buffer.properties();
|
|
auto p = ps.begin();
|
|
size_t i = 0;
|
|
|
|
auto load_properties = [&]() {
|
|
for (; p != ps.end() && p->position() == i; ++p)
|
|
w << *p;
|
|
};
|
|
auto write_whitespace = [&]() {
|
|
for (; i < s.length() && iswspace(s[i]); ++i)
|
|
{
|
|
load_properties();
|
|
w << s[i];
|
|
}
|
|
};
|
|
auto write_word = [&](bool load_properties_) {
|
|
for (; i < s.length() && !iswspace(s[i]); ++i)
|
|
{
|
|
if (load_properties_)
|
|
load_properties();
|
|
w << s[i];
|
|
}
|
|
};
|
|
auto write_buffer = [&](bool generate_height_only) -> size_t {
|
|
int new_y;
|
|
size_t height = 1;
|
|
size_t old_i;
|
|
auto old_p = p;
|
|
int x, y;
|
|
i = 0;
|
|
p = ps.begin();
|
|
y = getY();
|
|
while (i < s.length())
|
|
{
|
|
// write all whitespaces.
|
|
write_whitespace();
|
|
|
|
// if we are generating height, check difference
|
|
// between previous Y coord and current one and
|
|
// update height accordingly.
|
|
if (generate_height_only)
|
|
{
|
|
new_y = getY();
|
|
height += new_y - y;
|
|
y = new_y;
|
|
}
|
|
|
|
if (i == s.length())
|
|
break;
|
|
|
|
// save current string position state and get current
|
|
// coordinates as we are before the beginning of a word.
|
|
old_i = i;
|
|
old_p = p;
|
|
x = getX();
|
|
y = getY();
|
|
|
|
// write word to test if it overflows, but do not load properties
|
|
// yet since if it overflows, we do not want to load them twice.
|
|
write_word(false);
|
|
|
|
// restore previous indexes state
|
|
i = old_i;
|
|
p = old_p;
|
|
|
|
// get new Y coord to see if word overflew into next line.
|
|
new_y = getY();
|
|
if (new_y != y)
|
|
{
|
|
if (generate_height_only)
|
|
{
|
|
// if it did, let's update height...
|
|
++height;
|
|
}
|
|
else
|
|
{
|
|
// ...or go to old coordinates, erase
|
|
// part of the string from previous line...
|
|
goToXY(x, y);
|
|
wclrtoeol(m_window);
|
|
}
|
|
|
|
// ...start at the beginning of next line...
|
|
++y;
|
|
goToXY(0, y);
|
|
|
|
// ...write word again, this time with properties...
|
|
write_word(true);
|
|
|
|
if (generate_height_only)
|
|
{
|
|
// ... and check for potential
|
|
// difference in Y coordinates again.
|
|
new_y = getY();
|
|
height += new_y - y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// word didn't overflow, rewrite it with properties.
|
|
goToXY(x, y);
|
|
write_word(true);
|
|
}
|
|
|
|
if (generate_height_only)
|
|
{
|
|
// move to the first line, since when we do
|
|
// generation, m_real_height = m_height and we
|
|
// don't have many lines to use.
|
|
goToXY(getX(), 0);
|
|
y = 0;
|
|
}
|
|
}
|
|
// load remaining properties if there are any
|
|
for (; p != ps.end(); ++p)
|
|
w << *p;
|
|
return height;
|
|
};
|
|
m_real_height = std::max(write_buffer(true), m_height);
|
|
if (m_real_height > m_height)
|
|
recreate(m_width, m_real_height);
|
|
else
|
|
werase(m_window);
|
|
write_buffer(false);
|
|
}
|
|
|
|
void Scrollpad::reset()
|
|
{
|
|
m_beginning = 0;
|
|
}
|
|
|
|
bool Scrollpad::setProperties(Color begin, const std::string &s, Color end, size_t flags, size_t id)
|
|
{
|
|
return regexSearch(m_buffer, begin, s, end, id, flags);
|
|
}
|
|
|
|
bool Scrollpad::setProperties(Format begin, const std::string &s, Format end, size_t flags, size_t id)
|
|
{
|
|
return regexSearch(m_buffer, begin, s, end, flags, id);
|
|
}
|
|
|
|
void Scrollpad::removeProperties(size_t id)
|
|
{
|
|
m_buffer.removeProperties(id);
|
|
}
|
|
|
|
}
|