aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/TextBox.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/FbTk/TextBox.cc')
-rw-r--r--src/FbTk/TextBox.cc118
1 files changed, 96 insertions, 22 deletions
diff --git a/src/FbTk/TextBox.cc b/src/FbTk/TextBox.cc
index 8c0af87..bf46ae3 100644
--- a/src/FbTk/TextBox.cc
+++ b/src/FbTk/TextBox.cc
@@ -43,18 +43,22 @@
43#include <X11/keysym.h> 43#include <X11/keysym.h>
44#include <X11/Xutil.h> 44#include <X11/Xutil.h>
45 45
46#include <iostream>
47
46namespace FbTk { 48namespace FbTk {
47 49
50
48TextBox::TextBox(int screen_num, 51TextBox::TextBox(int screen_num,
49 const Font &font, const std::string &text): 52 const Font &font, const std::string &text):
50 FbWindow(screen_num, 0, 0, 1, 1, ExposureMask | KeyPressMask | ButtonPressMask), 53 FbWindow(screen_num, 0, 0, 1, 1, ExposureMask | KeyPressMask | ButtonPressMask | FocusChangeMask | KeymapStateMask),
51 m_font(&font), 54 m_font(&font),
52 m_text(text), 55 m_text(text),
53 m_gc(0), 56 m_gc(0),
54 m_cursor_pos(0), 57 m_cursor_pos(0),
55 m_start_pos(0), 58 m_start_pos(0),
56 m_end_pos(0), 59 m_end_pos(0),
57 m_select_pos(std::string::npos) { 60 m_select_pos(std::string::npos),
61 m_xic(0) {
58 62
59 FbTk::EventManager::instance()->add(*this, *this); 63 FbTk::EventManager::instance()->add(*this, *this);
60} 64}
@@ -68,7 +72,8 @@ TextBox::TextBox(const FbWindow &parent,
68 m_cursor_pos(0), 72 m_cursor_pos(0),
69 m_start_pos(0), 73 m_start_pos(0),
70 m_end_pos(0), 74 m_end_pos(0),
71 m_select_pos(std::string::npos) { 75 m_select_pos(std::string::npos),
76 m_xic(0) {
72 77
73 FbTk::EventManager::instance()->add(*this, *this); 78 FbTk::EventManager::instance()->add(*this, *this);
74} 79}
@@ -111,36 +116,43 @@ void TextBox::cursorEnd() {
111} 116}
112 117
113void TextBox::cursorForward() { 118void TextBox::cursorForward() {
114 if (m_start_pos + cursorPosition() < m_end_pos) 119 StringRange r = charRange(m_start_pos + cursorPosition());
115 m_cursor_pos++; 120 const std::string::size_type s = r.end - r.begin + 1;
121 if (r.end < m_end_pos)
122 m_cursor_pos = r.end + 1 - m_start_pos;
116 else if (m_end_pos < text().size()) { 123 else if (m_end_pos < text().size()) {
117 m_cursor_pos++; 124 m_cursor_pos = r.end + 1 - m_start_pos;
118 m_end_pos++; 125 m_end_pos += s;
119 adjustStartPos(); 126 adjustStartPos();
120 } 127 }
121} 128}
122 129
123void TextBox::cursorBackward() { 130void TextBox::cursorBackward() {
124 if (cursorPosition()) 131 if (m_start_pos || cursorPosition()) {
125 m_cursor_pos--; 132 StringRange r = charRange(m_start_pos + cursorPosition() - 1);
126 else if (m_start_pos) { 133 const std::string::size_type s = r.end - r.begin + 1;
127 m_start_pos--; 134 if (cursorPosition())
128 adjustEndPos(); 135 m_cursor_pos = r.begin - m_start_pos;
136 else if (m_start_pos) {
137 m_start_pos -= s;
138 adjustEndPos();
139 }
129 } 140 }
130} 141}
131 142
132void TextBox::backspace() { 143void TextBox::backspace() {
133 if (hasSelection()) 144 if (hasSelection())
134 return deleteForward(); 145 return deleteForward();
135
136 if (m_start_pos || cursorPosition()) { 146 if (m_start_pos || cursorPosition()) {
137 FbString t = text(); 147 FbString t = text();
138 t.erase(m_start_pos + cursorPosition() - 1, 1); 148 StringRange r = charRange(m_start_pos + cursorPosition() - 1);
149 const std::string::size_type s = r.end - r.begin + 1;
150 t.erase(r.begin, s);
139 m_text.setLogical(t); 151 m_text.setLogical(t);
140 if (cursorPosition()) 152 if (cursorPosition())
141 setCursorPosition(cursorPosition() - 1); 153 setCursorPosition(r.begin - m_start_pos);
142 else 154 else
143 m_start_pos--; 155 m_start_pos -= s;
144 adjustEndPos(); 156 adjustEndPos();
145 } 157 }
146} 158}
@@ -148,10 +160,15 @@ void TextBox::backspace() {
148void TextBox::deleteForward() { 160void TextBox::deleteForward() {
149 std::string::size_type pos = m_start_pos + m_cursor_pos; 161 std::string::size_type pos = m_start_pos + m_cursor_pos;
150 int length = 1; 162 int length = 1;
151 if (hasSelection()) { 163 bool selected = false;
164 if (selected = hasSelection()) {
152 pos = std::min(m_start_pos + m_cursor_pos, m_select_pos); 165 pos = std::min(m_start_pos + m_cursor_pos, m_select_pos);
153 length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - pos; 166 length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - pos;
154 m_cursor_pos = pos - m_start_pos; 167 m_cursor_pos = pos - m_start_pos;
168 } else {
169 StringRange r = charRange(pos);
170 pos = r.begin;
171 length = r.end - r.begin + 1;
155 } 172 }
156 if (pos < m_end_pos) { 173 if (pos < m_end_pos) {
157 FbString t = text(); 174 FbString t = text();
@@ -159,7 +176,7 @@ void TextBox::deleteForward() {
159 m_text.setLogical(t); 176 m_text.setLogical(t);
160 adjustEndPos(); 177 adjustEndPos();
161 } 178 }
162 if (length > 1) 179 if (selected && length > 1)
163 adjustStartPos(); 180 adjustStartPos();
164} 181}
165 182
@@ -168,7 +185,8 @@ void TextBox::insertText(const std::string &val) {
168 deleteForward(); 185 deleteForward();
169 186
170 FbString t = text(); 187 FbString t = text();
171 t.insert(m_start_pos + cursorPosition(), val); 188 std::string::size_type pos = m_start_pos + m_cursor_pos;
189 t.insert(pos ? charRange(pos - 1).end + 1 : pos, val);
172 m_text.setLogical(t); 190 m_text.setLogical(t);
173 m_cursor_pos += val.size(); 191 m_cursor_pos += val.size();
174 m_end_pos += val.size(); 192 m_end_pos += val.size();
@@ -267,12 +285,17 @@ void TextBox::buttonPressEvent(XButtonEvent &event) {
267} 285}
268 286
269void TextBox::keyPressEvent(XKeyEvent &event) { 287void TextBox::keyPressEvent(XKeyEvent &event) {
270 288
271 event.state = KeyUtil::instance().cleanMods(event.state); 289 event.state = KeyUtil::instance().cleanMods(event.state);
272 290
273 KeySym ks; 291 KeySym ks;
274 char keychar[1]; 292 char keychar[20];
275 XLookupString(&event, keychar, 1, &ks, 0); 293 int count = 1;
294 if (m_xic)
295 count = Xutf8LookupString(m_xic, &event, keychar, 20, &ks, 0);
296 else
297 XLookupString(&event, keychar, 1, &ks, 0);
298
276 // a modifier key by itself doesn't do anything 299 // a modifier key by itself doesn't do anything
277 if (IsModifierKey(ks)) return; 300 if (IsModifierKey(ks)) return;
278 301
@@ -401,6 +424,17 @@ void TextBox::keyPressEvent(XKeyEvent &event) {
401 break; 424 break;
402 } 425 }
403 } 426 }
427 if (count > 1 && count < 20) {
428 wchar_t wc;
429 count = mbrtowc(&wc, keychar, count, 0);
430 if (count > 0 && iswprint(wc)) {
431 keychar[count] = '\0';
432 std::string val;
433 val += (char*)keychar;
434 insertText(val);
435 m_select_pos = std::string::npos;
436 }
437 }
404 if (isprint(keychar[0])) { 438 if (isprint(keychar[0])) {
405 std::string val; 439 std::string val;
406 val += keychar[0]; 440 val += keychar[0];
@@ -412,6 +446,23 @@ void TextBox::keyPressEvent(XKeyEvent &event) {
412 clear(); 446 clear();
413} 447}
414 448
449void TextBox::handleEvent(XEvent &event) {
450 if (event.type == KeymapNotify) {
451 XRefreshKeyboardMapping(&event.xmapping);
452 } else if (event.type == FocusIn) {
453 if (!m_xic && App::instance()->inputModule())
454 m_xic = XCreateIC(App::instance()->inputModule(), XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window(), NULL);
455 if (m_xic)
456 XSetICFocus(m_xic);
457 } else if (event.type == FocusIn) {
458 if (m_xic) {
459 XUnsetICFocus(m_xic);
460 XFree(m_xic);
461 m_xic = 0;
462 }
463 }
464}
465
415void TextBox::setCursorPosition(int pos) { 466void TextBox::setCursorPosition(int pos) {
416 m_cursor_pos = pos < 0 ? 0 : pos; 467 m_cursor_pos = pos < 0 ? 0 : pos;
417 if (m_cursor_pos > text().size()) 468 if (m_cursor_pos > text().size())
@@ -527,4 +578,27 @@ void TextBox::selectAll() {
527 select(0, m_text.visual().size()); 578 select(0, m_text.visual().size());
528} 579}
529 580
581TextBox::StringRange TextBox::charRange(std::string::size_type pos) const {
582 auto isUtf8Head = [](char c) {
583 const char indicator = (1<<7)|(1<<6); // first byte starts 11, following 10
584 return (c & indicator) == indicator;
585 };
586
587 StringRange range = {pos, pos};
588 FbString t = text();
589
590 if (pos < 0 || pos >= t.size() || t.at(pos) > 0) // invalid pos or ASCII
591 return range;
592
593 while (range.begin > 0 && t.at(range.begin) < 0 && !isUtf8Head(t.at(range.begin)))
594 --range.begin;
595
596 if (isUtf8Head(t.at(range.end))) // if pos is a utf-8 head, move into the range
597 ++range.end;
598 while (range.end < t.size() - 1 && t.at(range.end + 1) < 0 && !isUtf8Head(t.at(range.end + 1)))
599 ++range.end;
600
601 return range;
602}
603
530} // end namespace FbTk 604} // end namespace FbTk