window: use readline for handling line input
This commit is contained in:
10
configure.ac
10
configure.ac
@@ -181,6 +181,16 @@ PKG_CHECK_MODULES([libmpdclient], [libmpdclient >= 2.8], [
|
|||||||
AC_MSG_ERROR([libmpdclient >= 2.8 is required!])
|
AC_MSG_ERROR([libmpdclient >= 2.8 is required!])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
dnl =========================
|
||||||
|
dnl = checking for readline =
|
||||||
|
dnl =========================
|
||||||
|
AC_CHECK_HEADERS([readline/readline.h readline/history.h],
|
||||||
|
AC_CHECK_LIB(readline, rl_initialize, LDFLAGS="$LDFLAGS -lreadline",
|
||||||
|
AC_MSG_ERROR([readline headers found but there is no readline library to make use of])
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
dnl ========================
|
dnl ========================
|
||||||
dnl = checking for pthread =
|
dnl = checking for pthread =
|
||||||
dnl ========================
|
dnl ========================
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
|
||||||
#include "actions.h"
|
#include "actions.h"
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
@@ -191,6 +192,7 @@ void resizeScreen(bool reload_main_window)
|
|||||||
// update internal screen dimensions
|
// update internal screen dimensions
|
||||||
if (reload_main_window)
|
if (reload_main_window)
|
||||||
{
|
{
|
||||||
|
rl_resize_terminal();
|
||||||
endwin();
|
endwin();
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@@ -1189,7 +1191,7 @@ void SetCrossfade::run()
|
|||||||
|
|
||||||
Statusbar::lock();
|
Statusbar::lock();
|
||||||
Statusbar::put() << "Set crossfade to: ";
|
Statusbar::put() << "Set crossfade to: ";
|
||||||
std::string crossfade = wFooter->getString(3);
|
std::string crossfade = wFooter->getString();
|
||||||
Statusbar::unlock();
|
Statusbar::unlock();
|
||||||
int cf = fromString<unsigned>(crossfade);
|
int cf = fromString<unsigned>(crossfade);
|
||||||
lowerBoundCheck(cf, 1);
|
lowerBoundCheck(cf, 1);
|
||||||
@@ -1203,7 +1205,7 @@ void SetVolume::run()
|
|||||||
|
|
||||||
Statusbar::lock();
|
Statusbar::lock();
|
||||||
Statusbar::put() << "Set volume to: ";
|
Statusbar::put() << "Set volume to: ";
|
||||||
std::string strvolume = wFooter->getString(3);
|
std::string strvolume = wFooter->getString();
|
||||||
Statusbar::unlock();
|
Statusbar::unlock();
|
||||||
int volume = fromString<unsigned>(strvolume);
|
int volume = fromString<unsigned>(strvolume);
|
||||||
boundsCheck(volume, 0, 100);
|
boundsCheck(volume, 0, 100);
|
||||||
@@ -1765,7 +1767,7 @@ void ApplyFilter::run()
|
|||||||
|
|
||||||
Statusbar::lock();
|
Statusbar::lock();
|
||||||
Statusbar::put() << NC::Format::Bold << "Apply filter: " << NC::Format::NoBold;
|
Statusbar::put() << NC::Format::Bold << "Apply filter: " << NC::Format::NoBold;
|
||||||
wFooter->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f, ToWString(filter)));
|
wFooter->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f, filter));
|
||||||
wFooter->getString(filter);
|
wFooter->getString(filter);
|
||||||
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
||||||
Statusbar::unlock();
|
Statusbar::unlock();
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace
|
|||||||
{
|
{
|
||||||
std::ofstream errorlog;
|
std::ofstream errorlog;
|
||||||
std::streambuf *cerr_buffer;
|
std::streambuf *cerr_buffer;
|
||||||
|
bool run_resize_screen = false;
|
||||||
|
|
||||||
# if !defined(WIN32)
|
# if !defined(WIN32)
|
||||||
void sighandler(int signal)
|
void sighandler(int signal)
|
||||||
@@ -63,7 +64,7 @@ namespace
|
|||||||
}
|
}
|
||||||
else if (signal == SIGWINCH)
|
else if (signal == SIGWINCH)
|
||||||
{
|
{
|
||||||
Actions::resizeScreen(true);
|
run_resize_screen = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif // !WIN32
|
# endif // !WIN32
|
||||||
@@ -153,7 +154,6 @@ int main(int argc, char **argv)
|
|||||||
wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border::None);
|
wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border::None);
|
||||||
wFooter->setTimeout(500);
|
wFooter->setTimeout(500);
|
||||||
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
||||||
wFooter->createHistory();
|
|
||||||
|
|
||||||
// initialize global timer
|
// initialize global timer
|
||||||
gettimeofday(&Timer, 0);
|
gettimeofday(&Timer, 0);
|
||||||
@@ -225,6 +225,12 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status::trace();
|
Status::trace();
|
||||||
|
|
||||||
|
if (run_resize_screen)
|
||||||
|
{
|
||||||
|
Actions::resizeScreen(true);
|
||||||
|
run_resize_screen = false;
|
||||||
|
}
|
||||||
|
|
||||||
// header stuff
|
// header stuff
|
||||||
if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec)
|
if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec)
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ void Status::handleServerError(MPD::ServerError &e)
|
|||||||
{
|
{
|
||||||
wFooter->setGetStringHelper(nullptr);
|
wFooter->setGetStringHelper(nullptr);
|
||||||
Statusbar::put() << "Password: ";
|
Statusbar::put() << "Password: ";
|
||||||
Mpd.SetPassword(wFooter->getString(-1, 0, 1));
|
Mpd.SetPassword(wFooter->getString(0, true));
|
||||||
Mpd.SendPassword();
|
Mpd.SendPassword();
|
||||||
Statusbar::msg("Password accepted");
|
Statusbar::msg("Password accepted");
|
||||||
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
|
||||||
|
|||||||
@@ -189,13 +189,13 @@ void Statusbar::Helpers::mpd()
|
|||||||
Status::update(Mpd.noidle());
|
Status::update(Mpd.noidle());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Statusbar::Helpers::getString(const std::wstring &)
|
bool Statusbar::Helpers::getString(const char *)
|
||||||
{
|
{
|
||||||
Status::trace();
|
Status::trace();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const std::wstring &ws)
|
bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const char *s)
|
||||||
{
|
{
|
||||||
using Global::myScreen;
|
using Global::myScreen;
|
||||||
// if input queue is not empty, we don't want to update filter since next
|
// if input queue is not empty, we don't want to update filter since next
|
||||||
@@ -205,10 +205,10 @@ bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const std::wstring &
|
|||||||
// is next in queue, so its effects will be seen.
|
// is next in queue, so its effects will be seen.
|
||||||
if (wFooter->inputQueue().empty() || wFooter->inputQueue().front() == KEY_ENTER)
|
if (wFooter->inputQueue().empty() || wFooter->inputQueue().front() == KEY_ENTER)
|
||||||
{
|
{
|
||||||
if (m_ws != ws)
|
if (m_s != s)
|
||||||
{
|
{
|
||||||
m_ws = ws;
|
m_s = s;
|
||||||
m_f->applyFilter(ToString(m_ws));
|
m_f->applyFilter(m_s);
|
||||||
myScreen->refreshWindow();
|
myScreen->refreshWindow();
|
||||||
}
|
}
|
||||||
Status::trace();
|
Status::trace();
|
||||||
@@ -216,13 +216,13 @@ bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const std::wstring &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const std::wstring &ws)
|
bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
|
||||||
{
|
{
|
||||||
bool continue_ = true;
|
bool continue_ = true;
|
||||||
if (m_ws != ws)
|
if (m_s != s)
|
||||||
{
|
{
|
||||||
m_ws = ws;
|
m_s = s;
|
||||||
auto cmd = Bindings.findCommand(ToString(m_ws));
|
auto cmd = Bindings.findCommand(m_s);
|
||||||
if (cmd && cmd->immediate())
|
if (cmd && cmd->immediate())
|
||||||
continue_ = false;
|
continue_ = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,27 +71,28 @@ namespace Helpers {//
|
|||||||
void mpd();
|
void mpd();
|
||||||
|
|
||||||
/// called each time user types another character while inside Window::getString
|
/// called each time user types another character while inside Window::getString
|
||||||
bool getString(const std::wstring &);
|
bool getString(const char *);
|
||||||
|
|
||||||
/// called each time user changes current filter (while being inside Window::getString)
|
/// called each time user changes current filter (while being inside Window::getString)
|
||||||
struct ApplyFilterImmediately
|
struct ApplyFilterImmediately
|
||||||
{
|
{
|
||||||
ApplyFilterImmediately(Filterable *f, const std::wstring &filter)
|
template <typename StringT>
|
||||||
: m_f(f), m_ws(filter) { }
|
ApplyFilterImmediately(Filterable *f, StringT &&filter)
|
||||||
|
: m_f(f), m_s(std::forward<StringT>(filter)) { }
|
||||||
|
|
||||||
bool operator()(const std::wstring &ws);
|
bool operator()(const char *s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Filterable *m_f;
|
Filterable *m_f;
|
||||||
std::wstring m_ws;
|
std::string m_s;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TryExecuteImmediateCommand
|
struct TryExecuteImmediateCommand
|
||||||
{
|
{
|
||||||
bool operator()(const std::wstring &ws);
|
bool operator()(const char *s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::wstring m_ws;
|
std::string m_s;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
386
src/window.cpp
386
src/window.cpp
@@ -18,8 +18,12 @@
|
|||||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
# include <winsock.h>
|
# include <winsock.h>
|
||||||
@@ -33,6 +37,111 @@
|
|||||||
#include "utility/wide_string.h"
|
#include "utility/wide_string.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace rl {
|
||||||
|
|
||||||
|
NC::Window *w;
|
||||||
|
size_t start_x;
|
||||||
|
size_t start_y;
|
||||||
|
size_t width;
|
||||||
|
bool encrypted;
|
||||||
|
const char *base;
|
||||||
|
|
||||||
|
int read_key(FILE *)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
w->runGetStringHelper(rl_line_buffer);
|
||||||
|
w->refresh();
|
||||||
|
result = w->readKey();
|
||||||
|
}
|
||||||
|
while (result == ERR);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_string()
|
||||||
|
{
|
||||||
|
auto print_char = [](wchar_t wc) {
|
||||||
|
if (encrypted)
|
||||||
|
*w << '*';
|
||||||
|
else
|
||||||
|
*w << wc;
|
||||||
|
};
|
||||||
|
auto print_string = [](wchar_t *ws, size_t len) {
|
||||||
|
if (encrypted)
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
*w << '*';
|
||||||
|
else
|
||||||
|
*w << ws;
|
||||||
|
};
|
||||||
|
|
||||||
|
char pt = rl_line_buffer[rl_point];
|
||||||
|
rl_line_buffer[rl_point] = 0;
|
||||||
|
wchar_t pre_pos[rl_point+1];
|
||||||
|
pre_pos[mbstowcs(pre_pos, rl_line_buffer, rl_point)] = 0;
|
||||||
|
rl_line_buffer[rl_point] = pt;
|
||||||
|
|
||||||
|
int pos = wcswidth(pre_pos, rl_point);
|
||||||
|
assert(pos >= 0);
|
||||||
|
if (pos < 0)
|
||||||
|
pos = rl_point;
|
||||||
|
|
||||||
|
mvwhline(w->raw(), start_y, start_x, ' ', width+1);
|
||||||
|
w->goToXY(start_x, start_y);
|
||||||
|
if (size_t(pos) <= width)
|
||||||
|
{
|
||||||
|
print_string(pre_pos, pos);
|
||||||
|
|
||||||
|
wchar_t post_pos[rl_end-rl_point+1];
|
||||||
|
post_pos[mbstowcs(post_pos, rl_line_buffer+rl_point, rl_end-rl_point)] = 0;
|
||||||
|
|
||||||
|
size_t cpos = pos;
|
||||||
|
for (wchar_t *c = post_pos; *c != 0; ++c)
|
||||||
|
{
|
||||||
|
int n = wcwidth(*c);
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
print_char(L'.');
|
||||||
|
++cpos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cpos+n > width)
|
||||||
|
break;
|
||||||
|
cpos += n;
|
||||||
|
print_char(*c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wchar_t *mod_pre_pos = pre_pos;
|
||||||
|
while (*mod_pre_pos != 0)
|
||||||
|
{
|
||||||
|
++mod_pre_pos;
|
||||||
|
int n = wcwidth(*mod_pre_pos);
|
||||||
|
if (n < 0)
|
||||||
|
--pos;
|
||||||
|
else
|
||||||
|
pos -= n;
|
||||||
|
if (size_t(pos) <= width)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_string(mod_pre_pos, pos);
|
||||||
|
}
|
||||||
|
w->goToXY(start_x+pos, start_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_base()
|
||||||
|
{
|
||||||
|
rl_insert_text(base);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace NC {//
|
namespace NC {//
|
||||||
|
|
||||||
void initScreen(GNUC_UNUSED const char *window_title, bool enable_colors)
|
void initScreen(GNUC_UNUSED const char *window_title, bool enable_colors)
|
||||||
@@ -64,6 +173,12 @@ void initScreen(GNUC_UNUSED const char *window_title, bool enable_colors)
|
|||||||
noecho();
|
noecho();
|
||||||
cbreak();
|
cbreak();
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
|
|
||||||
|
rl_initialize();
|
||||||
|
// overwrite readline callbacks
|
||||||
|
rl_getc_function = rl::read_key;
|
||||||
|
rl_redisplay_function = rl::display_string;
|
||||||
|
rl_startup_hook = rl::add_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyScreen()
|
void destroyScreen()
|
||||||
@@ -93,7 +208,6 @@ Window::Window(size_t startx,
|
|||||||
m_border(border),
|
m_border(border),
|
||||||
m_get_string_helper(0),
|
m_get_string_helper(0),
|
||||||
m_title(title),
|
m_title(title),
|
||||||
m_history(0),
|
|
||||||
m_bold_counter(0),
|
m_bold_counter(0),
|
||||||
m_underline_counter(0),
|
m_underline_counter(0),
|
||||||
m_reverse_counter(0),
|
m_reverse_counter(0),
|
||||||
@@ -145,7 +259,6 @@ Window::Window(const Window &rhs)
|
|||||||
, m_color_stack(rhs.m_color_stack)
|
, m_color_stack(rhs.m_color_stack)
|
||||||
, m_input_queue(rhs.m_input_queue)
|
, m_input_queue(rhs.m_input_queue)
|
||||||
, m_fds(rhs.m_fds)
|
, m_fds(rhs.m_fds)
|
||||||
, m_history(rhs.m_history ? new std::list<std::wstring>(*rhs.m_history) : 0)
|
|
||||||
, m_bold_counter(rhs.m_bold_counter)
|
, m_bold_counter(rhs.m_bold_counter)
|
||||||
, m_underline_counter(rhs.m_underline_counter)
|
, m_underline_counter(rhs.m_underline_counter)
|
||||||
, m_reverse_counter(rhs.m_reverse_counter)
|
, m_reverse_counter(rhs.m_reverse_counter)
|
||||||
@@ -171,7 +284,6 @@ Window::Window(Window &&rhs)
|
|||||||
, m_color_stack(std::move(rhs.m_color_stack))
|
, m_color_stack(std::move(rhs.m_color_stack))
|
||||||
, m_input_queue(std::move(rhs.m_input_queue))
|
, m_input_queue(std::move(rhs.m_input_queue))
|
||||||
, m_fds(std::move(rhs.m_fds))
|
, m_fds(std::move(rhs.m_fds))
|
||||||
, m_history(rhs.m_history)
|
|
||||||
, m_bold_counter(rhs.m_bold_counter)
|
, m_bold_counter(rhs.m_bold_counter)
|
||||||
, m_underline_counter(rhs.m_underline_counter)
|
, m_underline_counter(rhs.m_underline_counter)
|
||||||
, m_reverse_counter(rhs.m_reverse_counter)
|
, m_reverse_counter(rhs.m_reverse_counter)
|
||||||
@@ -179,7 +291,6 @@ Window::Window(Window &&rhs)
|
|||||||
{
|
{
|
||||||
rhs.m_window = 0;
|
rhs.m_window = 0;
|
||||||
rhs.m_border_window = 0;
|
rhs.m_border_window = 0;
|
||||||
rhs.m_history = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Window &Window::operator=(Window rhs)
|
Window &Window::operator=(Window rhs)
|
||||||
@@ -201,7 +312,6 @@ Window &Window::operator=(Window rhs)
|
|||||||
std::swap(m_color_stack, rhs.m_color_stack);
|
std::swap(m_color_stack, rhs.m_color_stack);
|
||||||
std::swap(m_input_queue, rhs.m_input_queue);
|
std::swap(m_input_queue, rhs.m_input_queue);
|
||||||
std::swap(m_fds, rhs.m_fds);
|
std::swap(m_fds, rhs.m_fds);
|
||||||
std::swap(m_history, rhs.m_history);
|
|
||||||
std::swap(m_bold_counter, rhs.m_bold_counter);
|
std::swap(m_bold_counter, rhs.m_bold_counter);
|
||||||
std::swap(m_underline_counter, rhs.m_underline_counter);
|
std::swap(m_underline_counter, rhs.m_underline_counter);
|
||||||
std::swap(m_reverse_counter, rhs.m_reverse_counter);
|
std::swap(m_reverse_counter, rhs.m_reverse_counter);
|
||||||
@@ -213,7 +323,6 @@ Window::~Window()
|
|||||||
{
|
{
|
||||||
delwin(m_window);
|
delwin(m_window);
|
||||||
delwin(m_border_window);
|
delwin(m_border_window);
|
||||||
delete m_history;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setColor(Color fg, Color bg)
|
void Window::setColor(Color fg, Color bg)
|
||||||
@@ -286,18 +395,6 @@ void Window::setTitle(const std::string &new_title)
|
|||||||
m_title = new_title;
|
m_title = new_title;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::createHistory()
|
|
||||||
{
|
|
||||||
if (!m_history)
|
|
||||||
m_history = new std::list<std::wstring>;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::deleteHistory()
|
|
||||||
{
|
|
||||||
delete m_history;
|
|
||||||
m_history = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::recreate(size_t width, size_t height)
|
void Window::recreate(size_t width, size_t height)
|
||||||
{
|
{
|
||||||
delwin(m_window);
|
delwin(m_window);
|
||||||
@@ -481,229 +578,39 @@ void Window::pushChar(int ch)
|
|||||||
m_input_queue.push(ch);
|
m_input_queue.push(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Window::getString(const std::string &base, size_t length_, size_t width, bool encrypted)
|
std::string Window::getString(const std::string &base, size_t width, bool encrypted)
|
||||||
{
|
{
|
||||||
int input;
|
rl::w = this;
|
||||||
size_t beginning, maxbeginning, minx, x, real_x, y, maxx, real_maxx;
|
getyx(m_window, rl::start_y, rl::start_x);
|
||||||
|
rl::width = width;
|
||||||
getyx(m_window, y, x);
|
rl::encrypted = encrypted;
|
||||||
minx = real_maxx = maxx = real_x = x;
|
rl::base = base.c_str();
|
||||||
|
|
||||||
width--;
|
width--;
|
||||||
if (width == size_t(-1))
|
if (width == size_t(-1))
|
||||||
width = m_width-x-1;
|
rl::width = m_width-rl::start_x-1;
|
||||||
|
else
|
||||||
|
rl::width = width;
|
||||||
|
|
||||||
|
mmask_t oldmask;
|
||||||
|
std::string result;
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
|
keypad(m_window, 0);
|
||||||
std::wstring wbase = ToWString(base);
|
mousemask(0, &oldmask);
|
||||||
std::wstring *tmp = &wbase;
|
char *input = readline(nullptr);
|
||||||
std::list<std::wstring>::iterator history_it = m_history->end();
|
mousemask(oldmask, nullptr);
|
||||||
|
keypad(m_window, 1);
|
||||||
std::string tmp_in;
|
|
||||||
wchar_t wc_in;
|
|
||||||
bool gotoend = 1;
|
|
||||||
bool block_scrolling = 0;
|
|
||||||
|
|
||||||
// disable scrolling if wide chars are used
|
|
||||||
for (std::wstring::const_iterator it = tmp->begin(); it != tmp->end(); ++it)
|
|
||||||
if (wcwidth(*it) > 1)
|
|
||||||
block_scrolling = 1;
|
|
||||||
|
|
||||||
beginning = -1;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (tmp->empty())
|
|
||||||
block_scrolling = 0;
|
|
||||||
|
|
||||||
maxbeginning = block_scrolling ? 0 : (tmp->length() < width ? 0 : tmp->length()-width);
|
|
||||||
maxx = minx + (wideLength(*tmp) < width ? wideLength(*tmp) : width);
|
|
||||||
|
|
||||||
real_maxx = minx + (tmp->length() < width ? tmp->length() : width);
|
|
||||||
|
|
||||||
if (beginning > maxbeginning)
|
|
||||||
beginning = maxbeginning;
|
|
||||||
|
|
||||||
if (gotoend)
|
|
||||||
{
|
|
||||||
size_t real_real_maxx = minx;
|
|
||||||
size_t biggest_x = minx+width;
|
|
||||||
|
|
||||||
if (block_scrolling && maxx >= biggest_x)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
for (std::wstring::const_iterator it = tmp->begin(); i < width; ++it, ++real_real_maxx)
|
|
||||||
i += wcwidth(*it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
real_real_maxx = real_maxx;
|
|
||||||
|
|
||||||
real_x = real_real_maxx;
|
|
||||||
x = block_scrolling ? (maxx > biggest_x ? biggest_x : maxx) : maxx;
|
|
||||||
beginning = maxbeginning;
|
|
||||||
gotoend = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mvwhline(m_window, y, minx, ' ', width+1);
|
|
||||||
|
|
||||||
if (!encrypted)
|
|
||||||
mvwprintw(m_window, y, minx, "%ls", tmp->substr(beginning, width+1).c_str());
|
|
||||||
else
|
|
||||||
mvwhline(m_window, y, minx, '*', maxx-minx);
|
|
||||||
|
|
||||||
if (m_get_string_helper)
|
|
||||||
{
|
|
||||||
if (!m_get_string_helper(*tmp))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
wmove(m_window, y, x);
|
|
||||||
prefresh(m_window, 0, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
|
|
||||||
input = readKey();
|
|
||||||
|
|
||||||
switch (input)
|
|
||||||
{
|
|
||||||
case ERR:
|
|
||||||
case KEY_MOUSE:
|
|
||||||
break;
|
|
||||||
case KEY_UP:
|
|
||||||
if (m_history && !encrypted && history_it != m_history->begin())
|
|
||||||
{
|
|
||||||
while (--history_it != m_history->begin())
|
|
||||||
if (!history_it->empty())
|
|
||||||
break;
|
|
||||||
tmp = &*history_it;
|
|
||||||
gotoend = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KEY_DOWN:
|
|
||||||
if (m_history && !encrypted && history_it != m_history->end())
|
|
||||||
{
|
|
||||||
while (++history_it != m_history->end())
|
|
||||||
if (!history_it->empty())
|
|
||||||
break;
|
|
||||||
tmp = &(history_it == m_history->end() ? wbase : *history_it);
|
|
||||||
gotoend = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KEY_RIGHT:
|
|
||||||
{
|
|
||||||
if (x < maxx)
|
|
||||||
{
|
|
||||||
real_x++;
|
|
||||||
x += std::max(wcwidth((*tmp)[beginning+real_x-minx-1]), 1);
|
|
||||||
}
|
|
||||||
else if (beginning < maxbeginning)
|
|
||||||
beginning++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KEY_CTRL_H:
|
|
||||||
case KEY_BACKSPACE:
|
|
||||||
case KEY_BACKSPACE_2:
|
|
||||||
{
|
|
||||||
if (x <= minx && !beginning)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KEY_LEFT:
|
|
||||||
{
|
|
||||||
if (x > minx)
|
|
||||||
{
|
|
||||||
real_x--;
|
|
||||||
x -= std::max(wcwidth((*tmp)[beginning+real_x-minx]), 1);
|
|
||||||
}
|
|
||||||
else if (beginning > 0)
|
|
||||||
beginning--;
|
|
||||||
if (input != KEY_CTRL_H && input != KEY_BACKSPACE && input != KEY_BACKSPACE_2)
|
|
||||||
break; // backspace = left & delete.
|
|
||||||
}
|
|
||||||
case KEY_DC:
|
|
||||||
{
|
|
||||||
if ((real_x-minx)+beginning == tmp->length())
|
|
||||||
break;
|
|
||||||
tmp->erase(tmp->begin()+(real_x-minx)+beginning);
|
|
||||||
if (beginning && beginning == maxbeginning && real_x < maxx)
|
|
||||||
{
|
|
||||||
real_x++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KEY_HOME:
|
|
||||||
{
|
|
||||||
real_x = x = minx;
|
|
||||||
beginning = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KEY_END:
|
|
||||||
{
|
|
||||||
gotoend = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KEY_ENTER:
|
|
||||||
break;
|
|
||||||
case KEY_CTRL_U:
|
|
||||||
tmp->clear();
|
|
||||||
real_maxx = maxx = real_x = x = minx;
|
|
||||||
maxbeginning = beginning = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (tmp->length() >= length_)
|
|
||||||
break;
|
|
||||||
|
|
||||||
tmp_in += input;
|
|
||||||
if (int(mbrtowc(&wc_in, tmp_in.c_str(), MB_CUR_MAX, 0)) < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
int wcwidth_res = wcwidth(wc_in);
|
|
||||||
if (wcwidth_res > 1)
|
|
||||||
block_scrolling = 1;
|
|
||||||
|
|
||||||
if (wcwidth_res > 0) // is char printable? we want to ignore things like Ctrl-?, Fx etc.
|
|
||||||
{
|
|
||||||
if ((real_x-minx)+beginning >= tmp->length())
|
|
||||||
{
|
|
||||||
tmp->push_back(wc_in);
|
|
||||||
if (!beginning)
|
|
||||||
{
|
|
||||||
real_x++;
|
|
||||||
x += wcwidth(wc_in);
|
|
||||||
}
|
|
||||||
beginning++;
|
|
||||||
gotoend = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tmp->insert(tmp->begin()+(real_x-minx)+beginning, wc_in);
|
|
||||||
if (x < maxx)
|
|
||||||
{
|
|
||||||
real_x++;
|
|
||||||
x += wcwidth(wc_in);
|
|
||||||
}
|
|
||||||
else if (beginning < maxbeginning)
|
|
||||||
beginning++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmp_in.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (input != KEY_ENTER);
|
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
|
if (input != nullptr)
|
||||||
if (m_history && !encrypted)
|
|
||||||
{
|
{
|
||||||
if (history_it != m_history->end())
|
if (input[0] != 0)
|
||||||
{
|
add_history(input);
|
||||||
m_history->push_back(*history_it);
|
result = input;
|
||||||
tmp = &m_history->back();
|
free(input);
|
||||||
m_history->erase(history_it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_history->push_back(*tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToString(*tmp);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::goToXY(int x, int y)
|
void Window::goToXY(int x, int y)
|
||||||
@@ -739,6 +646,17 @@ bool Window::hasCoords(int &x, int &y)
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Window::runGetStringHelper(const char *arg) const
|
||||||
|
{
|
||||||
|
if (m_get_string_helper)
|
||||||
|
{
|
||||||
|
m_get_string_helper(arg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
size_t Window::getWidth() const
|
size_t Window::getWidth() const
|
||||||
{
|
{
|
||||||
if (m_border != Border::None)
|
if (m_border != Border::None)
|
||||||
|
|||||||
26
src/window.h
26
src/window.h
@@ -132,7 +132,7 @@ enum class Scroll { Up, Down, PageUp, PageDown, Home, End };
|
|||||||
/// Helper function that is invoked each time one will want
|
/// Helper function that is invoked each time one will want
|
||||||
/// to obtain string from Window::getString() function
|
/// to obtain string from Window::getString() function
|
||||||
/// @see Window::getString()
|
/// @see Window::getString()
|
||||||
typedef std::function<bool(const std::wstring &)> GetStringHelper;
|
typedef std::function<bool(const char *)> GetStringHelper;
|
||||||
|
|
||||||
/// Initializes curses screen and sets some additional attributes
|
/// Initializes curses screen and sets some additional attributes
|
||||||
/// @param window_title title of the window (has an effect only if pdcurses lib is used)
|
/// @param window_title title of the window (has an effect only if pdcurses lib is used)
|
||||||
@@ -163,7 +163,7 @@ struct XY
|
|||||||
/// Main class of NCurses namespace, used as base for other specialized windows
|
/// Main class of NCurses namespace, used as base for other specialized windows
|
||||||
struct Window
|
struct Window
|
||||||
{
|
{
|
||||||
Window() : m_window(0), m_border_window(0), m_history(0) { }
|
Window() : m_window(0), m_border_window(0) { }
|
||||||
|
|
||||||
/// Constructs an empty window with given parameters
|
/// Constructs an empty window with given parameters
|
||||||
/// @param startx X position of left upper corner of constructed window
|
/// @param startx X position of left upper corner of constructed window
|
||||||
@@ -229,14 +229,13 @@ struct Window
|
|||||||
/// @see setGetStringHelper()
|
/// @see setGetStringHelper()
|
||||||
/// @see SetTimeout()
|
/// @see SetTimeout()
|
||||||
/// @see CreateHistory()
|
/// @see CreateHistory()
|
||||||
std::string getString(const std::string &base, size_t length_ = -1,
|
std::string getString(const std::string &base, size_t width = 0, bool encrypted = 0);
|
||||||
size_t width = 0, bool encrypted = 0);
|
|
||||||
|
|
||||||
/// Wrapper for above function that doesn't take base string (it will be empty).
|
/// Wrapper for above function that doesn't take base string (it will be empty).
|
||||||
/// Taken parameters are the same as for above.
|
/// Taken parameters are the same as for above.
|
||||||
std::string getString(size_t length_ = -1, size_t width = 0, bool encrypted = 0)
|
std::string getString(size_t width = 0, bool encrypted = 0)
|
||||||
{
|
{
|
||||||
return getString("", length_, width, encrypted);
|
return getString("", width, encrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves cursor to given coordinates
|
/// Moves cursor to given coordinates
|
||||||
@@ -262,6 +261,11 @@ struct Window
|
|||||||
/// @param helper pointer to function that matches getStringHelper prototype
|
/// @param helper pointer to function that matches getStringHelper prototype
|
||||||
/// @see getString()
|
/// @see getString()
|
||||||
void setGetStringHelper(GetStringHelper helper) { m_get_string_helper = helper; }
|
void setGetStringHelper(GetStringHelper helper) { m_get_string_helper = helper; }
|
||||||
|
|
||||||
|
/// Run current GetString helper function (if defined).
|
||||||
|
/// @see getString()
|
||||||
|
/// @return true if helper was run, false otherwise
|
||||||
|
bool runGetStringHelper(const char *arg) const;
|
||||||
|
|
||||||
/// Sets window's base color
|
/// Sets window's base color
|
||||||
/// @param fg foregound base color
|
/// @param fg foregound base color
|
||||||
@@ -280,13 +284,6 @@ struct Window
|
|||||||
/// @param new_title new title for window
|
/// @param new_title new title for window
|
||||||
void setTitle(const std::string &new_title);
|
void setTitle(const std::string &new_title);
|
||||||
|
|
||||||
/// Creates internal container that stores all previous
|
|
||||||
/// strings that were edited using this window.
|
|
||||||
void createHistory();
|
|
||||||
|
|
||||||
/// Deletes container with all previous history entries
|
|
||||||
void deleteHistory();
|
|
||||||
|
|
||||||
/// Refreshed whole window and its border
|
/// Refreshed whole window and its border
|
||||||
/// @see refresh()
|
/// @see refresh()
|
||||||
void display();
|
void display();
|
||||||
@@ -509,9 +506,6 @@ private:
|
|||||||
typedef std::vector< std::pair<int, void (*)()> > FDCallbacks;
|
typedef std::vector< std::pair<int, void (*)()> > FDCallbacks;
|
||||||
FDCallbacks m_fds;
|
FDCallbacks m_fds;
|
||||||
|
|
||||||
/// pointer to container used as history
|
|
||||||
std::list<std::wstring> *m_history;
|
|
||||||
|
|
||||||
/// counters for format flags
|
/// counters for format flags
|
||||||
int m_bold_counter;
|
int m_bold_counter;
|
||||||
int m_underline_counter;
|
int m_underline_counter;
|
||||||
|
|||||||
Reference in New Issue
Block a user