diff options
author | Andrzej Rybczak <electricityispower@gmail.com> | 2013-12-25 23:31:27 +0100 |
---|---|---|
committer | Andrzej Rybczak <electricityispower@gmail.com> | 2013-12-25 23:31:27 +0100 |
commit | 70945596ef4abbdbaaeb30f52d9070ad07da8804 (patch) | |
tree | e1e157b9ae2444713ba8164016e9a0af1e17d09d /src | |
parent | 819d8baebd917ea64834eee4072f3c28d5536e51 (diff) |
window: use readline for handling line input
Diffstat (limited to 'src')
-rw-r--r-- | src/actions.cpp | 8 | ||||
-rw-r--r-- | src/ncmpcpp.cpp | 10 | ||||
-rw-r--r-- | src/status.cpp | 2 | ||||
-rw-r--r-- | src/statusbar.cpp | 18 | ||||
-rw-r--r-- | src/statusbar.h | 15 | ||||
-rw-r--r-- | src/window.cpp | 386 | ||||
-rw-r--r-- | src/window.h | 26 |
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; |