diff options
author | fluxgen <fluxgen> | 2003-06-24 10:22:42 (GMT) |
---|---|---|
committer | fluxgen <fluxgen> | 2003-06-24 10:22:42 (GMT) |
commit | 489af9787c89c1ee390deb39b280fcc7df727f93 (patch) | |
tree | 181829f163cdae9216ada83761fc8cb235a687dc | |
parent | fc5de0455e1d99b6b89168ab3c9b973360791d68 (diff) | |
download | fluxbox-489af9787c89c1ee390deb39b280fcc7df727f93.zip fluxbox-489af9787c89c1ee390deb39b280fcc7df727f93.tar.bz2 |
emacs keybindings and tab completion, thanks David J Burger
-rw-r--r-- | util/fbrun/FbRun.cc | 279 | ||||
-rw-r--r-- | 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 @@ | |||
1 | // FbRun.hh | 1 | // FbRun.cc |
2 | // Copyright (c) 2002 Henrik Kinnunen (fluxgen@linuxmail.org) | 2 | // Copyright (c) 2002-2003 Henrik Kinnunen (fluxgen<at>users.sourceforge.net) |
3 | // | 3 | // |
4 | // Permission is hereby granted, free of charge, to any person obtaining a | 4 | // Permission is hereby granted, free of charge, to any person obtaining a |
5 | // copy of this software and associated documentation files (the "Software"), | 5 | // copy of this software and associated documentation files (the "Software"), |
@@ -19,7 +19,7 @@ | |||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. | 20 | // DEALINGS IN THE SOFTWARE. |
21 | 21 | ||
22 | // $Id: FbRun.cc,v 1.11 2003/04/27 02:26:21 rathnor Exp $ | 22 | // $Id: FbRun.cc,v 1.12 2003/06/24 10:22:42 fluxgen Exp $ |
23 | 23 | ||
24 | #include "FbRun.hh" | 24 | #include "FbRun.hh" |
25 | 25 | ||
@@ -50,13 +50,17 @@ FbRun::FbRun(int x, int y, size_t width): | |||
50 | m_end(false), | 50 | m_end(false), |
51 | m_current_history_item(0), | 51 | m_current_history_item(0), |
52 | m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)), | 52 | m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)), |
53 | m_cursor_pos(0) { | 53 | m_start_pos(0), |
54 | XDefineCursor(FbTk::App::instance()->display(), m_win.window(), m_cursor); | 54 | m_end_pos(0), |
55 | m_cursor_pos(0) | ||
56 | { | ||
57 | m_win.setCursor(m_cursor); | ||
55 | // setting nomaximize in local resize | 58 | // setting nomaximize in local resize |
56 | resize(width, m_font.height()); | 59 | resize(width, m_font.height()); |
57 | FbTk::EventManager::instance()->registerEventHandler(*this, m_win.window()); | 60 | FbTk::EventManager::instance()->registerEventHandler(*this, m_win.window()); |
58 | } | 61 | } |
59 | 62 | ||
63 | |||
60 | FbRun::~FbRun() { | 64 | FbRun::~FbRun() { |
61 | hide(); | 65 | hide(); |
62 | FbTk::EventManager::instance()->unregisterEventHandler(m_win.window()); | 66 | FbTk::EventManager::instance()->unregisterEventHandler(m_win.window()); |
@@ -71,7 +75,7 @@ void FbRun::run(const std::string &command) { | |||
71 | } | 75 | } |
72 | 76 | ||
73 | hide(); // hide gui | 77 | hide(); // hide gui |
74 | 78 | ||
75 | // save command history to file | 79 | // save command history to file |
76 | if (m_runtext.size() != 0) { // no need to save empty command | 80 | if (m_runtext.size() != 0) { // no need to save empty command |
77 | // open file in append mode | 81 | // open file in append mode |
@@ -129,7 +133,6 @@ void FbRun::setBackground(const FbTk::Color &color) { | |||
129 | redrawLabel(); | 133 | redrawLabel(); |
130 | } | 134 | } |
131 | 135 | ||
132 | |||
133 | void FbRun::setText(const string &text) { | 136 | void FbRun::setText(const string &text) { |
134 | m_runtext = text; | 137 | m_runtext = text; |
135 | redrawLabel(); | 138 | redrawLabel(); |
@@ -144,7 +147,7 @@ void FbRun::move(int x, int y) { | |||
144 | } | 147 | } |
145 | 148 | ||
146 | void FbRun::resize(size_t width, size_t height) { | 149 | void FbRun::resize(size_t width, size_t height) { |
147 | m_win.resize(width, height); | 150 | m_win.resize(width, height); |
148 | setNoMaximize(); | 151 | setNoMaximize(); |
149 | } | 152 | } |
150 | 153 | ||
@@ -167,49 +170,79 @@ void FbRun::drawString(int x, int y, | |||
167 | const char *text, size_t len) { | 170 | const char *text, size_t len) { |
168 | assert(m_gc); | 171 | assert(m_gc); |
169 | 172 | ||
170 | // check right boundary and adjust text drawing | 173 | m_font.drawText(m_win.window(), DefaultScreen(m_display), m_gc, text + m_start_pos, m_end_pos - m_start_pos, x, y - 2); |
171 | size_t text_width = m_font.textWidth(text, len); | ||
172 | size_t startpos = 0; | ||
173 | if (text_width > m_win.width()) { | ||
174 | for (; startpos < len; ++startpos) { | ||
175 | if (m_font.textWidth(text+startpos, len-startpos) < m_win.width()) | ||
176 | break; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | m_font.drawText(m_win.window(), DefaultScreen(m_display), m_gc, text + startpos, len-startpos, x, y - 2); | ||
181 | int cursor_pos = m_font.textWidth(text + m_cursor_pos, len - startpos) + 1; | ||
182 | // draw cursor position | 174 | // draw cursor position |
183 | XDrawLine(FbTk::App::instance()->display(), m_win.window(), m_gc, | 175 | int cursor_pos = m_font.textWidth(text + m_start_pos, m_cursor_pos) + 1; |
184 | cursor_pos, 0, | 176 | m_win.drawLine(m_gc, cursor_pos, 0, cursor_pos, m_font.height()); |
185 | cursor_pos, m_font.height()); | ||
186 | } | 177 | } |
187 | 178 | ||
188 | void FbRun::keyPressEvent(XKeyEvent &ke) { | 179 | void FbRun::keyPressEvent(XKeyEvent &ke) { |
189 | KeySym ks; | 180 | KeySym ks; |
190 | char keychar[1]; | 181 | char keychar[1]; |
191 | XLookupString(&ke, keychar, 1, &ks, 0); | 182 | XLookupString(&ke, keychar, 1, &ks, 0); |
192 | if (ks == XK_Escape) { | 183 | // a modifier key by itself doesn't do anything |
193 | m_end = true; | 184 | if (IsModifierKey(ks)) return; |
194 | hide(); | 185 | |
195 | FbTk::App::instance()->end(); // end program | 186 | if (ke.state) { // a modifier key is down |
196 | } else if (ks == XK_Return) { | 187 | if (ke.state == ControlMask) { |
197 | run(m_runtext); | 188 | switch (ks) { |
198 | m_runtext = ""; // clear text | 189 | case XK_b: |
199 | } else if (ks == XK_BackSpace) { | 190 | cursorLeft(); |
200 | if (m_runtext.size() != 0) { // we can't erase what we don't have ;) | 191 | break; |
201 | m_runtext.erase(m_runtext.size()-1); | 192 | case XK_f: |
202 | redrawLabel(); | 193 | cursorRight(); |
203 | m_cursor_pos--; | 194 | break; |
195 | case XK_p: | ||
196 | prevHistoryItem(); | ||
197 | break; | ||
198 | case XK_n: | ||
199 | nextHistoryItem(); | ||
200 | break; | ||
201 | case XK_a: | ||
202 | cursorHome(); | ||
203 | break; | ||
204 | case XK_e: | ||
205 | cursorEnd(); | ||
206 | break; | ||
207 | case XK_d: | ||
208 | deleteForward(); | ||
209 | break; | ||
210 | case XK_k: | ||
211 | killToEnd(); | ||
212 | break; | ||
213 | } | ||
214 | } else if (ke.state == (Mod1Mask | ShiftMask)) { | ||
215 | switch (ks) { | ||
216 | case XK_less: | ||
217 | firstHistoryItem(); | ||
218 | break; | ||
219 | case XK_greater: | ||
220 | lastHistoryItem(); | ||
221 | break; | ||
222 | } | ||
223 | } else if (ke.state == ShiftMask) { | ||
224 | if (isprint(keychar[0]))insertCharacter(ks, keychar); | ||
204 | } | 225 | } |
205 | } else if (! IsModifierKey(ks) && !IsCursorKey(ks)) { // insert normal character at cursor pos | 226 | } else { // no modifier key |
206 | char in_char[2] = {keychar[0], 0}; | ||
207 | m_runtext.insert(m_cursor_pos, in_char); | ||
208 | m_cursor_pos++; | ||
209 | redrawLabel(); | ||
210 | } else if (IsCursorKey(ks)) { | ||
211 | |||
212 | switch (ks) { | 227 | switch (ks) { |
228 | case XK_Escape: | ||
229 | m_end = true; | ||
230 | hide(); | ||
231 | FbTk::App::instance()->end(); // end program | ||
232 | break; | ||
233 | case XK_Return: | ||
234 | run(m_runtext); | ||
235 | m_runtext = ""; // clear text | ||
236 | break; | ||
237 | case XK_BackSpace: | ||
238 | backspace(); | ||
239 | break; | ||
240 | case XK_Home: | ||
241 | cursorHome(); | ||
242 | break; | ||
243 | case XK_End: | ||
244 | cursorEnd(); | ||
245 | break; | ||
213 | case XK_Up: | 246 | case XK_Up: |
214 | prevHistoryItem(); | 247 | prevHistoryItem(); |
215 | break; | 248 | break; |
@@ -222,18 +255,20 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { | |||
222 | case XK_Right: | 255 | case XK_Right: |
223 | cursorRight(); | 256 | cursorRight(); |
224 | break; | 257 | break; |
258 | case XK_Tab: | ||
259 | tabCompleteHistory(); | ||
260 | break; | ||
261 | default: | ||
262 | if (isprint(keychar[0])) insertCharacter(ks, keychar); | ||
225 | } | 263 | } |
226 | redrawLabel(); | ||
227 | } else if (ks == XK_End) { | ||
228 | m_cursor_pos = m_runtext.size() - 1; | ||
229 | } | 264 | } |
265 | redrawLabel(); | ||
230 | } | 266 | } |
231 | 267 | ||
232 | void FbRun::exposeEvent(XExposeEvent &ev) { | 268 | void FbRun::exposeEvent(XExposeEvent &ev) { |
233 | redrawLabel(); | 269 | redrawLabel(); |
234 | } | 270 | } |
235 | 271 | ||
236 | |||
237 | void FbRun::setNoMaximize() { | 272 | void FbRun::setNoMaximize() { |
238 | // we don't need to maximize this window | 273 | // we don't need to maximize this window |
239 | XSizeHints sh; | 274 | XSizeHints sh; |
@@ -246,32 +281,160 @@ void FbRun::setNoMaximize() { | |||
246 | } | 281 | } |
247 | 282 | ||
248 | void FbRun::prevHistoryItem() { | 283 | void FbRun::prevHistoryItem() { |
249 | 284 | if (m_history.size() == 0 || m_current_history_item == 0) { | |
250 | if (m_current_history_item > 0 && m_history.size() > 0) | 285 | XBell(m_display, 0); |
286 | } else { | ||
251 | m_current_history_item--; | 287 | m_current_history_item--; |
252 | if (m_current_history_item < m_history.size()) | ||
253 | m_runtext = m_history[m_current_history_item]; | 288 | m_runtext = m_history[m_current_history_item]; |
289 | m_cursor_pos = m_end_pos = m_runtext.size(); | ||
290 | adjustStartPos(); | ||
291 | } | ||
254 | } | 292 | } |
255 | 293 | ||
256 | void FbRun::nextHistoryItem() { | 294 | void FbRun::nextHistoryItem() { |
257 | m_current_history_item++; | 295 | if (m_current_history_item == m_history.size()) { |
258 | if (m_current_history_item >= m_history.size()) { | 296 | XBell(m_display, 0); |
259 | m_current_history_item = m_history.size(); | 297 | } else { |
260 | m_runtext = ""; | 298 | m_current_history_item++; |
261 | return; | 299 | if (m_current_history_item == m_history.size()) { |
262 | } else | 300 | m_current_history_item = m_history.size(); |
301 | m_runtext = ""; | ||
302 | m_start_pos = m_cursor_pos = m_end_pos = 0; | ||
303 | } else { | ||
304 | m_runtext = m_history[m_current_history_item]; | ||
305 | m_cursor_pos = m_end_pos = m_runtext.size(); | ||
306 | adjustStartPos(); | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | |||
311 | void FbRun::firstHistoryItem() { | ||
312 | if (m_history.size() == 0 || m_current_history_item == 0) { | ||
313 | XBell(m_display, 0); | ||
314 | } else { | ||
315 | m_current_history_item = 0; | ||
263 | m_runtext = m_history[m_current_history_item]; | 316 | m_runtext = m_history[m_current_history_item]; |
317 | m_cursor_pos = m_end_pos = m_runtext.size(); | ||
318 | adjustStartPos(); | ||
319 | } | ||
320 | } | ||
264 | 321 | ||
322 | void FbRun::lastHistoryItem() { | ||
323 | // actually one past the end | ||
324 | if (m_history.size() == 0) { | ||
325 | XBell(m_display, 0); | ||
326 | } else { | ||
327 | m_current_history_item = m_history.size(); | ||
328 | m_runtext = ""; | ||
329 | m_start_pos = m_cursor_pos = m_end_pos = 0; | ||
330 | } | ||
265 | } | 331 | } |
266 | 332 | ||
333 | void FbRun::tabCompleteHistory() { | ||
334 | if (m_current_history_item == 0) { | ||
335 | XBell(m_display, 0); | ||
336 | } else { | ||
337 | int history_item = m_current_history_item - 1; | ||
338 | string prefix = m_runtext.substr(0, m_cursor_pos); | ||
339 | while (history_item > - 1) { | ||
340 | if (m_history[history_item].find(prefix) == 0) { | ||
341 | m_current_history_item = history_item; | ||
342 | m_runtext = m_history[m_current_history_item]; | ||
343 | adjustEndPos(); | ||
344 | break; | ||
345 | } | ||
346 | history_item--; | ||
347 | } | ||
348 | if (history_item == -1) XBell(m_display, 0); | ||
349 | } | ||
350 | } | ||
267 | 351 | ||
268 | void FbRun::cursorLeft() { | 352 | void FbRun::cursorLeft() { |
269 | if (m_cursor_pos > 0) | 353 | if (m_cursor_pos) |
270 | m_cursor_pos--; | 354 | m_cursor_pos--; |
355 | else if (m_start_pos) { | ||
356 | m_start_pos--; | ||
357 | adjustEndPos(); | ||
358 | } | ||
271 | } | 359 | } |
272 | 360 | ||
273 | void FbRun::cursorRight() { | 361 | void FbRun::cursorRight() { |
362 | if (m_start_pos + m_cursor_pos < m_end_pos) | ||
363 | m_cursor_pos++; | ||
364 | else if (m_end_pos < m_runtext.size()) { | ||
365 | m_cursor_pos++; | ||
366 | m_end_pos++; | ||
367 | adjustStartPos(); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | void FbRun::cursorHome() { | ||
372 | m_start_pos = m_cursor_pos = 0; | ||
373 | adjustEndPos(); | ||
374 | } | ||
375 | |||
376 | void FbRun::cursorEnd() { | ||
377 | m_cursor_pos = m_end_pos = m_runtext.size(); | ||
378 | adjustStartPos(); | ||
379 | } | ||
380 | |||
381 | void FbRun::backspace() { | ||
382 | if (m_start_pos || m_cursor_pos) { | ||
383 | m_runtext.erase(m_start_pos + m_cursor_pos - 1, 1); | ||
384 | if (m_cursor_pos) | ||
385 | m_cursor_pos--; | ||
386 | else | ||
387 | m_start_pos--; | ||
388 | adjustEndPos(); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | void FbRun::deleteForward() { | ||
393 | if (m_start_pos + m_cursor_pos < m_end_pos) { | ||
394 | m_runtext.erase(m_start_pos + m_cursor_pos, 1); | ||
395 | adjustEndPos(); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | void FbRun::killToEnd() { | ||
400 | if (m_start_pos + m_cursor_pos < m_end_pos) { | ||
401 | m_runtext.erase(m_start_pos + m_cursor_pos); | ||
402 | adjustEndPos(); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | void FbRun::insertCharacter(KeySym ks, char *keychar) { | ||
407 | char in_char[2] = {keychar[0], 0}; | ||
408 | m_runtext.insert(m_start_pos + m_cursor_pos, in_char); | ||
274 | m_cursor_pos++; | 409 | m_cursor_pos++; |
275 | if (m_cursor_pos >= m_runtext.size()) | 410 | m_end_pos++; |
276 | m_cursor_pos = m_runtext.size() - 1; | 411 | if (m_start_pos + m_cursor_pos < m_end_pos) |
412 | adjustEndPos(); | ||
413 | else | ||
414 | adjustStartPos(); | ||
415 | } | ||
416 | |||
417 | void FbRun::adjustEndPos() { | ||
418 | m_end_pos = m_runtext.size(); | ||
419 | const char *text = m_runtext.c_str(); | ||
420 | int text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); | ||
421 | while (text_width > m_win.width()) { | ||
422 | m_end_pos--; | ||
423 | text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | void FbRun::adjustStartPos() { | ||
428 | const char *text = m_runtext.c_str(); | ||
429 | int text_width = m_font.textWidth(text + m_start_pos, m_end_pos - m_start_pos); | ||
430 | if (text_width < m_win.width()) return; | ||
431 | int start_pos = 0; | ||
432 | text_width = m_font.textWidth(text + start_pos, m_end_pos - start_pos); | ||
433 | while (text_width > m_win.width()) { | ||
434 | start_pos++; | ||
435 | text_width = m_font.textWidth(text + start_pos, m_end_pos - start_pos); | ||
436 | } | ||
437 | // adjust m_cursor_pos according relative to change to m_start_pos | ||
438 | m_cursor_pos -= start_pos - m_start_pos; | ||
439 | m_start_pos = start_pos; | ||
277 | } | 440 | } |
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 @@ | |||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. | 20 | // DEALINGS IN THE SOFTWARE. |
21 | 21 | ||
22 | // $Id: FbRun.hh,v 1.9 2003/03/22 11:31:43 fluxgen Exp $ | 22 | // $Id: FbRun.hh,v 1.10 2003/06/24 10:22:42 fluxgen Exp $ |
23 | 23 | ||
24 | #ifndef FBRUN_HH | 24 | #ifndef FBRUN_HH |
25 | #define FBRUN_HH | 25 | #define FBRUN_HH |
@@ -71,7 +71,7 @@ public: | |||
71 | void exposeEvent(XExposeEvent &ev); | 71 | void exposeEvent(XExposeEvent &ev); |
72 | void keyPressEvent(XKeyEvent &ev); | 72 | void keyPressEvent(XKeyEvent &ev); |
73 | ///@} | 73 | ///@} |
74 | 74 | ||
75 | private: | 75 | private: |
76 | void nextHistoryItem(); | 76 | void nextHistoryItem(); |
77 | void prevHistoryItem(); | 77 | void prevHistoryItem(); |
@@ -79,11 +79,23 @@ private: | |||
79 | void cursorRight(); | 79 | void cursorRight(); |
80 | void drawString(int x, int y, const char *text, size_t len); | 80 | void drawString(int x, int y, const char *text, size_t len); |
81 | void getSize(size_t &width, size_t &height); | 81 | void getSize(size_t &width, size_t &height); |
82 | void createWindow(int x, int y, size_t width, size_t height); | 82 | void createWindow(int x, int y, size_t width, size_t height); |
83 | void redrawLabel(); | 83 | void redrawLabel(); |
84 | /// set no maximizable for this window | 84 | /// set no maximizable for this window |
85 | void setNoMaximize(); | 85 | void setNoMaximize(); |
86 | 86 | ||
87 | void cursorHome(); | ||
88 | void cursorEnd(); | ||
89 | void backspace(); | ||
90 | void deleteForward(); | ||
91 | void killToEnd(); | ||
92 | void insertCharacter(KeySym ks, char *keychar); | ||
93 | void adjustStartPos(); | ||
94 | void adjustEndPos(); | ||
95 | void firstHistoryItem(); | ||
96 | void lastHistoryItem(); | ||
97 | void tabCompleteHistory(); | ||
98 | |||
87 | FbTk::Font m_font; ///< font used to draw command text | 99 | FbTk::Font m_font; ///< font used to draw command text |
88 | FbTk::FbWindow m_win; ///< toplevel window | 100 | FbTk::FbWindow m_win; ///< toplevel window |
89 | Display *m_display; ///< display connection | 101 | Display *m_display; ///< display connection |
@@ -95,7 +107,11 @@ private: | |||
95 | size_t m_current_history_item; ///< holds current position in command history | 107 | size_t m_current_history_item; ///< holds current position in command history |
96 | std::string m_history_file; ///< holds filename for command history file | 108 | std::string m_history_file; ///< holds filename for command history file |
97 | Cursor m_cursor; | 109 | Cursor m_cursor; |
98 | int m_cursor_pos; | 110 | |
111 | int m_start_pos; ///< start position of portion of text to display | ||
112 | int m_cursor_pos; ///< relative to m_start_pos | ||
113 | int m_end_pos; ///< end postition of portion of text to display | ||
114 | |||
99 | }; | 115 | }; |
100 | 116 | ||
101 | #endif // FBRUN_HH | 117 | #endif // FBRUN_HH |