summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrzej Rybczak <electricityispower@gmail.com>2013-12-25 23:31:27 +0100
committerAndrzej Rybczak <electricityispower@gmail.com>2013-12-25 23:31:27 +0100
commit70945596ef4abbdbaaeb30f52d9070ad07da8804 (patch)
treee1e157b9ae2444713ba8164016e9a0af1e17d09d /src
parent819d8baebd917ea64834eee4072f3c28d5536e51 (diff)
window: use readline for handling line input
Diffstat (limited to 'src')
-rw-r--r--src/actions.cpp8
-rw-r--r--src/ncmpcpp.cpp10
-rw-r--r--src/status.cpp2
-rw-r--r--src/statusbar.cpp18
-rw-r--r--src/statusbar.h15
-rw-r--r--src/window.cpp386
-rw-r--r--src/window.h26
7 files changed, 193 insertions, 272 deletions
diff --git a/src/actions.cpp b/src/actions.cpp
index 3366bc4b..dba5751e 100644
--- a/src/actions.cpp
+++ b/src/actions.cpp
@@ -27,6 +27,7 @@
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <iostream>
+#include <readline/readline.h>
#include "actions.h"
#include "charset.h"
@@ -191,6 +192,7 @@ void resizeScreen(bool reload_main_window)
// update internal screen dimensions
if (reload_main_window)
{
+ rl_resize_terminal();
endwin();
refresh();
}
@@ -1189,7 +1191,7 @@ void SetCrossfade::run()
Statusbar::lock();
Statusbar::put() << "Set crossfade to: ";
- std::string crossfade = wFooter->getString(3);
+ std::string crossfade = wFooter->getString();
Statusbar::unlock();
int cf = fromString<unsigned>(crossfade);
lowerBoundCheck(cf, 1);
@@ -1203,7 +1205,7 @@ void SetVolume::run()
Statusbar::lock();
Statusbar::put() << "Set volume to: ";
- std::string strvolume = wFooter->getString(3);
+ std::string strvolume = wFooter->getString();
Statusbar::unlock();
int volume = fromString<unsigned>(strvolume);
boundsCheck(volume, 0, 100);
@@ -1765,7 +1767,7 @@ void ApplyFilter::run()
Statusbar::lock();
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->setGetStringHelper(Statusbar::Helpers::getString);
Statusbar::unlock();
diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp
index ea025af3..f0084ae2 100644
--- a/src/ncmpcpp.cpp
+++ b/src/ncmpcpp.cpp
@@ -53,6 +53,7 @@ namespace
{
std::ofstream errorlog;
std::streambuf *cerr_buffer;
+ bool run_resize_screen = false;
# if !defined(WIN32)
void sighandler(int signal)
@@ -63,7 +64,7 @@ namespace
}
else if (signal == SIGWINCH)
{
- Actions::resizeScreen(true);
+ run_resize_screen = true;
}
}
# 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->setTimeout(500);
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
- wFooter->createHistory();
// initialize global timer
gettimeofday(&Timer, 0);
@@ -225,6 +225,12 @@ int main(int argc, char **argv)
}
Status::trace();
+
+ if (run_resize_screen)
+ {
+ Actions::resizeScreen(true);
+ run_resize_screen = false;
+ }
// header stuff
if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec)
diff --git a/src/status.cpp b/src/status.cpp
index 4badb49a..34b67943 100644
--- a/src/status.cpp
+++ b/src/status.cpp
@@ -118,7 +118,7 @@ void Status::handleServerError(MPD::ServerError &e)
{
wFooter->setGetStringHelper(nullptr);
Statusbar::put() << "Password: ";
- Mpd.SetPassword(wFooter->getString(-1, 0, 1));
+ Mpd.SetPassword(wFooter->getString(0, true));
Mpd.SendPassword();
Statusbar::msg("Password accepted");
wFooter->setGetStringHelper(Statusbar::Helpers::getString);
diff --git a/src/statusbar.cpp b/src/statusbar.cpp
index 34c490ae..e1e7280f 100644
--- a/src/statusbar.cpp
+++ b/src/statusbar.cpp
@@ -189,13 +189,13 @@ void Statusbar::Helpers::mpd()
Status::update(Mpd.noidle());
}
-bool Statusbar::Helpers::getString(const std::wstring &)
+bool Statusbar::Helpers::getString(const char *)
{
Status::trace();
return true;
}
-bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const std::wstring &ws)
+bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const char *s)
{
using Global::myScreen;
// 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.
if (wFooter->inputQueue().empty() || wFooter->inputQueue().front() == KEY_ENTER)
{
- if (m_ws != ws)
+ if (m_s != s)
{
- m_ws = ws;
- m_f->applyFilter(ToString(m_ws));
+ m_s = s;
+ m_f->applyFilter(m_s);
myScreen->refreshWindow();
}
Status::trace();
@@ -216,13 +216,13 @@ bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const std::wstring &
return true;
}
-bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const std::wstring &ws)
+bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
{
bool continue_ = true;
- if (m_ws != ws)
+ if (m_s != s)
{
- m_ws = ws;
- auto cmd = Bindings.findCommand(ToString(m_ws));
+ m_s = s;
+ auto cmd = Bindings.findCommand(m_s);
if (cmd && cmd->immediate())
continue_ = false;
}
diff --git a/src/statusbar.h b/src/statusbar.h
index 8edea90a..699eef92 100644
--- a/src/statusbar.h
+++ b/src/statusbar.h
@@ -71,27 +71,28 @@ namespace Helpers {//
void mpd();
/// 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)
struct ApplyFilterImmediately
{
- ApplyFilterImmediately(Filterable *f, const std::wstring &filter)
- : m_f(f), m_ws(filter) { }
+ template <typename StringT>
+ 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:
Filterable *m_f;
- std::wstring m_ws;
+ std::string m_s;
};
struct TryExecuteImmediateCommand
{
- bool operator()(const std::wstring &ws);
+ bool operator()(const char *s);
private:
- std::wstring m_ws;
+ std::string m_s;
};
}
diff --git a/src/window.cpp b/src/window.cpp
index def2587d..1c2b9019 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -18,8 +18,12 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
+#include <cassert>
#include <cstring>
+#include <cstdio>
#include <cstdlib>
+#include <readline/history.h>
+#include <readline/readline.h>
#ifdef WIN32
# include <winsock.h>
@@ -33,6 +37,111 @@
#include "utility/wide_string.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 {//
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();
cbreak();
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()
@@ -93,7 +208,6 @@ Window::Window(size_t startx,
m_border(border),
m_get_string_helper(0),
m_title(title),
- m_history(0),
m_bold_counter(0),
m_underline_counter(0),
m_reverse_counter(0),
@@ -145,7 +259,6 @@ Window::Window(const Window &rhs)
, m_color_stack(rhs.m_color_stack)
, m_input_queue(rhs.m_input_queue)
, 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_underline_counter(rhs.m_underline_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_input_queue(std::move(rhs.m_input_queue))
, m_fds(std::move(rhs.m_fds))
-, m_history(rhs.m_history)
, m_bold_counter(rhs.m_bold_counter)
, m_underline_counter(rhs.m_underline_counter)
, m_reverse_counter(rhs.m_reverse_counter)
@@ -179,7 +291,6 @@ Window::Window(Window &&rhs)
{
rhs.m_window = 0;
rhs.m_border_window = 0;
- rhs.m_history = 0;
}
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_input_queue, rhs.m_input_queue);
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_underline_counter, rhs.m_underline_counter);
std::swap(m_reverse_counter, rhs.m_reverse_counter);
@@ -213,7 +323,6 @@ Window::~Window()
{
delwin(m_window);
delwin(m_border_window);
- delete m_history;
}
void Window::setColor(Color fg, Color bg)
@@ -286,18 +395,6 @@ void Window::setTitle(const std::string &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)
{
delwin(m_window);
@@ -481,229 +578,39 @@ void Window::pushChar(int 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;
- size_t beginning, maxbeginning, minx, x, real_x, y, maxx, real_maxx;
-
- getyx(m_window, y, x);
- minx = real_maxx = maxx = real_x = x;
-
+ rl::w = this;
+ getyx(m_window, rl::start_y, rl::start_x);
+ rl::width = width;
+ rl::encrypted = encrypted;
+ rl::base = base.c_str();
+
width--;
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);
-
- std::wstring wbase = ToWString(base);
- std::wstring *tmp = &wbase;
- std::list<std::wstring>::iterator history_it = m_history->end();
-
- 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);
+ keypad(m_window, 0);
+ mousemask(0, &oldmask);
+ char *input = readline(nullptr);
+ mousemask(oldmask, nullptr);
+ keypad(m_window, 1);
curs_set(0);
-
- if (m_history && !encrypted)
+ if (input != nullptr)
{
- if (history_it != m_history->end())
- {
- m_history->push_back(*history_it);
- tmp = &m_history->back();
- m_history->erase(history_it);
- }
- else
- m_history->push_back(*tmp);
+ if (input[0] != 0)
+ add_history(input);
+ result = input;
+ free(input);
}
-
- return ToString(*tmp);
+
+ return result;
}
void Window::goToXY(int x, int y)
@@ -739,6 +646,17 @@ bool Window::hasCoords(int &x, int &y)
# 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
{
if (m_border != Border::None)
diff --git a/src/window.h b/src/window.h
index 2b99349a..2fa62d77 100644
--- a/src/window.h
+++ b/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
/// to obtain string from Window::getString() function
/// @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
/// @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
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
/// @param startx X position of left upper corner of constructed window
@@ -229,14 +229,13 @@ struct Window
/// @see setGetStringHelper()
/// @see SetTimeout()
/// @see CreateHistory()
- std::string getString(const std::string &base, size_t length_ = -1,
- size_t width = 0, bool encrypted = 0);
+ std::string getString(const std::string &base, size_t width = 0, bool encrypted = 0);
/// Wrapper for above function that doesn't take base string (it will be empty).
/// 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
@@ -262,6 +261,11 @@ struct Window
/// @param helper pointer to function that matches getStringHelper prototype
/// @see getString()
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
/// @param fg foregound base color
@@ -280,13 +284,6 @@ struct Window
/// @param new_title new title for window
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
/// @see refresh()
void display();
@@ -509,9 +506,6 @@ private:
typedef std::vector< std::pair<int, void (*)()> > FDCallbacks;
FDCallbacks m_fds;
- /// pointer to container used as history
- std::list<std::wstring> *m_history;
-
/// counters for format flags
int m_bold_counter;
int m_underline_counter;