Fix build on systems that have a separate libintl library
[centerim5.git] / cppconsui / TextEdit.cpp
blob40bb06b57a78404ecafb40dfe663f6ee1b967364
1 // Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
2 //
3 // This file is part of CenterIM.
4 //
5 // CenterIM is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // CenterIM is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with CenterIM. If not, see <http://www.gnu.org/licenses/>.
18 /// @file
19 /// TextEdit class implementation
20 ///
21 /// @ingroup cppconsui
23 // Gap buffer implementation based on code from Hsin Tsao
24 // (stsao@lazyhacker.com).
26 #include "TextEdit.h"
28 #include "ColorScheme.h"
30 #include <algorithm>
31 #include <cassert>
32 #include <cstring>
34 // Gap expand size when the gap becomes filled.
35 #define GAP_SIZE_EXPAND 4096
37 namespace CppConsUI {
39 TextEdit::TextEdit(int w, int h, const char *text, int flags, bool single_line,
40 bool accept_tabs, bool masked)
41 : Widget(w, h), flags_(flags), editable_(true), overwrite_mode_(false),
42 single_line_mode_(single_line), accept_tabs_(accept_tabs), masked_(masked),
43 buffer_(nullptr), screen_lines_dirty_(false)
45 setText(text);
47 can_focus_ = true;
48 declareBindables();
51 TextEdit::~TextEdit()
53 delete[] buffer_;
56 bool TextEdit::processInputText(const TermKeyKey &key)
58 if (!editable_)
59 return false;
61 if (single_line_mode_ && key.code.codepoint == '\n')
62 return false;
64 if (!accept_tabs_ && key.code.codepoint == '\t')
65 return false;
67 // Filter out unwanted input.
68 if (flags_ != 0) {
69 if ((flags_ & FLAG_NUMERIC) && !UTF8::isUniCharDigit(key.code.codepoint))
70 return false;
71 if ((flags_ & FLAG_NOSPACE) && UTF8::isUniCharSpace(key.code.codepoint))
72 return false;
75 insertTextAtCursor(key.utf8);
76 return true;
79 int TextEdit::draw(Curses::ViewPort area, Error &error)
81 assertUpdatedScreenLines();
83 DRAW(area.erase(error));
85 int attrs;
86 DRAW(getAttributes(ColorScheme::PROPERTY_TEXTEDIT_TEXT, &attrs, error));
87 DRAW(area.attrOn(attrs, error));
89 ScreenLines::iterator i;
90 int j;
91 for (i = screen_lines_.begin() + view_top_, j = 0;
92 i != screen_lines_.end() && j < real_height_; ++i, ++j) {
93 const char *p = i->start;
94 int w = 0;
95 for (std::size_t k = 0; k < i->length && *p != '\n'; ++k) {
96 int printed;
97 if (masked_)
98 DRAW(area.addChar(w, j, '*', error, &printed));
99 else {
100 UTF8::UniChar uc = UTF8::getUniChar(p);
101 if (uc == '\t') {
102 printed = onScreenWidth(uc, w);
103 for (int l = 0; l < printed; ++l)
104 DRAW(area.addChar(w + l, j, ' ', error));
106 else
107 DRAW(area.addChar(w, j, uc, error, &printed));
109 w += printed;
110 p = nextChar(p);
114 DRAW(area.attrOff(attrs, error));
116 if (has_focus_) {
117 const char *line = screen_lines_[current_sc_line_].start;
118 int sc_x = width(line, current_sc_linepos_);
119 int sc_y = current_sc_line_ - view_top_;
120 DRAW(area.changeAt(sc_x, sc_y, 1, Curses::Attr::REVERSE, 0, error));
123 return 0;
126 void TextEdit::setText(const char *new_text)
128 if (new_text == nullptr) {
129 clear();
130 return;
133 // XXX should the text be validated (FLAG_*)?
134 std::size_t size = strlen(new_text);
135 initBuffer(size + GAP_SIZE_EXPAND);
136 insertTextAtCursor(new_text, size);
139 void TextEdit::clear()
141 initBuffer(GAP_SIZE_EXPAND);
142 redraw();
145 const char *TextEdit::getText() const
147 assert(gapend_ > gapstart_);
149 screen_lines_dirty_ = true;
151 // Move gap to the end.
152 bool point_after_gap = point_ >= gapend_;
154 // '-1' so the last '\n' is still in the end of the buffer.
155 std::memmove(gapstart_, gapend_, bufend_ - gapend_ - 1);
156 if (point_after_gap)
157 point_ -= gapend_ - gapstart_;
158 gapstart_ += bufend_ - gapend_ - 1;
159 gapend_ = bufend_ - 1;
161 *gapstart_ = '\0';
163 return buffer_;
166 void TextEdit::setFlags(int new_flags, bool revalidate)
168 if (new_flags == flags_)
169 return;
171 flags_ = new_flags;
173 if (flags_ != 0 && revalidate) {
174 bool valid = true;
175 const char *p = getTextStart();
176 while (p < bufend_ - 1) {
177 UTF8::UniChar uc = UTF8::getUniChar(p);
178 if ((flags_ & FLAG_NUMERIC) && !UTF8::isUniCharDigit(uc)) {
179 valid = false;
180 break;
182 if ((flags_ & FLAG_NOSPACE) && UTF8::isUniCharSpace(uc)) {
183 valid = false;
184 break;
186 p = nextChar(p);
188 if (!valid)
189 clear();
193 void TextEdit::setSingleLineMode(bool new_single_line_mode)
195 if (new_single_line_mode == single_line_mode_)
196 return;
198 single_line_mode_ = new_single_line_mode;
201 void TextEdit::setAcceptTabs(bool new_accept_tabs)
203 if (new_accept_tabs == accept_tabs_)
204 return;
206 accept_tabs_ = new_accept_tabs;
209 void TextEdit::setMasked(bool new_masked)
211 if (new_masked == masked_)
212 return;
214 masked_ = new_masked;
215 // In the masked mode, the tab character and wide characters lose their width
216 // property, thus screen lines and cursor have to be updated.
217 updateScreenLines();
218 updateScreenCursor();
219 redraw();
222 bool TextEdit::ScreenLine::operator==(const ScreenLine &other) const
224 return start == other.start && end == other.end && length == other.length;
227 bool TextEdit::CmpScreenLineEnd::operator()(ScreenLine &sline, const char *tag)
229 return sline.end < tag;
232 void TextEdit::updateArea()
234 // Update screen lines and cursor position if the area width changed.
235 updateScreenLines();
236 updateScreenCursor();
239 void TextEdit::initBuffer(std::size_t size)
241 assert(size > 0);
243 delete[] buffer_;
244 buffer_ = new char[size];
246 current_pos_ = 0;
247 point_ = gapstart_ = buffer_;
249 bufend_ = gapend_ = buffer_ + size;
250 gapend_ = bufend_ - 1;
251 // Insert an empty line.
252 *gapend_ = '\n';
254 text_length_ = 0;
255 current_pos_ = 0;
256 current_sc_line_ = 0;
257 current_sc_linepos_ = 0;
259 view_top_ = 0;
261 updateScreenLines();
264 std::size_t TextEdit::getGapSize() const
266 // '-1' so '\0' character can be stored at the gapstart position in the
267 // getText() method.
268 return gapend_ - gapstart_ - 1;
271 void TextEdit::expandGap(std::size_t size)
273 std::size_t gap_size = getGapSize();
274 if (size <= gap_size)
275 return;
277 size += GAP_SIZE_EXPAND - gap_size;
279 char *origbuffer = buffer_;
280 bool point_after_gap = point_ >= gapend_;
282 std::size_t alloc_size = (bufend_ - buffer_) + size;
283 buffer_ = new char[alloc_size];
284 std::memcpy(buffer_, origbuffer, alloc_size);
286 point_ = buffer_ + (point_ - origbuffer);
287 bufend_ = buffer_ + (bufend_ - origbuffer);
288 gapstart_ = buffer_ + (gapstart_ - origbuffer);
289 gapend_ = buffer_ + (gapend_ - origbuffer);
291 delete[] origbuffer;
293 std::memmove(gapend_ + size, gapend_, bufend_ - gapend_);
295 if (point_after_gap) {
296 // This should never happen because moveGapToCursor() is always called
297 // before expandGap().
298 point_ += size;
300 gapend_ += size;
301 bufend_ += size;
304 void TextEdit::moveGapToCursor()
306 if (point_ == gapstart_)
307 return;
309 if (point_ == gapend_) {
310 point_ = gapstart_;
311 return;
314 // Move gap towards the left.
315 if (point_ < gapstart_) {
316 // Move the point over by gapsize.
317 std::memmove(point_ + (gapend_ - gapstart_), point_, gapstart_ - point_);
318 gapend_ -= gapstart_ - point_;
319 gapstart_ = point_;
321 else {
322 // Since point is after the gap, find distance between gapend and point and
323 // that is how much we move from gapend to gapstart.
324 std::memmove(gapstart_, gapend_, point_ - gapend_);
325 gapstart_ += point_ - gapend_;
326 gapend_ = point_;
327 point_ = gapstart_;
331 char *TextEdit::getTextStart() const
333 if (buffer_ == gapstart_)
334 return const_cast<char *>(gapend_);
335 return const_cast<char *>(buffer_);
338 char *TextEdit::prevChar(const char *p) const
340 if (p >= gapend_) {
341 if ((p = UTF8::findPrevChar(gapend_, p)))
342 return const_cast<char *>(p);
343 else
344 p = gapstart_;
347 if ((p = UTF8::findPrevChar(buffer_, p)))
348 return const_cast<char *>(p);
349 else
350 return const_cast<char *>(buffer_);
353 char *TextEdit::nextChar(const char *p) const
355 // This happens when point_ == gapstart_.
356 if (p == gapstart_)
357 p = gapend_;
359 if (p < gapstart_) {
360 if ((p = UTF8::findNextChar(p, gapstart_)))
361 return const_cast<char *>(p);
362 else
363 return const_cast<char *>(gapend_);
366 if ((p = UTF8::findNextChar(p, bufend_)))
367 return const_cast<char *>(p);
368 else
369 return const_cast<char *>(bufend_);
372 int TextEdit::width(const char *start, std::size_t chars) const
374 assert(start != nullptr);
376 int width = 0;
378 while (chars-- > 0) {
379 UTF8::UniChar uc = UTF8::getUniChar(start);
380 width += onScreenWidth(uc, width);
381 start = nextChar(start);
383 return width;
386 int TextEdit::onScreenWidth(UTF8::UniChar uc, int w) const
388 if (masked_)
389 return 1;
390 return Curses::onScreenWidth(uc, w);
393 char *TextEdit::getScreenLine(
394 const char *text, int max_width, std::size_t *res_length) const
396 assert(text != nullptr);
397 assert(text < bufend_);
398 assert(max_width > 0);
399 assert(res_length != nullptr);
401 const char *cur = text;
402 const char *res = text;
403 int prev_width = 0;
404 int cur_width = 0;
405 std::size_t cur_length = 0;
406 bool space = false;
407 *res_length = 0;
409 while (cur < bufend_) {
410 prev_width = cur_width;
411 UTF8::UniChar uc = UTF8::getUniChar(cur);
412 cur_width += onScreenWidth(uc, cur_width);
413 ++cur_length;
415 if (prev_width > max_width)
416 break;
418 // Possibly too long word.
419 if (cur_width > max_width && !*res_length) {
420 *res_length = cur_length - 1;
421 res = cur;
424 // End of line (paragraph on screen) found.
425 if (*cur == '\n') {
426 *res_length = cur_length;
427 return nextChar(cur);
430 if (UTF8::isUniCharSpace(uc))
431 space = true;
432 else if (space) {
433 // Found start of a word and everything before that can fit into one
434 // screen line.
435 *res_length = cur_length - 1;
436 res = cur;
437 space = false;
440 cur = nextChar(cur);
443 // Fix for very small max_width and characters wider that 1 cell. For example,
444 // max_width = 1 and text = "W" where W is a wide character (2 cells width)
445 // (or simply for tabs). In that case we can not draw anything but we want to
446 // skip to another character.
447 if (res == text) {
448 *res_length = 1;
449 res = nextChar(res);
452 return const_cast<char *>(res);
455 void TextEdit::updateScreenLines()
457 screen_lines_.clear();
459 if (real_width_ <= 1)
460 return;
462 const char *p = getTextStart();
464 while (p < bufend_) {
465 const char *s = p;
466 std::size_t length;
467 // Lower max width by one to make a room for the cursor.
468 p = getScreenLine(p, real_width_ - 1, &length);
469 screen_lines_.push_back(ScreenLine(s, p, length));
473 void TextEdit::updateScreenLines(const char *begin, const char *end)
475 assert(begin != nullptr);
476 assert(end != nullptr);
478 if (real_width_ <= 1)
479 return;
481 ScreenLines::iterator b, i;
482 b = std::lower_bound(screen_lines_.begin(), screen_lines_.end(), begin,
483 TextEdit::CmpScreenLineEnd());
484 if (b != screen_lines_.begin()) {
485 // Initial Correct final
486 // situation situation
487 // --------- ---------
488 // |aaaa | -> |aaaa b |
489 // |bcdddd | |cdddd |
491 // User inserts a space in front of the 'c' character. The 'b' string can be
492 // moved on the previous line thus one more extra line before has to be
493 // recalculated to handle the situation correctly.
494 --b;
496 i = b;
498 ScreenLines new_screen_lines;
500 const char *p = b->start;
501 if (i == screen_lines_.begin())
502 p = getTextStart();
504 while (p < bufend_) {
505 const char *s = p;
506 std::size_t length;
507 // Lower max width by one to make a room for the cursor.
508 p = getScreenLine(p, real_width_ - 1, &length);
509 ScreenLine sline(s, p, length);
510 new_screen_lines.push_back(sline);
511 while (
512 i != screen_lines_.end() && (i->end <= end || i->start < s || i->end < p))
513 ++i;
514 if (i != screen_lines_.end() && sline == *i) {
515 // Screen lines are same thus it is not necessary to recalculate more
516 // of them.
517 break;
520 if (i != screen_lines_.end())
521 ++i;
523 // Replace old screen lines with new screen lines.
524 ScreenLines::iterator j;
525 for (j = new_screen_lines.begin(); j != new_screen_lines.end() && b != i;
526 ++j, ++b)
527 *b = *j;
529 if (j != new_screen_lines.end()) {
530 // b == i.
531 screen_lines_.insert(b, j, new_screen_lines.end());
533 else {
534 // b != i.
535 screen_lines_.erase(b, i);
539 void TextEdit::assertUpdatedScreenLines()
541 if (!screen_lines_dirty_)
542 return;
544 updateScreenLines();
545 screen_lines_dirty_ = false;
548 void TextEdit::updateScreenCursor()
550 std::size_t acu_length = 0;
551 current_sc_line_ = 0;
552 current_sc_linepos_ = 0;
554 assertUpdatedScreenLines();
556 for (ScreenLine &line : screen_lines_) {
557 std::size_t length = line.length;
558 if (acu_length <= current_pos_ && current_pos_ < acu_length + length) {
559 current_sc_linepos_ = current_pos_ - acu_length;
560 break;
562 ++current_sc_line_;
563 acu_length += length;
566 // Fix cursor visibility.
567 if (view_top_ <= current_sc_line_ &&
568 current_sc_line_ < view_top_ + real_height_)
569 return;
570 while (view_top_ > current_sc_line_)
571 --view_top_;
572 while (view_top_ + real_height_ <= current_sc_line_)
573 ++view_top_;
576 void TextEdit::insertTextAtCursor(
577 const char *new_text, std::size_t new_text_bytes)
579 assert(new_text != nullptr);
581 assertUpdatedScreenLines();
583 // Move the gap if the point is not already at the start of the gap.
584 char *min = gapstart_;
585 char *max = gapend_;
586 moveGapToCursor();
587 char *min2 = gapstart_;
589 // Make sure that the gap has enough room.
590 bool full_screen_lines_update = false;
591 if (new_text_bytes > getGapSize()) {
592 expandGap(new_text_bytes);
593 full_screen_lines_update = true;
596 std::size_t n_chars = 0;
597 const char *p = new_text;
598 while (p != nullptr && *p != '\0') {
599 ++n_chars;
600 p = UTF8::findNextChar(p, new_text + new_text_bytes);
602 text_length_ += n_chars;
603 current_pos_ += n_chars;
605 while (new_text_bytes) {
606 *gapstart_++ = *new_text++;
607 --new_text_bytes;
609 point_ = gapstart_;
611 if (full_screen_lines_update)
612 updateScreenLines();
613 else
614 updateScreenLines(std::min(min, min2), std::max(max, gapend_));
615 updateScreenCursor();
616 redraw();
618 signal_text_change(*this);
621 void TextEdit::insertTextAtCursor(const char *new_text)
623 assert(new_text != nullptr);
625 insertTextAtCursor(new_text, strlen(new_text));
628 void TextEdit::deleteFromCursor(DeleteType type, Direction dir)
630 if (!editable_)
631 return;
633 assertUpdatedScreenLines();
635 int count = 0;
637 switch (type) {
638 case DELETE_CHARS:
639 count = moveLogicallyFromCursor(dir) - current_pos_;
640 break;
641 case DELETE_WORD_ENDS:
642 count = moveWordFromCursor(dir, true) - current_pos_;
643 break;
644 default:
645 assert(0);
648 if (count != 0) {
649 char *min = gapstart_;
650 char *max = gapend_;
651 moveGapToCursor();
653 while (count > 0) {
654 gapend_ = nextChar(gapend_);
655 --text_length_;
656 --count;
659 while (count < 0) {
660 gapstart_ = prevChar(gapstart_);
661 --current_pos_;
662 --text_length_;
663 ++count;
665 point_ = gapstart_;
667 updateScreenLines(std::min(min, gapstart_), std::max(max, gapend_));
668 updateScreenCursor();
669 redraw();
671 signal_text_change(*this);
675 void TextEdit::moveCursor(CursorMovement step, Direction dir)
677 assertUpdatedScreenLines();
679 std::size_t old_pos = current_pos_;
680 switch (step) {
681 case MOVE_LOGICAL_POSITIONS:
682 current_pos_ = moveLogicallyFromCursor(dir);
683 break;
684 case MOVE_WORDS:
685 current_pos_ = moveWordFromCursor(dir, false);
686 break;
687 case MOVE_DISPLAY_LINES:
688 if (dir == DIR_FORWARD) {
689 if (current_sc_line_ + 1 < screen_lines_.size()) {
690 int oldw =
691 width(screen_lines_[current_sc_line_].start, current_sc_linepos_);
692 // First move to end of current line.
693 current_pos_ +=
694 screen_lines_[current_sc_line_].length - current_sc_linepos_;
695 // Find a character close to the original position.
696 const char *ch = screen_lines_[current_sc_line_ + 1].start;
697 std::size_t i = 0;
698 int w = 0;
699 while (w < oldw && i < screen_lines_[current_sc_line_ + 1].length - 1) {
700 UTF8::UniChar uc = UTF8::getUniChar(ch);
701 w += onScreenWidth(uc, w);
702 ch = nextChar(ch);
703 ++i;
705 current_pos_ += i;
708 else { // DIR_BACK
709 if (current_sc_line_ > 0) {
710 int oldw =
711 width(screen_lines_[current_sc_line_].start, current_sc_linepos_);
712 // First move to start of current line.
713 current_pos_ -= current_sc_linepos_;
714 // Move to the start of the previous line.
715 current_pos_ -= screen_lines_[current_sc_line_ - 1].length;
716 // Find a character close to the original position.
717 const char *ch = screen_lines_[current_sc_line_ - 1].start;
718 std::size_t i = 0;
719 int w = 0;
720 while (w < oldw && i < screen_lines_[current_sc_line_ - 1].length - 1) {
721 UTF8::UniChar uc = UTF8::getUniChar(ch);
722 w += onScreenWidth(uc, w);
723 ch = nextChar(ch);
724 ++i;
726 current_pos_ += i;
729 break;
730 case MOVE_DISPLAY_LINE_ENDS:
731 if (dir == DIR_FORWARD)
732 current_pos_ +=
733 screen_lines_[current_sc_line_].length - current_sc_linepos_ - 1;
734 else // DIR_BACK
735 current_pos_ -= current_sc_linepos_;
736 break;
737 default:
738 assert(0);
741 // Update point_.
742 while (old_pos > current_pos_) {
743 point_ = prevChar(point_);
744 --old_pos;
746 while (old_pos < current_pos_) {
747 point_ = nextChar(point_);
748 ++old_pos;
751 updateScreenCursor();
752 redraw();
755 void TextEdit::toggleOverwrite()
757 overwrite_mode_ = !overwrite_mode_;
760 std::size_t TextEdit::moveLogicallyFromCursor(Direction dir) const
762 if (dir == DIR_FORWARD && current_pos_ < text_length_)
763 return current_pos_ + 1;
764 else if (dir == DIR_BACK && current_pos_ > 0)
765 return current_pos_ - 1;
766 return current_pos_;
769 std::size_t TextEdit::moveWordFromCursor(Direction dir, bool word_end) const
771 std::size_t new_pos = current_pos_;
772 const char *cur = point_;
773 if (cur == gapstart_)
774 cur = gapend_;
776 if (dir == DIR_FORWARD) {
777 if (word_end) {
778 // Search for the first white character after non-white characters.
779 bool nonwhite = false;
780 while (new_pos < text_length_) {
781 if (!UTF8::isUniCharSpace(UTF8::getUniChar(cur)) && *cur != '\n')
782 nonwhite = true;
783 else if (nonwhite)
784 break;
785 cur = nextChar(cur);
786 ++new_pos;
788 return new_pos;
790 else {
791 // Search for the first nonwhite character after white characters.
792 bool white = false;
793 while (new_pos < text_length_) {
794 if (UTF8::isUniCharSpace(UTF8::getUniChar(cur)) || *cur == '\n')
795 white = true;
796 else if (white)
797 break;
798 cur = nextChar(cur);
799 ++new_pos;
801 return new_pos;
804 else { // DIR_BACK
805 if (new_pos == 0)
806 return 0;
808 // Always move at least one character back.
809 cur = prevChar(cur);
810 --new_pos;
812 // Search for the first white character before nonwhite characters.
813 bool nonwhite = false;
814 while (new_pos != static_cast<std::size_t>(-1)) {
815 if (!UTF8::isUniCharSpace(UTF8::getUniChar(cur)) && *cur != '\n')
816 nonwhite = true;
817 else if (nonwhite)
818 break;
819 if (new_pos > 0)
820 cur = prevChar(cur);
821 --new_pos;
823 return ++new_pos;
827 void TextEdit::actionMoveCursor(CursorMovement step, Direction dir)
829 moveCursor(step, dir);
832 void TextEdit::actionDelete(DeleteType type, Direction dir)
834 deleteFromCursor(type, dir);
837 void TextEdit::actionToggleOverwrite()
839 toggleOverwrite();
842 void TextEdit::declareBindables()
844 // Cursor movement.
845 declareBindable("textentry", "cursor-right",
846 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
847 MOVE_LOGICAL_POSITIONS, DIR_FORWARD),
848 InputProcessor::BINDABLE_NORMAL);
850 declareBindable("textentry", "cursor-left",
851 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
852 MOVE_LOGICAL_POSITIONS, DIR_BACK),
853 InputProcessor::BINDABLE_NORMAL);
855 declareBindable("textentry", "cursor-down",
856 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
857 MOVE_DISPLAY_LINES, DIR_FORWARD),
858 InputProcessor::BINDABLE_NORMAL);
860 declareBindable("textentry", "cursor-up",
861 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
862 MOVE_DISPLAY_LINES, DIR_BACK),
863 InputProcessor::BINDABLE_NORMAL);
865 declareBindable("textentry", "cursor-right-word",
866 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor), MOVE_WORDS,
867 DIR_FORWARD),
868 InputProcessor::BINDABLE_NORMAL);
870 declareBindable("textentry", "cursor-left-word",
871 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor), MOVE_WORDS,
872 DIR_BACK),
873 InputProcessor::BINDABLE_NORMAL);
875 declareBindable("textentry", "cursor-end",
876 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
877 MOVE_DISPLAY_LINE_ENDS, DIR_FORWARD),
878 InputProcessor::BINDABLE_NORMAL);
880 declareBindable("textentry", "cursor-begin",
881 sigc::bind(sigc::mem_fun(this, &TextEdit::actionMoveCursor),
882 MOVE_DISPLAY_LINE_ENDS, DIR_BACK),
883 InputProcessor::BINDABLE_NORMAL);
885 // Deleting text.
886 declareBindable("textentry", "delete-char",
887 sigc::bind(sigc::mem_fun(this, &TextEdit::actionDelete), DELETE_CHARS,
888 DIR_FORWARD),
889 InputProcessor::BINDABLE_NORMAL);
891 declareBindable("textentry", "backspace",
892 sigc::bind(sigc::mem_fun(this, &TextEdit::actionDelete), DELETE_CHARS,
893 DIR_BACK),
894 InputProcessor::BINDABLE_NORMAL);
896 declareBindable("textentry", "delete-word-end",
897 sigc::bind(sigc::mem_fun(this, &TextEdit::actionDelete), DELETE_WORD_ENDS,
898 DIR_FORWARD),
899 InputProcessor::BINDABLE_NORMAL);
901 declareBindable("textentry", "delete-word-begin",
902 sigc::bind(sigc::mem_fun(this, &TextEdit::actionDelete), DELETE_WORD_ENDS,
903 DIR_BACK),
904 InputProcessor::BINDABLE_NORMAL);
906 declareBindable("textentry", "newline",
907 sigc::bind(sigc::mem_fun(
908 this, static_cast<void (TextEdit::*)(const char *)>(
909 &TextEdit::insertTextAtCursor)),
910 "\n"),
911 InputProcessor::BINDABLE_NORMAL);
914 // Overwrite.
915 declareBindable("textentry", "toggle-overwrite", sigc::mem_fun(this,
916 &TextEdit::actionToggleOverwrite), InputProcessor::BINDABLE_NORMAL);
920 } // namespace CppConsUI
922 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: