4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at
9 // http://senf.berlios.de/license.html
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on,
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
15 // Software distributed under the License is distributed on an "AS IS" basis,
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17 // for the specific language governing rights and limitations under the License.
19 // The Original Code is Fraunhofer FOKUS code.
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V.
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
26 // Stefan Bund <g0dil@berlios.de>
29 \brief Editor non-inline non-template implementation */
32 //#include "Editor.ih"
35 #include <senf/Utils/membind.hh>
36 #include <senf/Scheduler/Scheduler.hh>
38 //#include "Editor.mpp"
40 //-/////////////////////////////////////////////////////////////////////////////////////////////////
42 prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal)
43 : terminal_ (&terminal),
44 keyTimeout_ (ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)),
45 timer_ ("senf::term::BaseEditor::keySequenceTimeout",
46 membind(&BaseEditor::keySequenceTimeout, this)),
47 column_ (0u), displayHeight_ (1u), line_ (0u)
49 terminal_->setCallbacks(*this);
52 prefix_ void senf::term::BaseEditor::newline()
56 write(tifo_.getString(Terminfo::properties::ClrEol));
60 prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
65 if (tifo_.hasProperty(Terminfo::properties::ParmRightCursor)) {
66 write(tifo_.formatString(Terminfo::properties::ParmRightCursor, c - column_));
70 char const * cuf1 (tifo_.getString(Terminfo::properties::CursorRight));
77 else if (c < column_) {
78 if (tifo_.hasProperty(Terminfo::properties::ParmLeftCursor)) {
79 write(tifo_.formatString(Terminfo::properties::ParmLeftCursor, column_ - c));
83 char const * cub1 (tifo_.getString(Terminfo::properties::CursorLeft));
92 prefix_ void senf::term::BaseEditor::put(char ch)
94 if (column_ >= width()-1)
100 prefix_ void senf::term::BaseEditor::put(std::string const & text)
102 if (text.size() > width()-column_-1) {
103 write(text.substr(0,width()-column_-1));
104 column_ = width() - 1;
108 column_ += text.size();
112 prefix_ void senf::term::BaseEditor::clearLine()
115 write(tifo_.getString(Terminfo::properties::ClrEol));
119 prefix_ void senf::term::BaseEditor::setBold()
121 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
122 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
123 write(tifo_.getString(Terminfo::properties::EnterBoldMode));
126 prefix_ void senf::term::BaseEditor::setNormal()
128 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
129 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
130 write(tifo_.getString(Terminfo::properties::ExitAttributeMode));
133 prefix_ void senf::term::BaseEditor::maybeClrScr()
135 if (tifo_.hasProperty(Terminfo::properties::ClearScreen))
136 write(tifo_.getString(Terminfo::properties::ClearScreen));
139 prefix_ void senf::term::BaseEditor::toLine(unsigned l)
144 if (ll >= displayHeight_)
145 ll = displayHeight_-1;
147 if (tifo_.hasProperty(Terminfo::properties::ParmDownCursor)) {
148 write(tifo_.formatString(Terminfo::properties::ParmDownCursor, ll - line_));
152 char const * cud1 (tifo_.getString(Terminfo::properties::CursorDown));
159 else if (ll < line_) {
160 if (tifo_.hasProperty(Terminfo::properties::ParmUpCursor)) {
161 write(tifo_.formatString(Terminfo::properties::ParmUpCursor, line_ - ll));
165 char const * cuu1 (tifo_.getString(Terminfo::properties::CursorUp));
174 write(tifo_.getString(Terminfo::properties::ClrEol));
182 prefix_ void senf::term::BaseEditor::reset()
184 for (unsigned i (1); i < displayHeight_; ++i) {
192 prefix_ unsigned senf::term::BaseEditor::currentColumn()
198 prefix_ unsigned senf::term::BaseEditor::currentLine()
204 prefix_ bool senf::term::BaseEditor::cb_init()
207 tifo_.load(terminal_->terminalType());
208 keyParser_.load(tifo_);
210 catch (Terminfo::InvalidTerminfoException & ex) {
214 typedef Terminfo::properties p;
215 if (! (tifo_.hasProperty(p::ClrEol) &&
216 (tifo_.hasProperty(p::ParmRightCursor) || tifo_.hasProperty(p::CursorRight)) &&
217 (tifo_.hasProperty(p::ParmLeftCursor) || tifo_.hasProperty(p::CursorLeft))))
220 if (tifo_.hasProperty(Terminfo::properties::KeypadXmit))
221 write(tifo_.getString(Terminfo::properties::KeypadXmit));
225 prefix_ void senf::term::BaseEditor::cb_charReceived(char c)
228 timer_.timeout(scheduler::eventTime() + keyTimeout_);
232 prefix_ void senf::term::BaseEditor::cb_windowSizeChanged()
234 if (column_ >= width())
238 prefix_ void senf::term::BaseEditor::keySequenceTimeout()
240 while (!inputBuffer_.empty()) {
242 v_keyReceived(keycode_t(inputBuffer_[0]));
243 inputBuffer_.erase(0, 1);
247 prefix_ void senf::term::BaseEditor::processKeys()
250 std::pair<KeyParser::keycode_t, std::string::size_type> result
251 (keyParser_.lookup(inputBuffer_));
252 if (result.first == KeyParser::Incomplete)
254 v_keyReceived(result.first);
255 inputBuffer_.erase(0, result.second);
256 } while (! inputBuffer_.empty());
260 prefix_ unsigned senf::term::BaseEditor::width()
263 return terminal_->width();
266 prefix_ unsigned senf::term::BaseEditor::height()
269 return terminal_->height();
272 prefix_ void senf::term::BaseEditor::write(char ch)
274 terminal_->write(ch);
277 prefix_ void senf::term::BaseEditor::write(std::string const & s)
279 for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
283 //-/////////////////////////////////////////////////////////////////////////////////////////////////
285 prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb)
286 : BaseEditor(terminal), enabled_ (false), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u),
287 text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u)
289 defineKey(KeyParser::Return, &bindings::accept);
290 defineKey(KeyParser::Right, &bindings::forwardChar);
291 defineKey(KeyParser::Left, &bindings::backwardChar);
292 defineKey(KeyParser::Up, &bindings::prevHistory);
293 defineKey(KeyParser::Down, &bindings::nextHistory);
294 defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar);
295 defineKey(KeyParser::Delete, &bindings::deleteChar);
296 defineKey(KeyParser::Home, &bindings::beginningOfLine);
297 defineKey(KeyParser::End, &bindings::endOfLine);
298 defineKey(KeyParser::Ctrl('K'), &bindings::deleteToEndOfLine);
299 defineKey(KeyParser::Ctrl('A'), &bindings::beginningOfLine);
300 defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine);
301 defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar);
302 defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit);
303 defineKey(KeyParser::Ctrl('L'), &bindings::clearScreen);
306 prefix_ void senf::term::LineEditor::prompt(std::string const & text)
309 promptWidth_ = prompt_.size();
310 if (promptWidth_ > width() - 4 && width() > 4)
311 promptWidth_ = width() - 4;
312 editWidth_ = width() - promptWidth_ - 3;
317 prefix_ void senf::term::LineEditor::set(std::string const & text, unsigned pos)
321 if (point_ > text.size())
322 point_ = text.size();
324 if (point_ > editWidth_)
325 displayPos_ = point_ - editWidth_;
329 prefix_ void senf::term::LineEditor::show()
334 for (unsigned n (0); n < auxDisplay_.size(); ++n) {
342 prefix_ void senf::term::LineEditor::hide()
351 prefix_ void senf::term::LineEditor::accept()
356 pushHistory(text_, true);
361 prefix_ void senf::term::LineEditor::clear()
364 historyPoint_ = history_.size();
367 prefix_ void senf::term::LineEditor::redisplay()
369 redisplayNeeded_ = true;
372 prefix_ void senf::term::LineEditor::forceRedisplay()
378 if (prompt_.size() > promptWidth_)
379 put(prompt_.substr(prompt_.size()-promptWidth_));
382 put( displayPos_ > 0 ? '<' : ' ' );
383 if (text_.size() > displayPos_ + editWidth_) {
384 toColumn(editWidth_ + promptWidth_ + 1);
386 toColumn(promptWidth_ + 1);
389 put(text_.substr(displayPos_, editWidth_));
390 toColumn(point_ - displayPos_ + promptWidth_ + 1);
391 redisplayNeeded_ = false;
394 prefix_ void senf::term::LineEditor::gotoChar(unsigned n)
397 if (point_ > text_.size())
398 point_ = text_.size();
399 if (point_ < displayPos_)
400 displayPos_ = point_;
401 if (point_ > displayPos_+editWidth_)
402 displayPos_ = point_-editWidth_;
406 prefix_ void senf::term::LineEditor::scrollTo(unsigned n)
409 if (displayPos_ > text_.size())
410 displayPos_ = text_.size();
411 if (point_ < displayPos_)
412 point_ = displayPos_;
413 if (point_ > displayPos_+editWidth_)
414 point_ = displayPos_+editWidth_;
418 prefix_ void senf::term::LineEditor::deleteChar(unsigned n)
420 if (point_ >= text_.size())
422 text_.erase(point_, n);
426 prefix_ void senf::term::LineEditor::insert(char ch)
428 text_.insert(point_, std::string(1, ch));
433 prefix_ void senf::term::LineEditor::insert(std::string const & text)
435 text_.insert(point_, text);
436 gotoChar(point_+text.size());
440 prefix_ void senf::term::LineEditor::pushHistory(std::string const & text, bool accept)
443 && (accept || historyPoint_ == history_.size() || history_[historyPoint_] != text)
444 && (history_.empty() || history_.back() != text)) {
445 history_.push_back(text);
446 while (history_.size() > MAX_HISTORY_SIZE)
447 history_.erase(history_.begin());
449 historyPoint_ = history_.size() - 1;
453 prefix_ void senf::term::LineEditor::prevHistory()
455 if (historyPoint_ <= 0)
458 std::string entry (history_[--historyPoint_]);
459 set(entry, entry.size());
462 prefix_ void senf::term::LineEditor::nextHistory()
464 if (historyPoint_ >= history_.size())
468 if (historyPoint_ >= history_.size())
471 std::string entry (history_[historyPoint_]);
472 set(entry, entry.size());
476 prefix_ void senf::term::LineEditor::auxDisplay(unsigned line, std::string const & text)
481 while (auxDisplay_.size() < line+1)
482 auxDisplay_.push_back("");
483 auxDisplay_[line] = text;
486 prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight()
491 prefix_ void senf::term::LineEditor::clearAuxDisplay()
497 prefix_ std::string const & senf::term::LineEditor::text()
502 prefix_ unsigned senf::term::LineEditor::point()
507 prefix_ unsigned senf::term::LineEditor::displayPos()
512 prefix_ senf::term::LineEditor::keycode_t senf::term::LineEditor::lastKey()
517 prefix_ void senf::term::LineEditor::defineKey(keycode_t key, KeyBinding binding)
519 bindings_[key] = binding;
522 prefix_ void senf::term::LineEditor::unsetKey(keycode_t key)
524 bindings_.erase(key);
527 prefix_ bool senf::term::LineEditor::cb_init()
529 if (!BaseEditor::cb_init())
536 prefix_ void senf::term::LineEditor::cb_windowSizeChanged()
538 BaseEditor::cb_windowSizeChanged();
545 prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key)
551 KeyMap::iterator i (bindings_.find(key));
552 if (i != bindings_.end())
554 else if (key >= ' ' && key < 256)
556 if (currentLine() != 0)
558 if (redisplayNeeded_)
561 toColumn(point_ - displayPos_ + promptWidth_ + 1);
564 //-/////////////////////////////////////////////////////////////////////////////////////////////////
566 prefix_ void senf::term::bindings::selfInsertCommand(LineEditor & editor)
568 LineEditor::keycode_t key (editor.lastKey());
569 if (key >= ' ' && key < 256)
573 prefix_ void senf::term::bindings::forwardChar(LineEditor & editor)
575 editor.gotoChar(editor.point()+1);
578 prefix_ void senf::term::bindings::backwardChar(LineEditor & editor)
580 unsigned p (editor.point());
582 editor.gotoChar(p-1);
585 prefix_ void senf::term::bindings::accept(LineEditor & editor)
590 prefix_ void senf::term::bindings::acceptWithRepeat(LineEditor & editor)
592 if (editor.text().empty()) {
593 editor.prevHistory();
594 editor.forceRedisplay();
599 prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
601 unsigned p (editor.point());
603 editor.gotoChar(p-1);
608 prefix_ void senf::term::bindings::deleteChar(LineEditor & editor)
613 prefix_ void senf::term::bindings::beginningOfLine(LineEditor & editor)
618 prefix_ void senf::term::bindings::endOfLine(LineEditor & editor)
620 editor.gotoChar(editor.text().size());
623 prefix_ void senf::term::bindings::deleteToEndOfLine(LineEditor & editor)
625 editor.deleteChar(editor.text().size()-editor.point());
628 prefix_ void senf::term::bindings::restartEdit(LineEditor & editor)
635 prefix_ void senf::term::bindings::prevHistory(LineEditor & editor)
637 editor.prevHistory();
640 prefix_ void senf::term::bindings::nextHistory(LineEditor & editor)
642 editor.nextHistory();
645 prefix_ void senf::term::bindings::clearScreen(LineEditor & editor)
647 editor.maybeClrScr();
649 editor.forceRedisplay();
652 prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer completer)
654 typedef std::vector<std::string> Completions;
656 std::string text (editor.text());
657 Completions completions;
659 unsigned e (editor.point());
661 completer(editor, b, e, prefix, completions);
662 if (completions.empty())
669 // Find common start string of all completions
670 unsigned commonStart (completions[0].size());
671 unsigned maxLen (commonStart);
672 for (Completions::const_iterator i (boost::next(completions.begin()));
673 i != completions.end(); ++i) {
674 if (i->size() > maxLen)
677 for (; n < commonStart && n < i->size() && completions[0][n] == (*i)[n]; ++n) ;
681 // Replace to-be-completed string with the common start string shared by all completions
682 std::string completion (prefix+completions[0].substr(0, commonStart));
683 bool didComplete (false);
684 if (text.substr(b, e) != completion) {
686 text.insert(b, completion);
690 // Otherwise place cursor directly after the (possibly partial) completion
691 editor.set(text, b+prefix.size()+commonStart);
692 if (didComplete || completions.size() == 1)
695 // Text was not changed, show list of possible completions
696 unsigned colWidth (maxLen+2);
697 unsigned nColumns ((editor.width()-1) / colWidth);
698 if (nColumns < 1) nColumns = 1;
699 unsigned nRows ((completions.size()+nColumns-1) / nColumns);
700 if (nRows > editor.maxAuxDisplayHeight()) {
701 editor.auxDisplay(0, "(too many completions)");
704 Completions::iterator i (completions.begin());
705 for (unsigned row (0); row < nRows; ++row) {
707 for (unsigned column (0); column < nColumns && i != completions.end(); ++column, ++i) {
708 std::string entry (colWidth, ' ');
709 std::copy(i->begin(),
710 i->size() > colWidth-2 ? i->begin()+colWidth-2 : i->end(),
714 editor.auxDisplay(row, line);
718 //-/////////////////////////////////////////////////////////////////////////////////////////////////
720 //#include "Editor.mpp"
726 // comment-column: 40
727 // c-file-style: "senf"
728 // indent-tabs-mode: nil
729 // ispell-local-dictionary: "american"
730 // compile-command: "scons -u test"