From 489af9787c89c1ee390deb39b280fcc7df727f93 Mon Sep 17 00:00:00 2001 From: fluxgen Date: Tue, 24 Jun 2003 10:22:42 +0000 Subject: emacs keybindings and tab completion, thanks David J Burger --- util/fbrun/FbRun.cc | 279 +++++++++++++++++++++++++++++++++++++++++----------- util/fbrun/FbRun.hh | 26 ++++- 2 files changed, 242 insertions(+), 63 deletions(-) diff --git a/util/fbrun/FbRun.cc b/util/fbrun/FbRun.cc index 992255f..334414b 100644 --- a/util/fbrun/FbRun.cc +++ b/util/fbrun/FbRun.cc @@ -1,5 +1,5 @@ -// FbRun.hh -// Copyright (c) 2002 Henrik Kinnunen (fluxgen@linuxmail.org) +// FbRun.cc +// Copyright (c) 2002-2003 Henrik Kinnunen (fluxgenusers.sourceforge.net) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: FbRun.cc,v 1.11 2003/04/27 02:26:21 rathnor Exp $ +// $Id: FbRun.cc,v 1.12 2003/06/24 10:22:42 fluxgen Exp $ #include "FbRun.hh" @@ -50,13 +50,17 @@ FbRun::FbRun(int x, int y, size_t width): m_end(false), m_current_history_item(0), m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)), - m_cursor_pos(0) { - XDefineCursor(FbTk::App::instance()->display(), m_win.window(), m_cursor); + m_start_pos(0), + m_end_pos(0), + m_cursor_pos(0) +{ + m_win.setCursor(m_cursor); // setting nomaximize in local resize resize(width, m_font.height()); FbTk::EventManager::instance()->registerEventHandler(*this, m_win.window()); } + FbRun::~FbRun() { hide(); FbTk::EventManager::instance()->unregisterEventHandler(m_win.window()); @@ -71,7 +75,7 @@ void FbRun::run(const std::string &command) { } hide(); // hide gui - + // save command history to file if (m_runtext.size() != 0) { // no need to save empty command // open file in append mode @@ -129,7 +133,6 @@ void FbRun::setBackground(const FbTk::Color &color) { redrawLabel(); } - void FbRun::setText(const string &text) { m_runtext = text; redrawLabel(); @@ -144,7 +147,7 @@ void FbRun::move(int x, int y) { } void FbRun::resize(size_t width, size_t height) { - m_win.resize(width, height); + m_win.resize(width, height); setNoMaximize(); } @@ -167,49 +170,79 @@ void FbRun::drawString(int x, int y, const char *text, size_t len) { assert(m_gc); - // check right boundary and adjust text drawing - size_t text_width = m_font.textWidth(text, len); - size_t startpos = 0; - if (text_width > m_win.width()) { - for (; startpos < len; ++startpos) { - if (m_font.textWidth(text+startpos, len-startpos) < m_win.width()) - break; - } - } - - m_font.drawText(m_win.window(), DefaultScreen(m_display), m_gc, text + startpos, len-startpos, x, y - 2); - int cursor_pos = m_font.textWidth(text + m_cursor_pos, len - startpos) + 1; + m_font.drawText(m_win.window(), DefaultScreen(m_display), m_gc, text + m_start_pos, m_end_pos - m_start_pos, x, y - 2); // draw cursor position - XDrawLine(FbTk::App::instance()->display(), m_win.window(), m_gc, - cursor_pos, 0, - cursor_pos, m_font.height()); + int cursor_pos = m_font.textWidth(text + m_start_pos, m_cursor_pos) + 1; + m_win.drawLine(m_gc, cursor_pos, 0, cursor_pos, m_font.height()); } void FbRun::keyPressEvent(XKeyEvent &ke) { KeySym ks; char keychar[1]; XLookupString(&ke, keychar, 1, &ks, 0); - if (ks == XK_Escape) { - m_end = true; - hide(); - FbTk::App::instance()->end(); // end program - } else if (ks == XK_Return) { - run(m_runtext); - m_runtext = ""; // clear text - } else if (ks == XK_BackSpace) { - if (m_runtext.size() != 0) { // we can't erase what we don't have ;) - m_runtext.erase(m_runtext.size()-1); - redrawLabel(); - m_cursor_pos--; + // a modifier key by itself doesn't do anything + if (IsModifierKey(ks)) return; + + if (ke.state) { // a modifier key is down + if (ke.state == ControlMask) { + switch (ks) { + case XK_b: + cursorLeft(); + break; + case XK_f: + cursorRight(); + break; + case XK_p: + prevHistoryItem(); + break; + case XK_n: + nextHistoryItem(); + break; + case XK_a: + cursorHome(); + break; + case XK_e: + cursorEnd(); + break; + case XK_d: + deleteForward(); + break; + case XK_k: + killToEnd(); + break; + } + } else if (ke.state == (Mod1Mask | ShiftMask)) { + switch (ks) { + case XK_less: + firstHistoryItem(); + break; + case XK_greater: + lastHistoryItem(); + break; + } + } else if (ke.state == ShiftMask) { + if (isprint(keychar[0]))insertCharacter(ks, keychar); } - } else if (! IsModifierKey(ks) && !IsCursorKey(ks)) { // insert normal character at cursor pos - char in_char[2] = {keychar[0], 0}; - m_runtext.insert(m_cursor_pos, in_char); - m_cursor_pos++; - redrawLabel(); - } else if (IsCursorKey(ks)) { - + } else { // no modifier key switch (ks) { + case XK_Escape: + m_end = true; + hide(); + FbTk::App::instance()->end(); // end program + break; + case XK_Return: + run(m_runtext); + m_runtext = ""; // clear text + break; + case XK_BackSpace: + backspace(); + break; + case XK_Home: + cursorHome(); + break; + case XK_End: + cursorEnd(); + break; case XK_Up: prevHistoryItem(); break; @@ -222,18 +255,20 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { case XK_Right: cursorRight(); break; + case XK_Tab: + tabCompleteHistory(); + break; + default: + if (isprint(keychar[0])) insertCharacter(ks, keychar); } - redrawLabel(); - } else if (ks == XK_End) { - m_cursor_pos = m_runtext.size() - 1; } + redrawLabel(); } void FbRun::exposeEvent(XExposeEvent &ev) { redrawLabel(); } - void FbRun::setNoMaximize() { // we don't need to maximize this window XSizeHints sh; @@ -246,32 +281,160 @@ void FbRun::setNoMaximize() { } void FbRun::prevHistoryItem() { - - if (m_current_history_item > 0 && m_history.size() > 0) + if (m_history.size() == 0 || m_current_history_item == 0) { + XBell(m_display, 0); + } else { m_current_history_item--; - if (m_current_history_item < m_history.size()) m_runtext = m_history[m_current_history_item]; + m_cursor_pos = m_end_pos = m_runtext.size(); + adjustStartPos(); + } } void FbRun::nextHistoryItem() { - m_current_history_item++; - if (m_current_history_item >= m_history.size()) { - m_current_history_item = m_history.size(); - m_runtext = ""; - return; - } else + if (m_current_history_item == m_history.size()) { + XBell(m_display, 0); + } else { + m_current_history_item++; + if (m_current_history_item == m_history.size()) { + m_current_history_item = m_history.size(); + m_runtext = ""; + m_start_pos = m_cursor_pos = m_end_pos = 0; + } else { + m_runtext = m_history[m_current_history_item]; + m_cursor_pos = m_end_pos = m_runtext.size(); + adjustStartPos(); + } + } +} + +void FbRun::firstHistoryItem() { + if (m_history.size() == 0 || m_current_history_item == 0) { + XBell(m_display, 0); + } else { + m_current_history_item = 0; m_runtext = m_history[m_current_history_item]; + m_cursor_pos = m_end_pos = m_runtext.size(); + adjustStartPos(); + } +} +void FbRun::lastHistoryItem() { + // actually one past the end + if (m_history.size() == 0) { + XBell(m_display, 0); + } else { + m_current_history_item = m_history.size(); + m_runtext = ""; + m_start_pos = m_cursor_pos = m_end_pos = 0; + } } +void FbRun::tabCompleteHistory() { + if (m_current_history_item == 0) { + XBell(m_display, 0); + } else { + int history_item = m_current_history_item - 1; + string prefix = m_runtext.substr(0, m_cursor_pos); + while (history_item > - 1) { + if (m_history[history_item].find(prefix) == 0) { + m_current_history_item = history_item; + m_runtext = m_history[m_current_history_item]; + adjustEndPos(); + break; + } + history_item--; + } + if (history_item == -1) XBell(m_display, 0); + } +} void FbRun::cursorLeft() { - if (m_cursor_pos > 0) + if (m_cursor_pos) m_cursor_pos--; + else if (m_start_pos) { + m_start_pos--; + adjustEndPos(); + } } void FbRun::cursorRight() { + if (m_start_pos + m_cursor_pos < m_end_pos) + m_cursor_pos++; + else if (m_end_pos < m_runtext.size()) { + m_cursor_pos++; + m_end_pos++; + adjustStartPos(); + } +} + +void FbRun::cursorHome() { + m_start_pos = m_cursor_pos = 0; + adjustEndPos(); +} + +void FbRun::cursorEnd() { + m_cursor_pos = m_end_pos = m_runtext.size(); + adjustStartPos(); +} + +void FbRun::backspace() { + if (m_start_pos || m_cursor_pos) { + m_runtext.erase(m_start_pos + m_cursor_pos - 1, 1); + if (m_cursor_pos) + m_cursor_pos--; + else + m_start_pos--; + adjustEndPos(); + } +} + +void FbRun::deleteForward() { + if (m_start_pos + m_cursor_pos < m_end_pos) { + m_runtext.erase(m_start_pos + m_cursor_pos, 1); + adjustEndPos(); + } +} + +void FbRun::killToEnd() { + if (m_start_pos + m_cursor_pos < m_end_pos) { + m_runtext.erase(m_start_pos + m_cursor_pos); + adjustEndPos(); + } +} + +void FbRun::insertCharacter(KeySym ks, char *keychar) { + char in_char[2] = {keychar[0], 0}; + m_runtext.insert(m_start_pos + m_cursor_pos, in_char); m_cursor_pos++; - if (m_cursor_pos >= m_runtext.size()) - m_cursor_pos = m_runtext.size() - 1; + m_end_pos++; + if (m_start_pos + m_cursor_pos < m_end_pos) + adjustEndPos(); + else + adjustStartPos(); +} + +void FbRun::adjustEndPos() { + m_end_pos = m_runtext.size(); + const char *text = m_runtext.c_str(); + int text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); + while (text_width > m_win.width()) { + m_end_pos--; + text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); + } +} + +void FbRun::adjustStartPos() { + const char *text = m_runtext.c_str(); + int text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); + if (text_width < m_win.width()) return; + int start_pos = 0; + text_width = m_font.textWidth(text + start_pos, m_end_pos - start_pos); + while (text_width > m_win.width()) { + start_pos++; + text_width = m_font.textWidth(text + start_pos, m_end_pos - start_pos); + } + // adjust m_cursor_pos according relative to change to m_start_pos + m_cursor_pos -= start_pos - m_start_pos; + m_start_pos = start_pos; } diff --git a/util/fbrun/FbRun.hh b/util/fbrun/FbRun.hh index 29a9afd..a92ca90 100644 --- a/util/fbrun/FbRun.hh +++ b/util/fbrun/FbRun.hh @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: FbRun.hh,v 1.9 2003/03/22 11:31:43 fluxgen Exp $ +// $Id: FbRun.hh,v 1.10 2003/06/24 10:22:42 fluxgen Exp $ #ifndef FBRUN_HH #define FBRUN_HH @@ -71,7 +71,7 @@ public: void exposeEvent(XExposeEvent &ev); void keyPressEvent(XKeyEvent &ev); ///@} - + private: void nextHistoryItem(); void prevHistoryItem(); @@ -79,11 +79,23 @@ private: void cursorRight(); void drawString(int x, int y, const char *text, size_t len); void getSize(size_t &width, size_t &height); - void createWindow(int x, int y, size_t width, size_t height); - void redrawLabel(); + void createWindow(int x, int y, size_t width, size_t height); + void redrawLabel(); /// set no maximizable for this window void setNoMaximize(); + void cursorHome(); + void cursorEnd(); + void backspace(); + void deleteForward(); + void killToEnd(); + void insertCharacter(KeySym ks, char *keychar); + void adjustStartPos(); + void adjustEndPos(); + void firstHistoryItem(); + void lastHistoryItem(); + void tabCompleteHistory(); + FbTk::Font m_font; ///< font used to draw command text FbTk::FbWindow m_win; ///< toplevel window Display *m_display; ///< display connection @@ -95,7 +107,11 @@ private: size_t m_current_history_item; ///< holds current position in command history std::string m_history_file; ///< holds filename for command history file Cursor m_cursor; - int m_cursor_pos; + + int m_start_pos; ///< start position of portion of text to display + int m_cursor_pos; ///< relative to m_start_pos + int m_end_pos; ///< end postition of portion of text to display + }; #endif // FBRUN_HH -- cgit v0.11.2