4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 \brief Editor non-inline non-template implementation */
27 //#include "Editor.ih"
30 #include <senf/Utils/membind.hh>
31 #include <senf/Scheduler/Scheduler.hh>
33 //#include "Editor.mpp"
35 ///////////////////////////////cc.p////////////////////////////////////////
37 prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal)
38 : terminal_ (&terminal),
39 keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)),
40 timer_ ("senf::term::BaseEditor::keySequenceTimeout",
41 senf::membind(&BaseEditor::keySequenceTimeout, this)),
42 column_ (0u), displayHeight_ (1u), line_ (0u)
44 terminal_->setCallbacks(*this);
47 prefix_ void senf::term::BaseEditor::newline()
51 write(tifo_.getString(Terminfo::properties::ClrEol));
55 prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
60 if (tifo_.hasProperty(Terminfo::properties::ParmRightCursor)) {
61 write(tifo_.formatString(Terminfo::properties::ParmRightCursor, c - column_));
65 char const * cuf1 (tifo_.getString(Terminfo::properties::CursorRight));
72 else if (c < column_) {
73 if (tifo_.hasProperty(Terminfo::properties::ParmLeftCursor)) {
74 write(tifo_.formatString(Terminfo::properties::ParmLeftCursor, column_ - c));
78 char const * cub1 (tifo_.getString(Terminfo::properties::CursorLeft));
87 prefix_ void senf::term::BaseEditor::put(char ch)
89 if (column_ >= width()-1)
95 prefix_ void senf::term::BaseEditor::put(std::string const & text)
97 if (text.size() > width()-column_-1) {
98 write(text.substr(0,width()-column_-1));
99 column_ = width() - 1;
103 column_ += text.size();
107 prefix_ void senf::term::BaseEditor::clearLine()
110 write(tifo_.getString(Terminfo::properties::ClrEol));
114 prefix_ void senf::term::BaseEditor::setBold()
116 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
117 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
118 write(tifo_.getString(Terminfo::properties::EnterBoldMode));
121 prefix_ void senf::term::BaseEditor::setNormal()
123 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
124 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
125 write(tifo_.getString(Terminfo::properties::ExitAttributeMode));
128 prefix_ void senf::term::BaseEditor::maybeClrScr()
130 if (tifo_.hasProperty(Terminfo::properties::ClearScreen))
131 write(tifo_.getString(Terminfo::properties::ClearScreen));
134 prefix_ void senf::term::BaseEditor::toLine(unsigned l)
139 if (ll >= displayHeight_)
140 ll = displayHeight_-1;
142 if (tifo_.hasProperty(Terminfo::properties::ParmDownCursor)) {
143 write(tifo_.formatString(Terminfo::properties::ParmDownCursor, ll - line_));
147 char const * cud1 (tifo_.getString(Terminfo::properties::CursorDown));
154 else if (ll < line_) {
155 if (tifo_.hasProperty(Terminfo::properties::ParmUpCursor)) {
156 write(tifo_.formatString(Terminfo::properties::ParmUpCursor, line_ - ll));
160 char const * cuu1 (tifo_.getString(Terminfo::properties::CursorUp));
169 write(tifo_.getString(Terminfo::properties::ClrEol));
177 prefix_ void senf::term::BaseEditor::reset()
179 for (unsigned i (1); i < displayHeight_; ++i) {
187 prefix_ unsigned senf::term::BaseEditor::currentColumn()
193 prefix_ unsigned senf::term::BaseEditor::currentLine()
199 prefix_ bool senf::term::BaseEditor::cb_init()
202 tifo_.load(terminal_->terminalType());
203 keyParser_.load(tifo_);
205 catch (Terminfo::InvalidTerminfoException & ex) {
209 typedef Terminfo::properties p;
210 if (! (tifo_.hasProperty(p::ClrEol) &&
211 (tifo_.hasProperty(p::ParmRightCursor) || tifo_.hasProperty(p::CursorRight)) &&
212 (tifo_.hasProperty(p::ParmLeftCursor) || tifo_.hasProperty(p::CursorLeft))))
215 if (tifo_.hasProperty(Terminfo::properties::KeypadXmit))
216 write(tifo_.getString(Terminfo::properties::KeypadXmit));
220 prefix_ void senf::term::BaseEditor::cb_charReceived(char c)
223 timer_.timeout(senf::scheduler::eventTime() + keyTimeout_);
227 prefix_ void senf::term::BaseEditor::cb_windowSizeChanged()
229 if (column_ >= width())
233 prefix_ void senf::term::BaseEditor::keySequenceTimeout()
235 while (!inputBuffer_.empty()) {
237 v_keyReceived(keycode_t(inputBuffer_[0]));
238 inputBuffer_.erase(0, 1);
242 prefix_ void senf::term::BaseEditor::processKeys()
245 std::pair<senf::term::KeyParser::keycode_t, std::string::size_type> result
246 (keyParser_.lookup(inputBuffer_));
247 if (result.first == senf::term::KeyParser::Incomplete)
249 v_keyReceived(result.first);
250 inputBuffer_.erase(0, result.second);
251 } while (! inputBuffer_.empty());
255 prefix_ unsigned senf::term::BaseEditor::width()
258 return terminal_->width();
261 prefix_ unsigned senf::term::BaseEditor::height()
264 return terminal_->height();
267 prefix_ void senf::term::BaseEditor::write(char ch)
269 terminal_->write(ch);
272 prefix_ void senf::term::BaseEditor::write(std::string const & s)
274 for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
278 ///////////////////////////////////////////////////////////////////////////
280 prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb)
281 : BaseEditor(terminal), enabled_ (false), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u),
282 text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u)
284 defineKey(KeyParser::Return, &bindings::accept);
285 defineKey(KeyParser::Right, &bindings::forwardChar);
286 defineKey(KeyParser::Left, &bindings::backwardChar);
287 defineKey(KeyParser::Up, &bindings::prevHistory);
288 defineKey(KeyParser::Down, &bindings::nextHistory);
289 defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar);
290 defineKey(KeyParser::Delete, &bindings::deleteChar);
291 defineKey(KeyParser::Home, &bindings::beginningOfLine);
292 defineKey(KeyParser::End, &bindings::endOfLine);
293 defineKey(KeyParser::Ctrl('K'), &bindings::deleteToEndOfLine);
294 defineKey(KeyParser::Ctrl('A'), &bindings::beginningOfLine);
295 defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine);
296 defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar);
297 defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit);
298 defineKey(KeyParser::Ctrl('L'), &bindings::clearScreen);
301 prefix_ void senf::term::LineEditor::prompt(std::string const & text)
304 promptWidth_ = prompt_.size();
305 if (promptWidth_ > width() - 4 && width() > 4)
306 promptWidth_ = width() - 4;
307 editWidth_ = width() - promptWidth_ - 3;
312 prefix_ void senf::term::LineEditor::set(std::string const & text, unsigned pos)
316 if (point_ > text.size())
317 point_ = text.size();
319 if (point_ > editWidth_)
320 displayPos_ = point_ - editWidth_;
324 prefix_ void senf::term::LineEditor::show()
329 for (unsigned n (0); n < auxDisplay_.size(); ++n) {
337 prefix_ void senf::term::LineEditor::hide()
346 prefix_ void senf::term::LineEditor::accept()
351 pushHistory(text_, true);
356 prefix_ void senf::term::LineEditor::clear()
359 historyPoint_ = history_.size();
362 prefix_ void senf::term::LineEditor::redisplay()
364 redisplayNeeded_ = true;
367 prefix_ void senf::term::LineEditor::forceRedisplay()
373 if (prompt_.size() > promptWidth_)
374 put(prompt_.substr(prompt_.size()-promptWidth_));
377 put( displayPos_ > 0 ? '<' : ' ' );
378 if (text_.size() > displayPos_ + editWidth_) {
379 toColumn(editWidth_ + promptWidth_ + 1);
381 toColumn(promptWidth_ + 1);
384 put(text_.substr(displayPos_, editWidth_));
385 toColumn(point_ - displayPos_ + promptWidth_ + 1);
386 redisplayNeeded_ = false;
389 prefix_ void senf::term::LineEditor::gotoChar(unsigned n)
392 if (point_ > text_.size())
393 point_ = text_.size();
394 if (point_ < displayPos_)
395 displayPos_ = point_;
396 if (point_ > displayPos_+editWidth_)
397 displayPos_ = point_-editWidth_;
401 prefix_ void senf::term::LineEditor::scrollTo(unsigned n)
404 if (displayPos_ > text_.size())
405 displayPos_ = text_.size();
406 if (point_ < displayPos_)
407 point_ = displayPos_;
408 if (point_ > displayPos_+editWidth_)
409 point_ = displayPos_+editWidth_;
413 prefix_ void senf::term::LineEditor::deleteChar(unsigned n)
415 if (point_ >= text_.size())
417 text_.erase(point_, n);
421 prefix_ void senf::term::LineEditor::insert(char ch)
423 text_.insert(point_, std::string(1, ch));
428 prefix_ void senf::term::LineEditor::insert(std::string const & text)
430 text_.insert(point_, text);
431 gotoChar(point_+text.size());
435 prefix_ void senf::term::LineEditor::pushHistory(std::string const & text, bool accept)
438 && (accept || historyPoint_ == history_.size() || history_[historyPoint_] != text)
439 && (history_.empty() || history_.back() != text)) {
440 history_.push_back(text);
441 while (history_.size() > MAX_HISTORY_SIZE)
442 history_.erase(history_.begin());
444 historyPoint_ = history_.size() - 1;
448 prefix_ void senf::term::LineEditor::prevHistory()
450 if (historyPoint_ <= 0)
453 std::string entry (history_[--historyPoint_]);
454 set(entry, entry.size());
457 prefix_ void senf::term::LineEditor::nextHistory()
459 if (historyPoint_ >= history_.size())
463 if (historyPoint_ >= history_.size())
466 std::string entry (history_[historyPoint_]);
467 set(entry, entry.size());
471 prefix_ void senf::term::LineEditor::auxDisplay(unsigned line, std::string const & text)
476 while (auxDisplay_.size() < line+1)
477 auxDisplay_.push_back("");
478 auxDisplay_[line] = text;
481 prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight()
486 prefix_ void senf::term::LineEditor::clearAuxDisplay()
492 prefix_ std::string const & senf::term::LineEditor::text()
497 prefix_ unsigned senf::term::LineEditor::point()
502 prefix_ unsigned senf::term::LineEditor::displayPos()
507 prefix_ senf::term::LineEditor::keycode_t senf::term::LineEditor::lastKey()
512 prefix_ void senf::term::LineEditor::defineKey(keycode_t key, KeyBinding binding)
514 bindings_[key] = binding;
517 prefix_ void senf::term::LineEditor::unsetKey(keycode_t key)
519 bindings_.erase(key);
522 prefix_ bool senf::term::LineEditor::cb_init()
524 if (!BaseEditor::cb_init())
531 prefix_ void senf::term::LineEditor::cb_windowSizeChanged()
533 BaseEditor::cb_windowSizeChanged();
540 prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key)
546 KeyMap::iterator i (bindings_.find(key));
547 if (i != bindings_.end())
549 else if (key >= ' ' && key < 256)
551 if (currentLine() != 0)
553 if (redisplayNeeded_)
556 toColumn(point_ - displayPos_ + promptWidth_ + 1);
559 ///////////////////////////////////////////////////////////////////////////
561 prefix_ void senf::term::bindings::selfInsertCommand(LineEditor & editor)
563 LineEditor::keycode_t key (editor.lastKey());
564 if (key >= ' ' && key < 256)
568 prefix_ void senf::term::bindings::forwardChar(LineEditor & editor)
570 editor.gotoChar(editor.point()+1);
573 prefix_ void senf::term::bindings::backwardChar(LineEditor & editor)
575 unsigned p (editor.point());
577 editor.gotoChar(p-1);
580 prefix_ void senf::term::bindings::accept(LineEditor & editor)
585 prefix_ void senf::term::bindings::acceptWithRepeat(LineEditor & editor)
587 if (editor.text().empty()) {
588 editor.prevHistory();
589 editor.forceRedisplay();
594 prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
596 unsigned p (editor.point());
598 editor.gotoChar(p-1);
603 prefix_ void senf::term::bindings::deleteChar(LineEditor & editor)
608 prefix_ void senf::term::bindings::beginningOfLine(LineEditor & editor)
613 prefix_ void senf::term::bindings::endOfLine(LineEditor & editor)
615 editor.gotoChar(editor.text().size());
618 prefix_ void senf::term::bindings::deleteToEndOfLine(LineEditor & editor)
620 editor.deleteChar(editor.text().size()-editor.point());
623 prefix_ void senf::term::bindings::restartEdit(LineEditor & editor)
630 prefix_ void senf::term::bindings::prevHistory(LineEditor & editor)
632 editor.prevHistory();
635 prefix_ void senf::term::bindings::nextHistory(LineEditor & editor)
637 editor.nextHistory();
640 prefix_ void senf::term::bindings::clearScreen(LineEditor & editor)
642 editor.maybeClrScr();
644 editor.forceRedisplay();
647 prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer completer)
649 typedef std::vector<std::string> Completions;
651 std::string text (editor.text());
652 Completions completions;
654 unsigned e (editor.point());
656 completer(editor, b, e, prefix, completions);
657 if (completions.empty())
664 // Find common start string of all completions
665 unsigned commonStart (completions[0].size());
666 unsigned maxLen (commonStart);
667 for (Completions::const_iterator i (boost::next(completions.begin()));
668 i != completions.end(); ++i) {
669 if (i->size() > maxLen)
672 for (; n < commonStart && n < i->size() && completions[0][n] == (*i)[n]; ++n) ;
676 // Replace to-be-completed string with the common start string shared by all completions
677 std::string completion (prefix+completions[0].substr(0, commonStart));
678 bool didComplete (false);
679 if (text.substr(b, e) != completion) {
681 text.insert(b, completion);
685 // Otherwise place cursor directly after the (possibly partial) completion
686 editor.set(text, b+prefix.size()+commonStart);
687 if (didComplete || completions.size() == 1)
690 // Text was not changed, show list of possible completions
691 unsigned colWidth (maxLen+2);
692 unsigned nColumns ((editor.width()-1) / colWidth);
693 if (nColumns < 1) nColumns = 1;
694 unsigned nRows ((completions.size()+nColumns-1) / nColumns);
695 if (nRows > editor.maxAuxDisplayHeight()) {
696 editor.auxDisplay(0, "(too many completions)");
699 Completions::iterator i (completions.begin());
700 for (unsigned row (0); row < nRows; ++row) {
702 for (unsigned column (0); column < nColumns && i != completions.end(); ++column, ++i) {
703 std::string entry (colWidth, ' ');
704 std::copy(i->begin(),
705 i->size() > colWidth-2 ? i->begin()+colWidth-2 : i->end(),
709 editor.auxDisplay(row, line);
713 ///////////////////////////////cc.e////////////////////////////////////////
715 //#include "Editor.mpp"
721 // comment-column: 40
722 // c-file-style: "senf"
723 // indent-tabs-mode: nil
724 // ispell-local-dictionary: "american"
725 // compile-command: "scons -u test"