Nux

Merge lp:~unity-team/nux/nux.text-entry-im-auto-test into lp:nux/2.0

Proposed by Brandon Schaefer
Status: Merged
Approved by: Jay Taoko
Approved revision: 589
Merged at revision: 582
Proposed branch: lp:~unity-team/nux/nux.text-entry-im-auto-test
Merge into: lp:nux/2.0
Diff against target: 4126 lines (+706/-2677)
14 files modified
Nux/InputMethodIBus.cpp (+53/-64)
Nux/InputMethodIBus.h (+15/-16)
Nux/Makefile.am (+5/-1)
Nux/TextEntry.cpp (+106/-58)
Nux/TextEntry.h (+36/-20)
Nux/TextEntryIM.cpp (+0/-2126)
Nux/TextEntryIM.h (+0/-383)
configure.ac (+11/-1)
examples/Makefile.am (+3/-1)
tests/Makefile.am (+15/-4)
tests/Readme.txt (+3/-0)
tests/nux_automated_test_framework.cpp (+58/-3)
tests/nux_automated_test_framework.h (+10/-0)
tests/xtest-text-entry.cpp (+391/-0)
To merge this branch: bzr merge lp:~unity-team/nux/nux.text-entry-im-auto-test
Reviewer Review Type Date Requested Status
Jay Taoko (community) Approve
Review via email: mp+94304@code.launchpad.net

Description of the change

TextEntry with InputMethod support.

To post a comment you must log in.
Revision history for this message
Jay Taoko (jaytaoko) wrote :

Excellent work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Nux/InputMethodIBus.cpp'
--- Nux/InputMethodIBus.cpp 2012-02-12 01:55:02 +0000
+++ Nux/InputMethodIBus.cpp 2012-02-23 05:24:18 +0000
@@ -6,7 +6,7 @@
6{6{
7 IBusBus* IBusIMEContext::bus_ = NULL;7 IBusBus* IBusIMEContext::bus_ = NULL;
88
9 IBusIMEContext::IBusIMEContext(TextEntryIM* text_entry) 9 IBusIMEContext::IBusIMEContext(TextEntry* text_entry)
10 : text_entry_(text_entry),10 : text_entry_(text_entry),
11 context_(NULL),11 context_(NULL),
12 is_focused_(false)12 is_focused_(false)
@@ -68,14 +68,16 @@
68 ibus_input_context_reset(context_);68 ibus_input_context_reset(context_);
69 }69 }
7070
71 // FIXME Need to get the key_code form nux NOT just the keysym
72 bool IBusIMEContext::FilterKeyEvent(const KeyEvent& event)71 bool IBusIMEContext::FilterKeyEvent(const KeyEvent& event)
73 {72 {
74 guint keyval = event.key_code(); // todo(jaytaoko): ui::GdkKeyCodeForWindowsKeyCode(event.key_code(), event.IsShiftDown() ^ event.IsCapsLockDown());73 guint keyval = event.key_sym(); // todo(jaytaoko): ui::GdkKeyCodeForWindowsKeyCode(event.key_code(), event.IsShiftDown() ^ event.IsCapsLockDown());
7574
76 if (context_) {75 if (context_) {
77 guint modifiers = 0;76 guint modifiers = 0;
7877
78 if (event.flags() & IBUS_IGNORED_MASK)
79 return false;
80
79 if (event.type() == EVENT_KEY_UP)81 if (event.type() == EVENT_KEY_UP)
80 modifiers |= IBUS_RELEASE_MASK;82 modifiers |= IBUS_RELEASE_MASK;
8183
@@ -88,19 +90,14 @@
88 if (event.IsCapsLockDown())90 if (event.IsCapsLockDown())
89 modifiers |= IBUS_LOCK_MASK;91 modifiers |= IBUS_LOCK_MASK;
9092
91 // FIXME Not the best way to get the x11_key_code. Should be able to get it from TextEntry::ProcessKeyEvent
92 // The x11_keycode is needed for the ibus-hangul engine!
93 nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent();
94
95 ibus_input_context_process_key_event_async(context_,93 ibus_input_context_process_key_event_async(context_,
96 keyval, cur_event.x11_keycode - 8, modifiers,94 keyval, event.key_code() - 8, modifiers,
97 -1,95 -1,
98 NULL,96 NULL,
99 reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),97 reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),
100 new ProcessKeyEventData(this, event));98 new ProcessKeyEventData(this, event));
101 return true;99 return true;
102 }100 }
103
104 return false;101 return false;
105 }102 }
106103
@@ -113,11 +110,14 @@
113 nuxAssert(bus_ != NULL);110 nuxAssert(bus_ != NULL);
114 nuxAssert(ibus_bus_is_connected(bus_));111 nuxAssert(ibus_bus_is_connected(bus_));
115112
116 context_ = ibus_bus_create_input_context(bus_, "nux");113 if (!(context_ = ibus_bus_create_input_context(bus_, "nux")))
114 {
115 nuxDebugMsg("[IBusIMEContext::IBusIMEContext] Cannot create InputContext");
116 return;
117 }
117118
118 // connect input context signals119 // connect input context signals
119 g_signal_connect(context_, "commit-text", G_CALLBACK(OnCommitText_), this);120 g_signal_connect(context_, "commit-text", G_CALLBACK(OnCommitText_), this);
120 g_signal_connect(context_, "forward-key-event", G_CALLBACK(OnForwardKeyEvent_), this);
121 g_signal_connect(context_, "update-preedit-text", G_CALLBACK(OnUpdatePreeditText_), this);121 g_signal_connect(context_, "update-preedit-text", G_CALLBACK(OnUpdatePreeditText_), this);
122 g_signal_connect(context_, "show-preedit-text", G_CALLBACK(OnShowPreeditText_), this);122 g_signal_connect(context_, "show-preedit-text", G_CALLBACK(OnShowPreeditText_), this);
123 g_signal_connect(context_, "hide-preedit-text", G_CALLBACK(OnHidePreeditText_), this);123 g_signal_connect(context_, "hide-preedit-text", G_CALLBACK(OnHidePreeditText_), this);
@@ -138,7 +138,7 @@
138138
139 void IBusIMEContext::DestroyContext()139 void IBusIMEContext::DestroyContext()
140 {140 {
141 nuxDebugMsg("***IBusIMEContext::DestroyContext***");141 //nuxDebugMsg("***IBusIMEContext::DestroyContext***");
142 if (!context_)142 if (!context_)
143 return;143 return;
144144
@@ -147,25 +147,27 @@
147 nuxAssert(!context_);147 nuxAssert(!context_);
148 }148 }
149149
150 // FIXME Also need to figure out how to get the pop up window
151 // for a possible ibus-engine to always be on top of the active
152 // window
153 void IBusIMEContext::UpdateCursorLocation()150 void IBusIMEContext::UpdateCursorLocation()
154 {151 {
155 nux::Rect strong, weak;152 nux::Rect strong, weak;
156 text_entry_->GetCursorRects(&strong, &weak);153 text_entry_->GetCursorRects(&strong, &weak);
157 nux::Geometry geo = text_entry_->GetGeometry();154
155 // Get the position of the TextEntry in the Window.
156 nux::Geometry geo = text_entry_->GetAbsoluteGeometry();
157
158 // Get the Geometry of the window on the display.
159 nux::Geometry window_geo = nux::GetGraphicsDisplay()->GetWindowGeometry();
158160
159 ibus_input_context_set_cursor_location(context_,161 ibus_input_context_set_cursor_location(context_,
160 strong.x + geo.x,162 geo.x + window_geo.x + strong.x,
161 strong.y + geo.y,163 geo.y + window_geo.y,
162 strong.width,164 0,
163 strong.height);165 geo.height);
164 }166 }
165167
166 void IBusIMEContext::OnConnected(IBusBus *bus)168 void IBusIMEContext::OnConnected(IBusBus *bus)
167 {169 {
168 nuxDebugMsg("***IBusIMEContext::OnConnected***");170 //nuxDebugMsg("***IBusIMEContext::OnConnected***");
169171
170 nuxAssert(bus_ == bus);172 nuxAssert(bus_ == bus);
171 nuxAssert(ibus_bus_is_connected(bus_));173 nuxAssert(ibus_bus_is_connected(bus_));
@@ -177,13 +179,15 @@
177179
178 void IBusIMEContext::OnDisconnected(IBusBus *bus)180 void IBusIMEContext::OnDisconnected(IBusBus *bus)
179 {181 {
180 nuxDebugMsg("***IBusIMEContext::OnDisonnected***");182 //nuxDebugMsg("***IBusIMEContext::OnDisonnected***");
183
184 if (context_)
185 DestroyContext();
181 }186 }
182187
183 void IBusIMEContext::OnCommitText(IBusInputContext *context, IBusText* text)188 void IBusIMEContext::OnCommitText(IBusInputContext *context, IBusText* text)
184 {189 {
185 nuxDebugMsg("***IBusIMEContext::OnCommitText***");190 //nuxDebugMsg("***IBusIMEContext::OnCommitText::%s***", text->text);
186 printf ("OnCommit %s\n", text->text);
187 nuxAssert(context_ == context);191 nuxAssert(context_ == context);
188192
189 text_entry_->DeleteSelection();193 text_entry_->DeleteSelection();
@@ -201,39 +205,15 @@
201 }205 }
202 }206 }
203207
204 void IBusIMEContext::OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state)
205 {
206 nuxDebugMsg("***IBusIMEContext::OnForwardKeyEvent***");
207 nuxAssert(context_ == context);
208
209 int flags = 0;
210
211 if (state & IBUS_LOCK_MASK)
212 flags |= KEY_MODIFIER_CAPS_LOCK;
213 if (state & IBUS_CONTROL_MASK)
214 flags |= KEY_MODIFIER_CTRL;
215 if (state & IBUS_SHIFT_MASK)
216 flags |= KEY_MODIFIER_SHIFT;
217 if (state & IBUS_MOD1_MASK)
218 flags |= KEY_MODIFIER_ALT;
219
220 int mouse_state = 0;
221 if (state & IBUS_BUTTON1_MASK)
222 mouse_state |= MOUSE_BUTTON1;
223 if (state & IBUS_BUTTON2_MASK)
224 mouse_state |= MOUSE_BUTTON2;
225 if (state & IBUS_BUTTON3_MASK)
226 mouse_state |= MOUSE_BUTTON3;
227
228 //ForwardKeyEvent(KeyEvent(state & IBUS_RELEASE_MASK ? EVENT_KEY_DOWN : EVENT_KEY_UP, keyval /* todo(jaytaoko): ui::WindowsKeyCodeForGdkKeyCode(keyval)*/, mouse_state, flags));
229 }
230
231 void IBusIMEContext::OnUpdatePreeditText(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible)208 void IBusIMEContext::OnUpdatePreeditText(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible)
232 {209 {
233 nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***");210 //nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***");
234 nuxAssert(context_ == context);211 nuxAssert(context_ == context);
235 nuxAssert(IBUS_IS_TEXT(text));212 nuxAssert(IBUS_IS_TEXT(text));
236213
214 if (text_entry_->preedit_.empty())
215 UpdateCursorLocation();
216
237 if (visible)217 if (visible)
238 {218 {
239 IBusAttrList* attrs = text->attrs;219 IBusAttrList* attrs = text->attrs;
@@ -279,50 +259,54 @@
279 text_entry_->preedit_ = preedit;259 text_entry_->preedit_ = preedit;
280 text_entry_->preedit_cursor_ = preedit.length();260 text_entry_->preedit_cursor_ = preedit.length();
281 text_entry_->QueueRefresh (true, true);261 text_entry_->QueueRefresh (true, true);
282 text_entry_->sigTextChanged.emit(text_entry_);262 text_entry_->text_changed.emit(text_entry_);
283 UpdateCursorLocation();
284 }263 }
285 }264 }
265 else
266 {
267 OnHidePreeditText(context_);
268 }
286 }269 }
287270
288 void IBusIMEContext::OnShowPreeditText(IBusInputContext *context)271 void IBusIMEContext::OnShowPreeditText(IBusInputContext *context)
289 {272 {
290 nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***");273 //nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***");
291 nuxAssert(context_ == context);274 nuxAssert(context_ == context);
292 }275 }
293276
294 void IBusIMEContext::OnHidePreeditText(IBusInputContext *context)277 void IBusIMEContext::OnHidePreeditText(IBusInputContext *context)
295 {278 {
296 nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***");279 //nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***");
297 nuxAssert(context_ == context);280 nuxAssert(context_ == context);
298281
299 text_entry_->ResetPreedit();282 text_entry_->ResetPreedit();
300 text_entry_->QueueRefresh (true, true);283 text_entry_->QueueRefresh (true, true);
301 text_entry_->sigTextChanged.emit(text_entry_);284 text_entry_->text_changed.emit(text_entry_);
302 }285 }
303286
304 void IBusIMEContext::OnEnable(IBusInputContext *context)287 void IBusIMEContext::OnEnable(IBusInputContext *context)
305 {288 {
306 nuxDebugMsg("***IBusIMEContext::OnEnable***");289 //nuxDebugMsg("***IBusIMEContext::OnEnable***");
307 nuxAssert(context_ == context);290 nuxAssert(context_ == context);
308291
309 text_entry_->ime_active_ = true;292 text_entry_->ime_active_ = true;
293 UpdateCursorLocation();
310 }294 }
311295
312 void IBusIMEContext::OnDisable(IBusInputContext *context)296 void IBusIMEContext::OnDisable(IBusInputContext *context)
313 {297 {
314 nuxDebugMsg("***IBusIMEContext::OnDisable***");298 //nuxDebugMsg("***IBusIMEContext::OnDisable***");
315 nuxAssert(context_ == context);299 nuxAssert(context_ == context);
316300
317 text_entry_->ime_active_ = false;301 text_entry_->ime_active_ = false;
318 text_entry_->ResetPreedit();302 text_entry_->ResetPreedit();
319 text_entry_->QueueRefresh (true, true);303 text_entry_->QueueRefresh (true, true);
320 text_entry_->sigTextChanged.emit(text_entry_);304 text_entry_->text_changed.emit(text_entry_);
321 }305 }
322306
323 void IBusIMEContext::OnDestroy(IBusInputContext *context)307 void IBusIMEContext::OnDestroy(IBusInputContext *context)
324 {308 {
325 nuxDebugMsg("***IBusIMEContext::OnDestroy***");309 //nuxDebugMsg("***IBusIMEContext::OnDestroy***");
326 nuxAssert(context_ == context);310 nuxAssert(context_ == context);
327311
328 g_object_unref(context_);312 g_object_unref(context_);
@@ -331,7 +315,7 @@
331315
332 void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context, GAsyncResult* res, ProcessKeyEventData *data)316 void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context, GAsyncResult* res, ProcessKeyEventData *data)
333 {317 {
334 nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***");318 //nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***");
335 nuxAssert(data->context->context_ == context);319 nuxAssert(data->context->context_ == context);
336320
337 GError *error = NULL;321 GError *error = NULL;
@@ -346,9 +330,14 @@
346 g_error_free (error);330 g_error_free (error);
347 }331 }
348332
349 // FIXME Need to forward this event somewhere..
350 if (processed == FALSE)333 if (processed == FALSE)
351 printf ("Processed %i\n", processed);334 {
335 data->context->text_entry_->ProcessKeyEvent(data->event.type(),
336 data->event.key_sym(),
337 data->event.flags() | IBUS_IGNORED_MASK,
338 data->event.character().c_str(),
339 0);
340 }
352341
353 delete data;342 delete data;
354 }343 }
355344
=== modified file 'Nux/InputMethodIBus.h'
--- Nux/InputMethodIBus.h 2012-02-12 00:39:29 +0000
+++ Nux/InputMethodIBus.h 2012-02-23 05:24:18 +0000
@@ -1,14 +1,14 @@
1#ifndef INPUTMETHODIBUS_H1#ifndef INPUTMETHODIBUS_H
2#define INPUTMETHODIBUS_H2#define INPUTMETHODIBUS_H
33
4#include <Nux/TextEntryIM.h>4#include <Nux/TextEntry.h>
5#include <ibus.h>5#include <ibus.h>
66
7namespace nux7namespace nux
8{8{
99
10 class IBusIMEContext;10 class IBusIMEContext;
11 class TextEntryIM;11 class TextEntry;
12 12
13 // FIXME This class should be reworked to replace the mouse_state13 // FIXME This class should be reworked to replace the mouse_state
14 // with the hardware key_code. 14 // with the hardware key_code.
@@ -17,19 +17,21 @@
17 public:17 public:
1818
19 KeyEvent(NuxEventType type,19 KeyEvent(NuxEventType type,
20 unsigned int key_code,20 unsigned int key_sym,
21 unsigned int mouse_state, unsigned int event_flags)21 unsigned int key_code, unsigned int event_flags, const char* character)
22 : type_(type)22 : type_(type)
23 , key_sym_(key_sym)
23 , key_code_(key_code)24 , key_code_(key_code)
24 , key_modifiers_(event_flags)25 , key_modifiers_(event_flags)
25 , mouse_state_(mouse_state)26 , character_(character)
26 {27 {
27 }28 }
2829
29 NuxEventType type() const {return type_;}30 NuxEventType type() const {return type_;}
31 unsigned int key_sym() const {return key_sym_;}
30 unsigned int key_code() const {return key_code_;}32 unsigned int key_code() const {return key_code_;}
31 unsigned int flags() const {return key_modifiers_;}33 unsigned int flags() const {return key_modifiers_;}
32 unsigned int MouseState() const {return mouse_state_;}34 std::string character() const {return character_;}
3335
34 bool IsShiftDown() const { return (key_modifiers_ & KEY_MODIFIER_SHIFT) != 0; }36 bool IsShiftDown() const { return (key_modifiers_ & KEY_MODIFIER_SHIFT) != 0; }
35 bool IsControlDown() const { return (key_modifiers_ & KEY_MODIFIER_CTRL) != 0; }37 bool IsControlDown() const { return (key_modifiers_ & KEY_MODIFIER_CTRL) != 0; }
@@ -38,9 +40,10 @@
3840
39 private:41 private:
40 EventType type_;42 EventType type_;
43 unsigned int key_sym_;
41 unsigned int key_code_;44 unsigned int key_code_;
42 unsigned int key_modifiers_;45 unsigned int key_modifiers_;
43 unsigned int mouse_state_;46 std::string character_;
4447
45 KeyEvent(const KeyEvent&);48 KeyEvent(const KeyEvent&);
46 void operator = (const KeyEvent&);49 void operator = (const KeyEvent&);
@@ -53,7 +56,7 @@
53 ProcessKeyEventData(IBusIMEContext* context,56 ProcessKeyEventData(IBusIMEContext* context,
54 const KeyEvent& event)57 const KeyEvent& event)
55 : context(context)58 : context(context)
56 , event(event.type(), event.key_code(), event.MouseState(), event.flags())59 , event(event.type(), event.key_sym(), event.key_code(), event.flags(), event.character().c_str())
57 {60 {
5861
59 }62 }
@@ -65,7 +68,7 @@
65 class IBusIMEContext68 class IBusIMEContext
66 {69 {
67 public:70 public:
68 explicit IBusIMEContext(TextEntryIM* text_entry);71 explicit IBusIMEContext(TextEntry* text_entry);
69 virtual ~IBusIMEContext();72 virtual ~IBusIMEContext();
7073
71 // views::IMEContext implementations:74 // views::IMEContext implementations:
@@ -87,7 +90,7 @@
87 void OnConnected(IBusBus *bus);90 void OnConnected(IBusBus *bus);
8891
89 //CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*);92 //CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*);
90 static void OnDisconnected_(IBusBus* bus, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnConnected(bus);}93 static void OnDisconnected_(IBusBus* bus, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnDisconnected(bus);}
91 void OnDisconnected(IBusBus *bus);94 void OnDisconnected(IBusBus *bus);
9295
93// // Event handlers for IBusIMEContext:96// // Event handlers for IBusIMEContext:
@@ -95,10 +98,6 @@
95 static void OnCommitText_(IBusInputContext* context, IBusText* text, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnCommitText(context, text);}98 static void OnCommitText_(IBusInputContext* context, IBusText* text, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnCommitText(context, text);}
96 void OnCommitText(IBusInputContext *context, IBusText* text);99 void OnCommitText(IBusInputContext *context, IBusText* text);
97100
98 //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnForwardKeyEvent, IBusInputContext*, guint, guint, guint);
99 static void OnForwardKeyEvent_(IBusInputContext* context, guint keyval, guint keycode, guint state, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnForwardKeyEvent(context, keyval, keycode, state);}
100 void OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state);
101
102 //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText, IBusInputContext*, IBusText*, guint, gboolean);101 //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText, IBusInputContext*, IBusText*, guint, gboolean);
103 static void OnUpdatePreeditText_(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnUpdatePreeditText(context, text, cursor_pos, visible);}102 static void OnUpdatePreeditText_(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnUpdatePreeditText(context, text, cursor_pos, visible);}
104 void OnUpdatePreeditText(IBusInputContext *context, IBusText* text, guint cursor_pos, gboolean visible);103 void OnUpdatePreeditText(IBusInputContext *context, IBusText* text, guint cursor_pos, gboolean visible);
@@ -127,7 +126,7 @@
127 GAsyncResult* res,126 GAsyncResult* res,
128 ProcessKeyEventData* data);127 ProcessKeyEventData* data);
129128
130 TextEntryIM* text_entry_;129 TextEntry* text_entry_;
131 IBusInputContext* context_;130 IBusInputContext* context_;
132 bool is_focused_;131 bool is_focused_;
133132
134133
=== modified file 'Nux/Makefile.am'
--- Nux/Makefile.am 2012-02-13 15:22:30 +0000
+++ Nux/Makefile.am 2012-02-23 05:24:18 +0000
@@ -15,13 +15,15 @@
15 -DG_LOG_DOMAIN=\"Nux\" \15 -DG_LOG_DOMAIN=\"Nux\" \
16 $(GCC_FLAGS) \16 $(GCC_FLAGS) \
17 $(NUX_CFLAGS) \17 $(NUX_CFLAGS) \
18 $(IBUS_CFLAGS) \
18 $(MAINTAINER_CFLAGS)19 $(MAINTAINER_CFLAGS)
1920
20libnux_@NUX_API_VERSION@_la_LIBADD = \21libnux_@NUX_API_VERSION@_la_LIBADD = \
21 $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \22 $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \
22 $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \23 $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \
23 $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \24 $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \
24 $(NUX_LIBS)25 $(NUX_LIBS) \
26 $(IBUS_LIBS)
2527
26libnux_@NUX_API_VERSION@_la_LDFLAGS = \28libnux_@NUX_API_VERSION@_la_LDFLAGS = \
27 $(NUX_LT_LDFLAGS)29 $(NUX_LT_LDFLAGS)
@@ -93,6 +95,7 @@
93 $(srcdir)/SystemThread.cpp \95 $(srcdir)/SystemThread.cpp \
94 $(srcdir)/TabView.cpp \96 $(srcdir)/TabView.cpp \
95 $(srcdir)/TextEntry.cpp \97 $(srcdir)/TextEntry.cpp \
98 $(srcdir)/InputMethodIBus.cpp \
96 $(srcdir)/TextLoader.cpp \99 $(srcdir)/TextLoader.cpp \
97 $(srcdir)/TextureArea.cpp \100 $(srcdir)/TextureArea.cpp \
98 $(srcdir)/Timeline.cpp \101 $(srcdir)/Timeline.cpp \
@@ -178,6 +181,7 @@
178 $(srcdir)/SystemThread.h \181 $(srcdir)/SystemThread.h \
179 $(srcdir)/TabView.h \182 $(srcdir)/TabView.h \
180 $(srcdir)/TextEntry.h \183 $(srcdir)/TextEntry.h \
184 $(srcdir)/InputMethodIBus.h \
181 $(srcdir)/TextLoader.h \185 $(srcdir)/TextLoader.h \
182 $(srcdir)/TextureArea.h \186 $(srcdir)/TextureArea.h \
183 $(srcdir)/Timeline.h \187 $(srcdir)/Timeline.h \
184188
=== modified file 'Nux/TextEntry.cpp'
--- Nux/TextEntry.cpp 2012-02-18 21:32:06 +0000
+++ Nux/TextEntry.cpp 2012-02-23 05:24:18 +0000
@@ -76,8 +76,8 @@
76 return result;76 return result;
77 }77 }
7878
79// Calculate pixel size based on the Windows DPI of 96 for compatibility79 // Calculate pixel size based on the Windows DPI of 96 for compatibility
80// reasons.80 // reasons.
81 CairoFont::CairoFont(const std::string &family,81 CairoFont::CairoFont(const std::string &family,
82 /*PangoFontDescription *font,*/82 /*PangoFontDescription *font,*/
83 double pt_size,83 double pt_size,
@@ -112,10 +112,10 @@
112 TextEntry::TextEntry(const char* text, NUX_FILE_LINE_DECL)112 TextEntry::TextEntry(const char* text, NUX_FILE_LINE_DECL)
113 : View(NUX_FILE_LINE_PARAM)113 : View(NUX_FILE_LINE_PARAM)
114 , _size_match_text(true)114 , _size_match_text(true)
115 , _texture2D(NULL)115 , _texture2D(nullptr)
116 , canvas_(NULL)116 , canvas_(nullptr)
117 , cached_layout_(NULL)117 , cached_layout_(nullptr)
118 , preedit_attrs_(NULL)118 , preedit_attrs_(nullptr)
119 , completion_color_(color::Gray)119 , completion_color_(color::Gray)
120 , last_dblclick_time_(0)120 , last_dblclick_time_(0)
121 , cursor_(0)121 , cursor_(0)
@@ -152,7 +152,9 @@
152 , align_(CairoGraphics::ALIGN_LEFT)152 , align_(CairoGraphics::ALIGN_LEFT)
153#if defined(NUX_OS_LINUX)153#if defined(NUX_OS_LINUX)
154 , caret_cursor_(None)154 , caret_cursor_(None)
155#endif155 , ime_(new IBusIMEContext(this))
156#endif
157 , ime_active_(false)
156 , text_input_mode_(false)158 , text_input_mode_(false)
157 , key_nav_mode_(false)159 , key_nav_mode_(false)
158 {160 {
@@ -182,8 +184,6 @@
182184
183 TextEntry::~TextEntry()185 TextEntry::~TextEntry()
184 {186 {
185 ResetLayout();
186
187 if (cursor_blink_timer_)187 if (cursor_blink_timer_)
188 g_source_remove(cursor_blink_timer_);188 g_source_remove(cursor_blink_timer_);
189189
@@ -191,8 +191,10 @@
191 if (_texture2D)191 if (_texture2D)
192 _texture2D->UnReference();192 _texture2D->UnReference();
193193
194 if (canvas_)194#if defined(NUX_OS_LINUX)
195 delete canvas_;195 if (ime_)
196 delete ime_;
197#endif
196 }198 }
197199
198 void TextEntry::PreLayoutManagement()200 void TextEntry::PreLayoutManagement()
@@ -242,7 +244,7 @@
242 {244 {
243 SelectLine();245 SelectLine();
244 }246 }
245 else if (event_type == NUX_MOUSE_DOUBLECLICK)247 else if (event_type == NUX_MOUSE_DOUBLECLICK && !ime_active_)
246 {248 {
247 SelectWord();249 SelectWord();
248 last_dblclick_time_ = current_time;250 last_dblclick_time_ = current_time;
@@ -281,26 +283,25 @@
281 const char* character , /*character*/283 const char* character , /*character*/
282 unsigned short keyCount /*key repeat count*/)284 unsigned short keyCount /*key repeat count*/)
283 {285 {
286 bool retval = FALSE;
287
284 if (event_type == NUX_KEYDOWN)288 if (event_type == NUX_KEYDOWN)
285 text_input_mode_ = true;289 text_input_mode_ = true;
286290
287// GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent());
288// ASSERT(gdk_event);
289//
290// Event::Type type = event.GetType();
291 // Cause the cursor to stop blinking for a while.
292 cursor_blink_status_ = 4;291 cursor_blink_status_ = 4;
293292
294// if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/)293 // FIXME Have to get the current event fot he x11_keycode for ibus-hangul/korean input
295// {294 nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent();
296// need_im_reset_ = true;295 KeyEvent event((NuxEventType)event_type, keysym,cur_event.x11_keycode, state, character);
297// QueueRefresh(false, true);296
298// return EVENT_RESULT_HANDLED;297#if defined(NUX_OS_LINUX)
299// }298 retval = ime_->FilterKeyEvent(event);
299#endif
300300
301 if (event_type == NUX_KEYUP)301 if (event_type == NUX_KEYUP)
302 return;302 return;
303303
304
304 // we need to ignore some characters305 // we need to ignore some characters
305 if (keysym == NUX_VK_TAB)306 if (keysym == NUX_VK_TAB)
306 return;307 return;
@@ -316,8 +317,7 @@
316 bool ctrl = (state & NUX_STATE_CTRL);317 bool ctrl = (state & NUX_STATE_CTRL);
317318
318 // DLOG("TextEntry::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl);319 // DLOG("TextEntry::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl);
319320 if (event_type == NUX_KEYDOWN && !retval)
320 if (event_type == NUX_KEYDOWN)
321 {321 {
322 if (keyval == NUX_VK_LEFT)322 if (keyval == NUX_VK_LEFT)
323 {323 {
@@ -428,7 +428,7 @@
428// }428// }
429 }429 }
430430
431 if (character != 0 && (strlen(character) != 0))431 if (!retval && character != 0 && (strlen(character) != 0))
432 {432 {
433 EnterText(character);433 EnterText(character);
434 }434 }
@@ -456,7 +456,7 @@
456 {456 {
457 ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags);457 ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags);
458 }458 }
459459
460 void TextEntry::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)460 void TextEntry::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
461 {461 {
462#if defined(NUX_OS_LINUX)462#if defined(NUX_OS_LINUX)
@@ -464,7 +464,7 @@
464 {464 {
465 Display* display = nux::GetGraphicsDisplay()->GetX11Display();465 Display* display = nux::GetGraphicsDisplay()->GetX11Display();
466 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());466 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());
467467
468 if (display && window)468 if (display && window)
469 {469 {
470 caret_cursor_ = XCreateFontCursor(display, XC_xterm);470 caret_cursor_ = XCreateFontCursor(display, XC_xterm);
@@ -473,7 +473,7 @@
473 }473 }
474#endif474#endif
475 }475 }
476476
477 void TextEntry::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)477 void TextEntry::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
478 {478 {
479#if defined(NUX_OS_LINUX)479#if defined(NUX_OS_LINUX)
@@ -481,7 +481,7 @@
481 {481 {
482 Display* display = nux::GetGraphicsDisplay()->GetX11Display();482 Display* display = nux::GetGraphicsDisplay()->GetX11Display();
483 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());483 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());
484484
485 if (display && window)485 if (display && window)
486 {486 {
487 XUndefineCursor(display, window->GetInputWindowId());487 XUndefineCursor(display, window->GetInputWindowId());
@@ -559,9 +559,9 @@
559 // intentionally left empty559 // intentionally left empty
560 }560 }
561561
562 void TextEntry::SetText(const char *text)562 void TextEntry::SetText(const char* text)
563 {563 {
564 const char *end = NULL;564 const char* end = NULL;
565 g_utf8_validate(text, -1, &end);565 g_utf8_validate(text, -1, &end);
566566
567 std::string txt((text && *text && end > text) ? std::string(text, end) : "");567 std::string txt((text && *text && end > text) ? std::string(text, end) : "");
@@ -574,7 +574,7 @@
574 need_im_reset_ = true;574 need_im_reset_ = true;
575 //ResetImContext();575 //ResetImContext();
576 QueueRefresh(true, true);576 QueueRefresh(true, true);
577 sigTextChanged.emit(this);577 text_changed.emit(this);
578 }578 }
579579
580 std::string const& TextEntry::GetText() const580 std::string const& TextEntry::GetText() const
@@ -582,9 +582,28 @@
582 return text_;582 return text_;
583 }583 }
584584
585 void TextEntry::SetCompletion(const char *text)585 std::string TextEntry::GetTextSelection() const
586 {586 {
587 const char *end = NULL;587 if (text_.size() == 0)
588 {
589 return std::string("");
590 }
591
592 int selection_start = 0;
593 int selection_end = 0;
594 if (GetSelectionBounds(&selection_start, &selection_end))
595 {
596 return text_.substr(selection_start, selection_end);
597 }
598 else
599 {
600 return std::string("");
601 }
602 }
603
604 void TextEntry::SetCompletion(const char* text)
605 {
606 const char* end = NULL;
588 g_utf8_validate(text, -1, &end);607 g_utf8_validate(text, -1, &end);
589 std::string txt((text && *text && end > text) ? std::string(text, end) : "");608 std::string txt((text && *text && end > text) ? std::string(text, end) : "");
590 if (txt == completion_)609 if (txt == completion_)
@@ -628,7 +647,7 @@
628 void TextEntry::MainDraw()647 void TextEntry::MainDraw()
629 {648 {
630649
631 CairoGraphics *edit_canvas = EnsureCanvas();650 CairoGraphics* edit_canvas = EnsureCanvas();
632651
633 if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty())652 if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty())
634 {653 {
@@ -677,6 +696,9 @@
677 if (!readonly_ /*&& im_context_*/)696 if (!readonly_ /*&& im_context_*/)
678 {697 {
679 need_im_reset_ = true;698 need_im_reset_ = true;
699#if defined(NUX_OS_LINUX)
700 ime_->Focus();
701#endif
680 //gtk_im_context_focus_in(im_context_);702 //gtk_im_context_focus_in(im_context_);
681 //UpdateIMCursorLocation();703 //UpdateIMCursorLocation();
682 }704 }
@@ -696,6 +718,9 @@
696 if (!readonly_ /*&& im_context_*/)718 if (!readonly_ /*&& im_context_*/)
697 {719 {
698 need_im_reset_ = true;720 need_im_reset_ = true;
721#if defined(NUX_OS_LINUX)
722 ime_->Blur();
723#endif
699 //gtk_im_context_focus_out(im_context_);724 //gtk_im_context_focus_out(im_context_);
700 }725 }
701 cursor_visible_ = false; // hide cursor when losing focus726 cursor_visible_ = false; // hide cursor when losing focus
@@ -733,7 +758,7 @@
733 int display_width = GetBaseWidth() - kInnerBorderX * 2;758 int display_width = GetBaseWidth() - kInnerBorderX * 2;
734 int display_height = GetBaseHeight() - kInnerBorderY * 2;759 int display_height = GetBaseHeight() - kInnerBorderY * 2;
735760
736 PangoLayout *layout = EnsureLayout();761 PangoLayout* layout = EnsureLayout();
737 int text_width, text_height;762 int text_width, text_height;
738 pango_layout_get_pixel_size(layout, &text_width, &text_height);763 pango_layout_get_pixel_size(layout, &text_width, &text_height);
739764
@@ -1199,7 +1224,7 @@
11991224
1200 int pre_completion_length = tmp_string.length();1225 int pre_completion_length = tmp_string.length();
12011226
1202 if (!completion_.empty() && !wrap_)1227 if (!completion_.empty() && !wrap_ && preedit_.empty())
1203 {1228 {
1204 tmp_string = text_ + completion_;1229 tmp_string = text_ + completion_;
1205 }1230 }
@@ -1225,7 +1250,7 @@
1225 }1250 }
1226 if (!completion_.empty() && !wrap_)1251 if (!completion_.empty() && !wrap_)
1227 {1252 {
1228 attr = pango_attr_foreground_new(65535 * completion_color_.red,1253 attr = pango_attr_foreground_new(65535 * completion_color_.red,
1229 65535 * completion_color_.green,1254 65535 * completion_color_.green,
1230 65535 * completion_color_.blue);1255 65535 * completion_color_.blue);
1231 attr->start_index = static_cast<guint>(pre_completion_length);1256 attr->start_index = static_cast<guint>(pre_completion_length);
@@ -1455,7 +1480,7 @@
1455 }1480 }
14561481
1457 ResetLayout();1482 ResetLayout();
1458 sigTextChanged.emit(this);1483 text_changed.emit(this);
1459 }1484 }
14601485
1461 void TextEntry::DeleteText(int start, int end)1486 void TextEntry::DeleteText(int start, int end)
@@ -1486,7 +1511,7 @@
1486 selection_bound_ -= (end - start);1511 selection_bound_ -= (end - start);
14871512
1488 ResetLayout();1513 ResetLayout();
1489 sigTextChanged.emit(this);1514 text_changed.emit(this);
1490 }1515 }
14911516
1492 void TextEntry::SelectWord()1517 void TextEntry::SelectWord()
@@ -1532,6 +1557,11 @@
1532 QueueRefresh(true, true);1557 QueueRefresh(true, true);
1533 }1558 }
15341559
1560 bool TextEntry::im_active()
1561 {
1562 return ime_active_;
1563 }
1564
1535 void TextEntry::DeleteSelection()1565 void TextEntry::DeleteSelection()
1536 {1566 {
1537 int start, end;1567 int start, end;
@@ -1668,8 +1698,8 @@
1668 continue;1698 continue;
1669 if (end_index < line->start_index)1699 if (end_index < line->start_index)
1670 break;1700 break;
1671 draw_start = Max<int>(start_index, line->start_index);1701 draw_start = std::max<int>(start_index, line->start_index);
1672 draw_end = Min<int>(end_index, line->start_index + line->length);1702 draw_end = std::min<int>(end_index, line->start_index + line->length);
1673 pango_layout_line_get_x_ranges(line, draw_start, draw_end,1703 pango_layout_line_get_x_ranges(line, draw_start, draw_end,
1674 &ranges, &n_ranges);1704 &ranges, &n_ranges);
1675 pango_layout_line_get_pixel_extents(line, NULL, &line_extents);1705 pango_layout_line_get_pixel_extents(line, NULL, &line_extents);
@@ -1739,8 +1769,8 @@
1739 nuxAssert(count);1769 nuxAssert(count);
1740 nuxAssert(preedit_.length() == 0);1770 nuxAssert(preedit_.length() == 0);
17411771
1742 PangoLayout *layout = EnsureLayout();1772 PangoLayout* layout = EnsureLayout();
1743 const char *text = pango_layout_get_text(layout);1773 const char* text = pango_layout_get_text(layout);
1744 int index = TextIndexToLayoutIndex(current_index, false);1774 int index = TextIndexToLayoutIndex(current_index, false);
1745 int new_index = 0;1775 int new_index = 0;
1746 int new_trailing = 0;1776 int new_trailing = 0;
@@ -1780,11 +1810,11 @@
17801810
1781 // The cursor movement direction shall be determined by the direction of1811 // The cursor movement direction shall be determined by the direction of
1782 // current text line.1812 // current text line.
1783 PangoLayout *layout = EnsureLayout();1813 PangoLayout* layout = EnsureLayout();
1784 int n_log_attrs;1814 int n_log_attrs;
1785 PangoLogAttr *log_attrs;1815 PangoLogAttr* log_attrs;
1786 pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs);1816 pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs);
1787 const char *text = pango_layout_get_text(layout);1817 const char* text = pango_layout_get_text(layout);
1788 int index = TextIndexToLayoutIndex(current_index, false);1818 int index = TextIndexToLayoutIndex(current_index, false);
1789 int line_index;1819 int line_index;
1790 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL);1820 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL);
@@ -1797,12 +1827,12 @@
1797 }1827 }
17981828
1799#if PANGO_VERSION_CHECK(1,16,0)1829#if PANGO_VERSION_CHECK(1,16,0)
1800 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index);1830 PangoLayoutLine* line = pango_layout_get_line_readonly(layout, line_index);
1801#else1831#else
1802 PangoLayoutLine *line = pango_layout_get_line(layout, line_index);1832 PangoLayoutLine* line = pango_layout_get_line(layout, line_index);
1803#endif1833#endif
1804 bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL);1834 bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL);
1805 const char *ptr = text + index;1835 const char* ptr = text + index;
1806 int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr));1836 int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr));
1807 while (count != 0)1837 while (count != 0)
1808 {1838 {
@@ -2055,12 +2085,17 @@
2055 return Clamp(index, 0, static_cast<int>(text_.length()));2085 return Clamp(index, 0, static_cast<int>(text_.length()));
2056 }2086 }
20572087
2058 bool TextEntry::GetSelectionBounds(int *start, int *end)2088 bool TextEntry::GetSelectionBounds(int* start, int* end) const
2059 {2089 {
2060 if (start)2090 if (start)
2061 *start = Min<int>(selection_bound_, cursor_);2091 {
2092 *start = std::min<int>(selection_bound_, cursor_);
2093 }
2094
2062 if (end)2095 if (end)
2063 *end = Max<int>(selection_bound_, cursor_);2096 {
2097 *end = std::max<int>(selection_bound_, cursor_);
2098 }
20642099
2065 return (selection_bound_ != cursor_);2100 return (selection_bound_ != cursor_);
2066 }2101 }
@@ -2108,7 +2143,7 @@
2108 unsigned int key_sym,2143 unsigned int key_sym,
2109 const char* character)2144 const char* character)
2110 {2145 {
2111 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false))2146 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false) && (ime_active_ == false))
2112 {2147 {
2113 if (key_sym == NUX_VK_ENTER ||2148 if (key_sym == NUX_VK_ENTER ||
2114 key_sym == NUX_KP_ENTER ||2149 key_sym == NUX_KP_ENTER ||
@@ -2124,7 +2159,7 @@
2124 }2159 }
2125 }2160 }
21262161
2127 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true))2162 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true) && (ime_active_ == false))
2128 {2163 {
2129 // Enable to exit the TextEntry when in write mode(hack for unity dash)2164 // Enable to exit the TextEntry when in write mode(hack for unity dash)
2130 if (key_sym == NUX_VK_UP ||2165 if (key_sym == NUX_VK_UP ||
@@ -2142,4 +2177,17 @@
21422177
2143 return true;2178 return true;
2144 }2179 }
2180
2181 /// Public API
2182
2183 void TextEntry::MoveCursorToLineStart()
2184 {
2185 MoveCursor(DISPLAY_LINE_ENDS, -1, 0);
2186 }
2187
2188 void TextEntry::MoveCursorToLineEnd()
2189 {
2190 MoveCursor(DISPLAY_LINE_ENDS, 1, 0);
2191 }
2192
2145}2193}
21462194
=== modified file 'Nux/TextEntry.h'
--- Nux/TextEntry.h 2012-02-12 02:24:29 +0000
+++ Nux/TextEntry.h 2012-02-23 05:24:18 +0000
@@ -25,10 +25,14 @@
25#include "pango/pango.h"25#include "pango/pango.h"
26#include "pango/pangocairo.h"26#include "pango/pangocairo.h"
27#include "NuxImage/CairoGraphics.h"27#include "NuxImage/CairoGraphics.h"
28#if defined(NUX_OS_LINUX)
29#include "InputMethodIBus.h"
30#endif
2831
29namespace nux32namespace nux
30{33{
31 class CairoGraphics;34 class CairoGraphics;
35 class IBusIMEContext;
3236
33 class CairoFont37 class CairoFont
34 {38 {
@@ -81,9 +85,9 @@
81 ~TextEntry();85 ~TextEntry();
8286
83 Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type);87 Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type);
84 virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw);88 virtual void Draw(GraphicsEngine& graphics_engine, bool force_draw);
85 virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw);89 virtual void DrawContent(GraphicsEngine& graphics_engine, bool force_draw);
86 virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw);90 virtual void PostDraw(GraphicsEngine& graphics_engine, bool force_draw);
8791
88 void PreLayoutManagement();92 void PreLayoutManagement();
89 long PostLayoutManagement(long layoutResult);93 long PostLayoutManagement(long layoutResult);
@@ -125,24 +129,25 @@
125 /*!129 /*!
126 This signal is emitted when the text has changed.130 This signal is emitted when the text has changed.
127 */131 */
128 sigc::signal <void, TextEntry*> sigTextChanged;132 sigc::signal <void, TextEntry*> text_changed;
129 sigc::signal <void> activated;133 sigc::signal <void> activated;
130 sigc::signal <void, int> cursor_moved;134 sigc::signal <void, int> cursor_moved;
131135
132 void SetText(const char *text);136 void SetText(const char* text);
133 std::string const& GetText() const;137 std::string const& GetText() const;
138 std::string GetTextSelection() const;
134139
135 void SetCompletion(const char *text); // Should use std::string, does not for consistancy140 void SetCompletion(const char* text); // Should use std::string, does not for consistancy
136 std::string const& GetCompletion() const;141 std::string const& GetCompletion() const;
137142
138 void SetCompletionColor(const Color &color);143 void SetCompletionColor(const Color& color);
139 Color const& GetCompletionColor() const;144 Color const& GetCompletionColor() const;
140145
141 void SetTextColor(const Color &color);146 void SetTextColor(const Color& color);
142 Color const& GetTextColor() const;147 Color const& GetTextColor() const;
143 void SetFontFamily(const char *font);148 void SetFontFamily(const char* font);
144 void SetFontSize(double font_size);149 void SetFontSize(double font_size);
145 void SetFontOptions(const cairo_font_options_t *options);150 void SetFontOptions(const cairo_font_options_t* options);
146151
147 /** Select text between start and end. */152 /** Select text between start and end. */
148 void Select(int start, int end);153 void Select(int start, int end);
@@ -152,6 +157,11 @@
152 CairoGraphics::Alignment GetAlign() const;157 CairoGraphics::Alignment GetAlign() const;
153 void SetAlign(CairoGraphics::Alignment align);158 void SetAlign(CairoGraphics::Alignment align);
154159
160 bool im_active();
161
162 void MoveCursorToLineStart();
163 void MoveCursorToLineEnd();
164
155 protected:165 protected:
156 bool _block_focus; // used to selectively ignore focus keyevents166 bool _block_focus; // used to selectively ignore focus keyevents
157167
@@ -195,17 +205,17 @@
195 void ResetPreedit();205 void ResetPreedit();
196 /** Send out a request to blink the cursor if necessary */206 /** Send out a request to blink the cursor if necessary */
197 void QueueCursorBlink();207 void QueueCursorBlink();
198 static bool CursorBlinkCallback(TextEntry *data);208 static bool CursorBlinkCallback(TextEntry* data);
199209
200 void ShowCursor();210 void ShowCursor();
201 void HideCursor();211 void HideCursor();
202212
203 /** Draw the Cursor to the canvas */213 /** Draw the Cursor to the canvas */
204 void DrawCursor(CairoGraphics *canvas);214 void DrawCursor(CairoGraphics* canvas);
205 /** Draw the text to the canvas */215 /** Draw the text to the canvas */
206 void DrawText(CairoGraphics *canvas);216 void DrawText(CairoGraphics* canvas);
207217
208 void GetCursorRects(Rect *strong, Rect *weak);218 void GetCursorRects(Rect* strong, Rect* weak);
209219
210 void UpdateCursorRegion();220 void UpdateCursorRegion();
211221
@@ -230,7 +240,7 @@
230 * coordinate in the layout */240 * coordinate in the layout */
231 int XYToTextIndex(int x, int y);241 int XYToTextIndex(int x, int y);
232 /** Get the offset range that is currently selected,in number of characters.*/242 /** Get the offset range that is currently selected,in number of characters.*/
233 bool GetSelectionBounds(int *start, int *end);243 bool GetSelectionBounds(int* start, int* end) const;
234 /** Set the offest range that should be selected, in number of characters. */244 /** Set the offest range that should be selected, in number of characters. */
235 void SetSelectionBounds(int selection_bound, int cursor);245 void SetSelectionBounds(int selection_bound, int cursor);
236246
@@ -247,7 +257,7 @@
247 int GetPrevCharLength(int index);257 int GetPrevCharLength(int index);
248258
249 /** Insert text at current caret position */259 /** Insert text at current caret position */
250 void EnterText(const char *str);260 void EnterText(const char* str);
251 /** Delete text in a specified range, in number of characters. */261 /** Delete text in a specified range, in number of characters. */
252 void DeleteText(int start, int end);262 void DeleteText(int start, int end);
253263
@@ -279,8 +289,8 @@
279 /**289 /**
280 * Gets the cursor location in pango layout. The unit is pixel.290 * Gets the cursor location in pango layout. The unit is pixel.
281 */291 */
282 void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height,292 void GetCursorLocationInLayout(int* strong_x, int* strong_y, int* strong_height,
283 int *weak_x, int *weak_y, int *weak_height);293 int* weak_x, int* weak_y, int* weak_height);
284294
285 /** The CairoCanvas which hold cairo_t inside */295 /** The CairoCanvas which hold cairo_t inside */
286 CairoGraphics* canvas_;296 CairoGraphics* canvas_;
@@ -293,7 +303,7 @@
293 /** The preedit text of the edit control */303 /** The preedit text of the edit control */
294 std::string preedit_;304 std::string preedit_;
295 /** Attribute list of the preedit text */305 /** Attribute list of the preedit text */
296 PangoAttrList *preedit_attrs_;306 PangoAttrList* preedit_attrs_;
297 /**307 /**
298 * The character that should be displayed in invisible mode.308 * The character that should be displayed in invisible mode.
299 * If this is empty, then the edit control is visible309 * If this is empty, then the edit control is visible
@@ -387,7 +397,7 @@
387 /** The font size of the text */397 /** The font size of the text */
388 double font_size_;398 double font_size_;
389399
390 cairo_font_options_t *font_options_;400 cairo_font_options_t* font_options_;
391 double font_dpi_;401 double font_dpi_;
392402
393 /** The text color of the edit control */403 /** The text color of the edit control */
@@ -404,6 +414,12 @@
404 std::list<Rect> last_cursor_region_;414 std::list<Rect> last_cursor_region_;
405 std::list<Rect> cursor_region_;415 std::list<Rect> cursor_region_;
406416
417#if defined(NUX_OS_LINUX)
418 IBusIMEContext* ime_;
419 friend class IBusIMEContext;
420#endif
421 bool ime_active_;
422
407 protected:423 protected:
408 bool text_input_mode_;424 bool text_input_mode_;
409 bool key_nav_mode_;425 bool key_nav_mode_;
410426
=== removed file 'Nux/TextEntryIM.cpp'
--- Nux/TextEntryIM.cpp 2012-02-12 01:55:02 +0000
+++ Nux/TextEntryIM.cpp 1970-01-01 00:00:00 +0000
@@ -1,2126 +0,0 @@
1/*
2 Copyright 2008 Google Inc.
3
4 Licensed under the Apache License, Version 2.0(the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17
18#include "Nux.h"
19#include "Layout.h"
20#include "HLayout.h"
21#include "VLayout.h"
22#include "Validator.h"
23
24#include "cairo/cairo.h"
25#include "pango/pango.h"
26#include "pango/pangocairo.h"
27#include "NuxImage/CairoGraphics.h"
28
29#include "TextEntryIM.h"
30#include "TextEntry.h"
31
32#if defined(NUX_OS_LINUX)
33#include <X11/cursorfont.h>
34#endif
35
36namespace nux
37{
38 static const int kInnerBorderX = 2;
39 static const int kInnerBorderY = 0; //1;
40 static const int kCursorBlinkTimeout = 400;
41 static const double kStrongCursorLineWidth = 1;
42 static const double kStrongCursorBarWidth = 1;
43 static const double kWeakCursorLineWidth = 3;
44 static const double kWeakCursorBarWidth = 3;
45 static const Color kStrongCursorColor(0.9f, 0.9f, 0.9f, 1.0f);
46 static const Color kWeakCursorColor(1.0f, 1.0f, 1.0f, 0.5f);
47 static const Color kDefaultTextColor(0, 0, 0, 1.0f);
48 static const Color kDefaultBackgroundColor(1, 1, 1, 1.0f);
49 static const Color kDefaultSelectionBackgroundColor(0.5, 0.5, 0.5, 1.0f);
50 static const Color kDefaultSelectionTextColor(1, 1, 1, 1.0f);
51 static const unsigned long long kTripleClickTimeout = 500;
52 static const std::string kDefaultFontName = "Ubuntu";
53
54 static unsigned long long GetCurrentTime()
55 {
56 GTimeVal tv;
57 g_get_current_time(&tv);
58 return static_cast<unsigned long long>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
59 }
60
61 static std::string CleanupLineBreaks(const char *source)
62 {
63 nuxAssert(source);
64 std::string result;
65 while (*source) {
66 if (*source == '\r') {
67 result += ' ';
68 if (source[1] == '\n')
69 source++;
70 } else if (*source == '\n') {
71 result += ' ';
72 } else {
73 result += *source;
74 }
75 source++;
76 }
77 return result;
78 }
79
80 NUX_IMPLEMENT_OBJECT_TYPE(TextEntryIM);
81
82 TextEntryIM::TextEntryIM(const char* text, NUX_FILE_LINE_DECL)
83 : View(NUX_FILE_LINE_PARAM)
84 , _size_match_text(true)
85 , _texture2D(nullptr)
86 , canvas_(nullptr)
87 , cached_layout_(nullptr)
88 , preedit_attrs_(nullptr)
89 , completion_color_(color::Gray)
90 , last_dblclick_time_(0)
91 , cursor_(0)
92 , preedit_cursor_(0)
93 , selection_bound_(0)
94 , scroll_offset_x_(0)
95 , scroll_offset_y_(0)
96 , cursor_blink_timer_(0)
97 , cursor_blink_status_(0)
98 , visible_(true)
99 , focused_(false)
100 , need_im_reset_(false)
101 , overwrite_(false)
102 , select_words_(false)
103 , select_lines_(false)
104 , button_(false)
105 , bold_(false)
106 , underline_(false)
107 , strikeout_(false)
108 , italic_(false)
109 , multiline_(false)
110 , wrap_(false)
111 , cursor_visible_(false)
112 , readonly_(false)
113 , content_modified_(false)
114 , selection_changed_(false)
115 , cursor_moved_(false)
116 , update_canvas_(true)
117 , font_family_("Ubuntu")
118 , font_size_(12)
119 , font_options_(cairo_font_options_create())
120 , font_dpi_(96.0)
121 , _text_color(color::White)
122 , align_(CairoGraphics::ALIGN_LEFT)
123#if defined(NUX_OS_LINUX)
124 , caret_cursor_(None)
125#endif
126 , ime_(new IBusIMEContext(this))
127 , ime_active_(false)
128 , text_input_mode_(false)
129 , key_nav_mode_(false)
130 {
131 cairo_font_options_set_antialias(font_options_, CAIRO_ANTIALIAS_SUBPIXEL);
132 cairo_font_options_set_hint_style(font_options_, CAIRO_HINT_STYLE_FULL);
133 cairo_font_options_set_hint_metrics(font_options_, CAIRO_HINT_METRICS_ON);
134 cairo_font_options_set_subpixel_order(font_options_, CAIRO_SUBPIXEL_ORDER_RGB);
135
136 mouse_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDown));
137 mouse_drag.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDrag));
138 mouse_up.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseUp));
139 mouse_double_click.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDoubleClick));
140 mouse_enter.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseEnter));
141 mouse_leave.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseLeave));
142
143 key_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvKeyEvent));
144
145 begin_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvStartKeyFocus));
146 end_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvEndKeyFocus));
147
148 SetMinimumSize(DEFAULT_WIDGET_WIDTH, PRACTICAL_WIDGET_HEIGHT);
149 SetText(text);
150
151 SetAcceptKeyboardEvent(true);
152 EnableDoubleClick(true);
153 }
154
155 TextEntryIM::~TextEntryIM()
156 {
157 if (cursor_blink_timer_)
158 g_source_remove(cursor_blink_timer_);
159
160 cairo_font_options_destroy(font_options_);
161 if (_texture2D)
162 _texture2D->UnReference();
163 }
164
165 void TextEntryIM::PreLayoutManagement()
166 {
167 View::PreLayoutManagement();
168 }
169
170 long TextEntryIM::PostLayoutManagement(long layoutResult)
171 {
172 long result = View::PostLayoutManagement(layoutResult);
173 MainDraw();
174 return result;
175 }
176
177 void TextEntryIM::GeometryChanged()
178 {
179
180 update_canvas_ = true;
181 View::GeometryChanged();
182
183 }
184
185 Area* TextEntryIM::FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type)
186 {
187 Area* area = View::FindAreaUnderMouse(mouse_position, event_type);
188
189 return area;
190 }
191
192 void TextEntryIM::ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
193 {
194 if (GetEventButton(button_flags) != 1 && event_type != NUX_MOUSE_MOVE)
195 return;
196
197 //ResetImContext();
198 //Event::Type type = event.GetType();
199
200 int X = static_cast<int>(x /*round(event.GetX())*/) - kInnerBorderX - scroll_offset_x_;
201 int Y = static_cast<int>(y /*round(event.GetY())*/) - kInnerBorderY - scroll_offset_y_;
202 int index = XYToTextIndex(X, Y);
203 int sel_start, sel_end;
204 GetSelectionBounds(&sel_start, &sel_end);
205
206 unsigned long long current_time = GetCurrentTime();
207
208 if ((event_type == NUX_MOUSE_PRESSED) && (current_time - last_dblclick_time_ <= kTripleClickTimeout))
209 {
210 SelectLine();
211 }
212 else if (event_type == NUX_MOUSE_DOUBLECLICK)
213 {
214 SelectWord();
215 last_dblclick_time_ = current_time;
216 }
217 else if (event_type == NUX_MOUSE_PRESSED)
218 {
219 if (key_flags & NUX_STATE_SHIFT)
220 {
221 // If current click position is inside the selection range, then just
222 // cancel the selection.
223 if (index > sel_start && index < sel_end)
224 SetCursor(index);
225 else if (index <= sel_start)
226 SetSelectionBounds(sel_end, index);
227 else if (index >= sel_end)
228 SetSelectionBounds(sel_start, index);
229 }
230 else
231 {
232 SetCursor(index);
233 }
234 }
235 else if (event_type == NUX_MOUSE_MOVE)
236 {
237 SetSelectionBounds(selection_bound_, index);
238 }
239
240 QueueRefresh(false, true);
241 //return EVENT_RESULT_HANDLED;
242 }
243
244 void TextEntryIM::ProcessKeyEvent(
245 unsigned long event_type , /*event type*/
246 unsigned long keysym , /*event keysym*/
247 unsigned long state , /*event state*/
248 const char* character , /*character*/
249 unsigned short keyCount /*key repeat count*/)
250 {
251
252 KeyEvent event((NuxEventType)event_type, keysym, 0, state);
253 ime_->FilterKeyEvent(event);
254
255 if (event_type == NUX_KEYDOWN)
256 text_input_mode_ = true;
257
258// GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent());
259// ASSERT(gdk_event);
260//
261// Event::Type type = event.GetType();
262 // Cause the cursor to stop blinking for a while.
263 cursor_blink_status_ = 4;
264
265// if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/)
266// {
267// need_im_reset_ = true;
268// QueueRefresh(false, true);
269// return EVENT_RESULT_HANDLED;
270// }
271
272 if (event_type == NUX_KEYUP)
273 return;
274
275 // we need to ignore some characters
276 if (keysym == NUX_VK_TAB)
277 return;
278
279 if (keysym == NUX_VK_ENTER || keysym == NUX_KP_ENTER)
280 {
281 activated.emit();
282 return;
283 }
284
285 unsigned int keyval = keysym;
286 bool shift = (state & NUX_STATE_SHIFT);
287 bool ctrl = (state & NUX_STATE_CTRL);
288
289 // DLOG("TextEntryIM::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl);
290
291 if (event_type == NUX_KEYDOWN)
292 {
293 if (keyval == NUX_VK_LEFT)
294 {
295 if (!ctrl)
296 MoveCursor(VISUALLY, -1, shift);
297 else
298 MoveCursor(WORDS, -1, shift);
299 }
300 else if (keyval == NUX_VK_RIGHT)
301 {
302 if (!ctrl)
303 MoveCursor(VISUALLY, 1, shift);
304 else
305 MoveCursor(WORDS, 1, shift);
306 }
307 else if (keyval == NUX_VK_UP)
308 {
309 MoveCursor(DISPLAY_LINES, -1, shift);
310 }
311 else if (keyval == NUX_VK_DOWN)
312 {
313 MoveCursor(DISPLAY_LINES, 1, shift);
314 }
315 else if (keyval == NUX_VK_HOME)
316 {
317 if (!ctrl)
318 MoveCursor(DISPLAY_LINE_ENDS, -1, shift);
319 else
320 MoveCursor(BUFFER, -1, shift);
321 }
322 else if (keyval == NUX_VK_END)
323 {
324 if (!ctrl)
325 MoveCursor(DISPLAY_LINE_ENDS, 1, shift);
326 else
327 MoveCursor(BUFFER, 1, shift);
328 }
329 else if (keyval == NUX_VK_PAGE_UP)
330 {
331 if (!ctrl)
332 MoveCursor(PAGES, -1, shift);
333 else
334 MoveCursor(BUFFER, -1, shift);
335 }
336 else if (keyval == NUX_VK_PAGE_DOWN)
337 {
338 if (!ctrl)
339 MoveCursor(PAGES, 1, shift);
340 else
341 MoveCursor(BUFFER, 1, shift);
342 }
343 else if (((keyval == NUX_VK_x) && ctrl && !shift) || ((keyval == NUX_VK_DELETE) && shift && !ctrl))
344 {
345 CutClipboard();
346 }
347 else if (((keyval == NUX_VK_c) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && ctrl && (!shift)))
348 {
349 CopyClipboard();
350 }
351 else if (((keyval == NUX_VK_v) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && shift && (!ctrl)))
352 {
353 PasteClipboard();
354 }
355 else if ((keyval == NUX_VK_a) && ctrl)
356 {
357 // Select all
358 int text_length = static_cast<int>(text_.length());
359 SetSelectionBounds(0, text_length);
360 QueueRefresh(false, true);
361 return;
362 }
363 else if (keyval == NUX_VK_BACKSPACE)
364 {
365 if (!ctrl)
366 BackSpace(VISUALLY);
367 else
368 BackSpace(WORDS);
369 }
370 else if ((keyval == NUX_VK_DELETE) && (!shift))
371 {
372 if (!ctrl)
373 Delete(VISUALLY);
374 else
375 Delete(WORDS);
376 }
377 else if ((keyval == NUX_VK_INSERT) && (!shift) && (!ctrl))
378 {
379 ToggleOverwrite();
380 }
381// else
382// {
383// return EVENT_RESULT_UNHANDLED;
384// }
385 }
386 else
387 { // EVENT_KEY_PRESS
388// if (keyval == GDK_Return || keyval == GDK_KP_Enter)
389// {
390// // If multiline_ is unset, just ignore new_line.
391// if (multiline_)
392// EnterText("\n");
393// else
394// return;
395// }
396// else
397// {
398// return;
399// }
400 }
401
402 if (character != 0 && (strlen(character) != 0) && !ime_active_)
403 {
404 EnterText(character);
405 }
406
407 QueueRefresh(false, true);
408 return;
409 }
410
411 void TextEntryIM::RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags)
412 {
413 ProcessMouseEvent(NUX_MOUSE_DOUBLECLICK, x, y, 0, 0, button_flags, key_flags);
414 }
415
416 void TextEntryIM::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags)
417 {
418 ProcessMouseEvent(NUX_MOUSE_RELEASED, x, y, 0, 0, button_flags, key_flags);
419 }
420
421 void TextEntryIM::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags)
422 {
423 ProcessMouseEvent(NUX_MOUSE_PRESSED, x, y, 0, 0, button_flags, key_flags);
424 }
425
426 void TextEntryIM::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
427 {
428 ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags);
429 }
430
431 void TextEntryIM::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
432 {
433#if defined(NUX_OS_LINUX)
434 if (caret_cursor_ == None)
435 {
436 Display* display = nux::GetGraphicsDisplay()->GetX11Display();
437 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());
438
439 if (display && window)
440 {
441 caret_cursor_ = XCreateFontCursor(display, XC_xterm);
442 XDefineCursor(display, window->GetInputWindowId(), caret_cursor_);
443 }
444 }
445#endif
446 }
447
448 void TextEntryIM::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
449 {
450#if defined(NUX_OS_LINUX)
451 if (caret_cursor_ != None)
452 {
453 Display* display = nux::GetGraphicsDisplay()->GetX11Display();
454 nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());
455
456 if (display && window)
457 {
458 XUndefineCursor(display, window->GetInputWindowId());
459 XFreeCursor(display, caret_cursor_);
460 caret_cursor_ = None;
461 }
462 }
463#endif
464 }
465
466 void TextEntryIM::RecvKeyEvent(
467 unsigned long eventType , /*event type*/
468 unsigned long keysym , /*event keysym*/
469 unsigned long state , /*event state*/
470 const char* character , /*character*/
471 unsigned short keyCount /*key repeat count*/)
472 {
473 ProcessKeyEvent(eventType, keysym, state, character, keyCount);
474 }
475
476 void TextEntryIM::RecvStartKeyFocus()
477 {
478 key_nav_mode_ = true;
479 text_input_mode_ = false;
480
481 FocusInx();
482 }
483
484 void TextEntryIM::RecvEndKeyFocus()
485 {
486 key_nav_mode_ = false;
487 text_input_mode_ = false;
488
489 FocusOutx();
490 }
491
492 void TextEntryIM::Draw(GraphicsEngine& gfxContext, bool forceDraw)
493 {
494 MainDraw();
495 Geometry base = GetGeometry();
496
497 gfxContext.PushClippingRectangle(base);
498
499 nux::GetPainter().PaintBackground(gfxContext, base);
500
501 Color col = color::Black;
502 col.alpha = 0;
503 gfxContext.QRP_Color(base.x,
504 base.y,
505 base.width,
506 base.height,
507 col);
508
509 TexCoordXForm texxform;
510 texxform.SetWrap(TEXWRAP_REPEAT, TEXWRAP_REPEAT);
511 texxform.SetTexCoordType(TexCoordXForm::OFFSET_COORD);
512 gfxContext.QRP_1Tex(base.x,
513 base.y,
514 base.width,
515 base.height,
516 _texture2D->GetDeviceTexture(),
517 texxform,
518 _text_color);
519
520 gfxContext.PopClippingRectangle();
521 }
522
523 void TextEntryIM::DrawContent(GraphicsEngine& gfxContext, bool forceDraw)
524 {
525 //MainDraw();
526 }
527
528 void TextEntryIM::PostDraw(GraphicsEngine& gfxContext, bool forceDraw)
529 {
530 // intentionally left empty
531 }
532
533 void TextEntryIM::SetText(const char *text)
534 {
535 const char *end = NULL;
536 g_utf8_validate(text, -1, &end);
537
538 std::string txt((text && *text && end > text) ? std::string(text, end) : "");
539 if (txt == text_)
540 return; // prevent some redraws
541
542 text_ = multiline_ ? txt : CleanupLineBreaks(txt.c_str());
543 cursor_ = 0;
544 selection_bound_ = 0;
545 need_im_reset_ = true;
546 //ResetImContext();
547 QueueRefresh(true, true);
548 sigTextChanged.emit(this);
549 }
550
551 std::string const& TextEntryIM::GetText() const
552 {
553 return text_;
554 }
555
556 void TextEntryIM::SetCompletion(const char *text)
557 {
558 const char *end = NULL;
559 g_utf8_validate(text, -1, &end);
560 std::string txt((text && *text && end > text) ? std::string(text, end) : "");
561 if (txt == completion_)
562 return;
563
564 completion_ = txt;
565 QueueRefresh(true, true);
566 }
567
568 std::string const& TextEntryIM::GetCompletion() const
569 {
570 return completion_;
571 }
572
573 void TextEntryIM::SetCompletionColor(const Color &color)
574 {
575 completion_color_ = color;
576 QueueRefresh(true, true);
577 }
578
579 Color const& TextEntryIM::GetCompletionColor() const
580 {
581 return completion_color_;
582 }
583
584 void TextEntryIM::SetTextColor(const Color &text_color)
585 {
586 if (_text_color != text_color)
587 {
588 _text_color = text_color;
589 QueueRefresh(true, true);
590 }
591 }
592
593 Color const& TextEntryIM::GetTextColor() const
594 {
595 return _text_color;
596 }
597
598
599 void TextEntryIM::MainDraw()
600 {
601
602 CairoGraphics *edit_canvas = EnsureCanvas();
603
604 if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty())
605 {
606 edit_canvas->PushState();
607 DrawText(edit_canvas);
608 edit_canvas->PopState();
609 }
610
611// if (background_)
612// background_->Draw(canvas, 0, 0, GetBaseWidth, GetBaseHeight);
613
614 CairoGraphics* final_canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, edit_canvas->GetWidth(), edit_canvas->GetHeight());
615
616 final_canvas->PushState();
617 final_canvas->IntersectRectClipRegion(kInnerBorderX,
618 kInnerBorderY,
619 GetBaseWidth() - kInnerBorderX,
620 GetBaseHeight() - kInnerBorderY);
621 final_canvas->DrawCanvas(0, 0, edit_canvas);
622 final_canvas->PopState();
623 DrawCursor(final_canvas);
624
625 update_canvas_ = false;
626 last_selection_region_ = selection_region_;
627 last_cursor_region_ = cursor_region_;
628
629 NBitmapData* bitmap = final_canvas->GetBitmap();
630 delete final_canvas;
631
632 if (!_texture2D || _texture2D->GetWidth() != bitmap->GetWidth() || _texture2D->GetHeight() != bitmap->GetHeight())
633 {
634 if (_texture2D)
635 _texture2D->UnReference();
636 _texture2D = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture();
637 }
638
639 _texture2D->Update(bitmap);
640 delete bitmap;
641 }
642
643 void TextEntryIM::FocusInx()
644 {
645 if (!focused_)
646 {
647 focused_ = true;
648 if (!readonly_ /*&& im_context_*/)
649 {
650 need_im_reset_ = true;
651 ime_->Focus();
652 //gtk_im_context_focus_in(im_context_);
653 //UpdateIMCursorLocation();
654 }
655 cursor_visible_ = true; // show cursor when getting focus
656 selection_changed_ = true;
657 cursor_moved_ = true;
658 // Don't adjust scroll.
659 QueueRefresh(true, false);
660 }
661 }
662
663 void TextEntryIM::FocusOutx()
664 {
665 if (focused_)
666 {
667 focused_ = false;
668 if (!readonly_ /*&& im_context_*/)
669 {
670 need_im_reset_ = true;
671 ime_->Blur();
672 //gtk_im_context_focus_out(im_context_);
673 }
674 cursor_visible_ = false; // hide cursor when losing focus
675 selection_changed_ = true;
676 cursor_moved_ = true;
677 // Don't adjust scroll.
678 QueueRefresh(true, false);
679 }
680 }
681
682 CairoGraphics* TextEntryIM::EnsureCanvas()
683 {
684 if (canvas_)
685 {
686 if ((GetBaseWidth() == canvas_->GetWidth()) && (GetBaseHeight() == canvas_->GetHeight()))
687 {
688 return canvas_;
689 }
690 else
691 {
692 nuxDebugMsg("[TextEntryIM::EnsureCanvas] Recreate canvas");
693 delete canvas_;
694 canvas_ = NULL;
695 }
696 }
697 canvas_ = new CairoGraphics(CAIRO_FORMAT_ARGB32, GetBaseWidth(), GetBaseHeight());
698 nuxAssert(canvas_);
699 return canvas_;
700 }
701
702 void TextEntryIM::AdjustScroll()
703 {
704 int old_offset_x = scroll_offset_x_;
705 int old_offset_y = scroll_offset_y_;
706 int display_width = GetBaseWidth() - kInnerBorderX * 2;
707 int display_height = GetBaseHeight() - kInnerBorderY * 2;
708
709 PangoLayout *layout = EnsureLayout();
710 int text_width, text_height;
711 pango_layout_get_pixel_size(layout, &text_width, &text_height);
712
713 int strong_x, strong_y, strong_height;
714 int weak_x, weak_y, weak_height;
715 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height,
716 &weak_x, &weak_y, &weak_height);
717
718 if (!wrap_ && display_width > text_width)
719 {
720 PangoAlignment align = pango_layout_get_alignment(layout);
721 if (align == PANGO_ALIGN_RIGHT)
722 scroll_offset_x_ = display_width - text_width;
723 else if (align == PANGO_ALIGN_CENTER)
724 scroll_offset_x_ = (display_width - text_width) / 2;
725 else
726 scroll_offset_x_ = 0;
727 }
728 else
729 {
730 if (scroll_offset_x_ + strong_x < 0)
731 scroll_offset_x_ = -strong_x;
732 else if (scroll_offset_x_ + strong_x > display_width)
733 scroll_offset_x_ = display_width - strong_x;
734
735 if (std::abs(weak_x - strong_x) < display_width)
736 {
737 if (scroll_offset_x_ + weak_x < 0)
738 scroll_offset_x_ = - weak_x;
739 else if (scroll_offset_x_ + weak_x > display_width)
740 scroll_offset_x_ = display_width - weak_x;
741 }
742 }
743
744 if (display_height > text_height)
745 {
746 scroll_offset_y_ = 0;
747 }
748 else
749 {
750 if (scroll_offset_y_ + strong_y + strong_height > display_height)
751 scroll_offset_y_ = display_height - strong_y - strong_height;
752 if (scroll_offset_y_ + strong_y < 0)
753 scroll_offset_y_ = -strong_y;
754 }
755
756 if (old_offset_x != scroll_offset_x_ || old_offset_y != scroll_offset_y_)
757 content_modified_ = true;
758 }
759
760 void TextEntryIM::QueueRefresh(bool relayout, bool adjust_scroll)
761 {
762 if (relayout)
763 ResetLayout();
764
765 if (adjust_scroll)
766 AdjustScroll();
767
768 QueueTextDraw();
769 QueueCursorBlink();
770 }
771
772 void TextEntryIM::ResetImContext()
773 {
774 if (need_im_reset_)
775 {
776 need_im_reset_ = false;
777// if (im_context_)
778// gtk_im_context_reset(im_context_);
779 ResetPreedit();
780 }
781 }
782
783 void TextEntryIM::ResetPreedit() {
784 // Reset layout if there were some content in preedit string
785 if (preedit_.length())
786 ResetLayout();
787
788 preedit_.clear();
789 preedit_cursor_ = 0;
790 if (preedit_attrs_) {
791 pango_attr_list_unref(preedit_attrs_);
792 preedit_attrs_ = NULL;
793 }
794 }
795
796 void TextEntryIM::DrawText(CairoGraphics *canvas)
797 {
798 PangoLayout *layout = EnsureLayout();
799
800 bool redraw_text = false;
801 if (update_canvas_)
802 {
803 canvas->ClearCanvas();
804 canvas->PushState();
805 redraw_text = true;
806 }
807 else if (!last_selection_region_.empty())
808 {
809 //last_selection_region_.Integerize();
810 canvas->PushState();
811 canvas->IntersectGeneralClipRegion(last_selection_region_);
812 canvas->ClearRect(0, 0, GetBaseWidth(), GetBaseHeight());
813 redraw_text = true;
814 }
815
816 if (redraw_text)
817 {
818 cairo_set_source_rgb(canvas->GetInternalContext(),
819 _text_color.red,
820 _text_color.green,
821 _text_color.blue);
822
823 cairo_move_to(canvas->GetInternalContext(),
824 scroll_offset_x_ + kInnerBorderX,
825 scroll_offset_y_ + kInnerBorderY);
826
827 pango_cairo_show_layout(canvas->GetInternalContext(), layout);
828
829 canvas->PopState();
830 }
831
832 // Draw selection background.
833 // Selection in a single line may be not continual, so we use pango to
834 // get the x-ranges of each selection range in one line, and draw them
835 // separately.
836 if (!selection_region_.empty())
837 {
838 canvas->PushState();
839 //selection_region_.Integerize();
840 canvas->IntersectGeneralClipRegion(selection_region_);
841
842 Color selection_color = GetSelectionBackgroundColor();
843 Color text_color = GetSelectionTextColor();
844
845 cairo_set_source_rgb(canvas->GetInternalContext(),
846 selection_color.red,
847 selection_color.green,
848 selection_color.blue);
849 cairo_paint(canvas->GetInternalContext());
850
851 cairo_move_to(canvas->GetInternalContext(),
852 scroll_offset_x_ + kInnerBorderX,
853 scroll_offset_y_ + kInnerBorderY);
854 cairo_set_source_rgb(canvas->GetInternalContext(),
855 text_color.red,
856 text_color.green,
857 text_color.blue);
858 pango_cairo_show_layout(canvas->GetInternalContext(), layout);
859 canvas->PopState();
860 }
861 }
862
863 bool TextEntryIM::CursorBlinkCallback(TextEntryIM *self)
864 {
865 if (self->cursor_blink_status_)
866 self->ShowCursor();
867 else
868 self->HideCursor();
869
870 if (--self->cursor_blink_status_ < 0)
871 self->cursor_blink_status_ = 2;
872
873 return true;
874 }
875
876 void TextEntryIM::QueueCursorBlink()
877 {
878 if (!cursor_blink_timer_)
879 cursor_blink_timer_ = g_timeout_add(kCursorBlinkTimeout,
880 (GSourceFunc)&CursorBlinkCallback,
881 this);
882 }
883
884 void TextEntryIM::ShowCursor()
885 {
886 if (!cursor_visible_)
887 {
888 cursor_visible_ = true;
889 if (focused_ && !readonly_)
890 {
891 cursor_moved_ = true;
892 QueueRefresh(false, false);
893 }
894 }
895 }
896
897 void TextEntryIM::HideCursor()
898 {
899 if (cursor_visible_)
900 {
901 cursor_visible_ = false;
902 if (focused_ && !readonly_)
903 {
904 cursor_moved_ = true;
905 QueueRefresh(false, false);
906 }
907 }
908 }
909
910 void TextEntryIM::GetCursorRects(Rect *strong, Rect *weak)
911 {
912 int strong_x, strong_y, strong_height;
913 int weak_x, weak_y, weak_height;
914 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height,
915 &weak_x, &weak_y, &weak_height);
916
917 strong->x =
918 strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth;
919 strong->width = kStrongCursorBarWidth * 2;
920 strong->y = strong_y + kInnerBorderY + scroll_offset_y_;
921 strong->height = strong_height;
922
923 if (weak_x != strong_x)
924 {
925 weak->x = weak_x+ kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth;
926 weak->width = kWeakCursorBarWidth * 2;
927 weak->y = weak_y+ kInnerBorderY + scroll_offset_y_;
928 weak->height = weak_height;
929 }
930 else
931 {
932 *weak = *strong;
933 }
934 }
935
936 void TextEntryIM::UpdateCursorRegion()
937 {
938 cursor_region_.clear();
939
940 Rect strong, weak;
941 GetCursorRects(&strong, &weak);
942
943 cursor_region_.push_back(strong);
944 cursor_region_.push_back(weak);
945 }
946
947
948 void TextEntryIM::DrawCursor(CairoGraphics *canvas)
949 {
950 if (!cursor_visible_)
951 return;
952
953 int strong_x, strong_y, strong_height;
954 int weak_x, weak_y, weak_height;
955 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height,
956 &weak_x, &weak_y, &weak_height);
957
958 // Draw strong cursor.
959 // 0.5 is for cairo drawing between the grid
960 canvas->DrawLine(strong_x + kInnerBorderX + scroll_offset_x_ + 0.5,
961 strong_y + kInnerBorderY + scroll_offset_y_,
962 strong_x + kInnerBorderX + scroll_offset_x_ + 0.5,
963 strong_y + strong_height + kInnerBorderY + scroll_offset_y_,
964 kStrongCursorLineWidth, kStrongCursorColor);
965 // Draw a small arror towards weak cursor
966 if (strong_x > weak_x)
967 {
968 canvas->DrawLine(
969 strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth,
970 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth,
971 strong_x + kInnerBorderX + scroll_offset_x_,
972 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth,
973 kStrongCursorLineWidth, kStrongCursorColor);
974 }
975 else if (strong_x < weak_x)
976 {
977 canvas->DrawLine(
978 strong_x + kInnerBorderX + scroll_offset_x_,
979 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth,
980 strong_x + kInnerBorderX + scroll_offset_x_ + kStrongCursorBarWidth,
981 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth,
982 kStrongCursorLineWidth, kStrongCursorColor);
983 }
984
985 if (strong_x != weak_x )
986 {
987 // Draw weak cursor.
988 canvas->DrawLine(weak_x + kInnerBorderX + scroll_offset_x_,
989 weak_y + kInnerBorderY + scroll_offset_y_,
990 weak_x + kInnerBorderX + scroll_offset_x_,
991 weak_y + weak_height + kInnerBorderY + scroll_offset_y_,
992 kWeakCursorLineWidth, kWeakCursorColor);
993 // Draw a small arror towards strong cursor
994 if (weak_x > strong_x)
995 {
996 canvas->DrawLine(
997 weak_x + kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth,
998 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth,
999 weak_x + kInnerBorderX + scroll_offset_x_,
1000 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth,
1001 kWeakCursorLineWidth, kWeakCursorColor);
1002 }
1003 else
1004 {
1005 canvas->DrawLine(
1006 weak_x + kInnerBorderX + scroll_offset_x_,
1007 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth,
1008 weak_x + kInnerBorderX + scroll_offset_x_ + kWeakCursorBarWidth,
1009 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth,
1010 kWeakCursorLineWidth, kWeakCursorColor);
1011 }
1012 }
1013 }
1014
1015 Color TextEntryIM::GetSelectionBackgroundColor()
1016 {
1017 return kDefaultSelectionBackgroundColor;
1018 }
1019
1020 Color TextEntryIM::GetSelectionTextColor()
1021 {
1022 return kDefaultSelectionTextColor;
1023 }
1024
1025 void TextEntryIM::GetCursorLocationInLayout(int *strong_x, int *strong_y,
1026 int *strong_height,
1027 int *weak_x, int *weak_y,
1028 int *weak_height)
1029 {
1030 PangoLayout *layout = EnsureLayout();
1031 int cursor_index = TextIndexToLayoutIndex(cursor_, true);
1032
1033 PangoRectangle strong, weak;
1034 pango_layout_get_cursor_pos(layout, cursor_index, &strong, &weak);
1035
1036 if (strong_x)
1037 *strong_x = PANGO_PIXELS(strong.x);
1038 if (strong_y)
1039 *strong_y = PANGO_PIXELS(strong.y);
1040 if (strong_height)
1041 *strong_height = PANGO_PIXELS(strong.height);
1042 if (weak_x)
1043 *weak_x = PANGO_PIXELS(weak.x);
1044 if (weak_y)
1045 *weak_y = PANGO_PIXELS(weak.y);
1046 if (weak_height)
1047 *weak_height = PANGO_PIXELS(weak.height);
1048 }
1049
1050 PangoLayout* TextEntryIM::EnsureLayout()
1051 {
1052 if (!cached_layout_)
1053 {
1054 cached_layout_ = CreateLayout();
1055 }
1056 return cached_layout_;
1057 }
1058
1059 void TextEntryIM::QueueTextDraw()
1060 {
1061 if (content_modified_)
1062 {
1063 UpdateSelectionRegion();
1064 UpdateCursorRegion();
1065 QueueDraw(); //owner->QueueDraw();
1066 content_modified_ = false;
1067 update_canvas_ = true;
1068 }
1069 else
1070 {
1071 if (selection_changed_)
1072 {
1073 UpdateSelectionRegion();
1074 if (!last_selection_region_.empty())
1075 QueueDraw(); //owner_->QueueDrawRegion(last_selection_region_);
1076 if (!selection_region_.empty())
1077 QueueDraw(); //owner_->QueueDrawRegion(selection_region_);
1078 selection_changed_ = false;
1079 }
1080 if (cursor_moved_)
1081 {
1082 UpdateCursorRegion();
1083 if (!last_cursor_region_.empty())
1084 QueueDraw(); //owner_->QueueDrawRegion(last_cursor_region_);
1085 if (!cursor_region_.empty())
1086 QueueDraw(); //owner_->QueueDrawRegion(cursor_region_);
1087 cursor_moved_ = false;
1088 }
1089 }
1090 }
1091
1092 void TextEntryIM::ResetLayout()
1093 {
1094 if (cached_layout_)
1095 {
1096 g_object_unref(cached_layout_);
1097 cached_layout_ = NULL;
1098 content_modified_ = true;
1099 }
1100 }
1101
1102 PangoLayout* TextEntryIM::CreateLayout()
1103 {
1104 // Creates the pango layout with a temporary canvas that is not zoomed.
1105 CairoGraphics *canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, 1, 1);
1106 PangoLayout *layout = pango_cairo_create_layout(canvas->GetInternalContext());
1107 delete canvas;
1108 PangoAttrList *tmp_attrs = pango_attr_list_new();
1109 std::string tmp_string;
1110
1111 /* Set necessary parameters */
1112 pango_cairo_context_set_font_options(pango_layout_get_context(layout),
1113 font_options_);
1114 pango_cairo_context_set_resolution(pango_layout_get_context(layout),
1115 font_dpi_);
1116
1117 if (wrap_)
1118 {
1119 pango_layout_set_width(layout, (GetBaseWidth() - kInnerBorderX * 2) * PANGO_SCALE);
1120 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
1121 }
1122 else
1123 {
1124 pango_layout_set_width(layout, -1);
1125 }
1126
1127 pango_layout_set_single_paragraph_mode(layout, !multiline_);
1128
1129 if (preedit_.length())
1130 {
1131 size_t cursor_index = static_cast<size_t>(cursor_);
1132 size_t text_length = text_.length();
1133 size_t preedit_length = preedit_.length();
1134 if (visible_)
1135 {
1136 tmp_string = text_;
1137 tmp_string.insert(cursor_index, preedit_);
1138 }
1139 else
1140 {
1141 size_t nchars = g_utf8_strlen(text_.c_str(), text_length);
1142 size_t preedit_nchars = g_utf8_strlen(preedit_.c_str(), preedit_length);
1143 nchars += preedit_nchars;
1144 tmp_string.reserve(password_char_.length() * nchars);
1145 for (size_t i = 0; i < nchars; ++i)
1146 tmp_string.append(password_char_);
1147 size_t cursor_offset =
1148 g_utf8_pointer_to_offset(text_.c_str(), text_.c_str() + cursor_index);
1149 /* Fix cursor index and preedit_length */
1150 cursor_index = cursor_offset * password_char_.length();
1151 preedit_length = preedit_nchars * password_char_.length();
1152 }
1153 if (preedit_attrs_)
1154 pango_attr_list_splice(tmp_attrs, preedit_attrs_,
1155 static_cast<int>(cursor_index),
1156 static_cast<int>(preedit_length));
1157 }
1158 else
1159 {
1160 if (visible_)
1161 {
1162 tmp_string = text_;
1163 }
1164 else
1165 {
1166 size_t nchars = g_utf8_strlen(text_.c_str(), text_.length());
1167 tmp_string.reserve(password_char_.length() * nchars);
1168 for (size_t i = 0; i < nchars; ++i)
1169 tmp_string.append(password_char_);
1170 }
1171 }
1172
1173 int pre_completion_length = tmp_string.length();
1174
1175 if (!completion_.empty() && !wrap_)
1176 {
1177 tmp_string = text_ + completion_;
1178 }
1179
1180 pango_layout_set_text(layout, tmp_string.c_str(),
1181 static_cast<int>(tmp_string.length()));
1182
1183 /* Set necessary attributes */
1184 PangoAttribute *attr;
1185 if (underline_)
1186 {
1187 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
1188 attr->start_index = 0;
1189 attr->end_index = static_cast<guint>(pre_completion_length);
1190 pango_attr_list_insert(tmp_attrs, attr);
1191 }
1192 if (strikeout_)
1193 {
1194 attr = pango_attr_strikethrough_new(TRUE);
1195 attr->start_index = 0;
1196 attr->end_index = static_cast<guint>(pre_completion_length);
1197 pango_attr_list_insert(tmp_attrs, attr);
1198 }
1199 if (!completion_.empty() && !wrap_)
1200 {
1201 attr = pango_attr_foreground_new(65535 * completion_color_.red,
1202 65535 * completion_color_.green,
1203 65535 * completion_color_.blue);
1204 attr->start_index = static_cast<guint>(pre_completion_length);
1205 attr->end_index = static_cast<guint>(tmp_string.length());
1206 pango_attr_list_insert(tmp_attrs, attr);
1207 }
1208 /* Set font desc */
1209 {
1210 /* safe to down_cast here, because we know the actual implementation. */
1211 CairoFont *font = new CairoFont(
1212 font_family_.empty() ? kDefaultFontName : font_family_.c_str(),
1213 font_size_,
1214 italic_ ? CairoFont::STYLE_ITALIC : CairoFont::STYLE_NORMAL,
1215 bold_ ? CairoFont::WEIGHT_BOLD : CairoFont::WEIGHT_NORMAL);
1216 nuxAssert(font);
1217 attr = pango_attr_font_desc_new(font->GetFontDescription());
1218 attr->start_index = 0;
1219 attr->end_index = static_cast<unsigned int>(tmp_string.length());
1220 pango_attr_list_insert(tmp_attrs, attr);
1221 pango_layout_set_font_description(layout, font->GetFontDescription());
1222 font->Destroy();
1223 }
1224 pango_layout_set_attributes(layout, tmp_attrs);
1225 pango_attr_list_unref(tmp_attrs);
1226
1227 /* Set alignment according to text direction. Only set layout's alignment
1228 * when it's not wrapped and in single line mode.
1229 */
1230 if (!wrap_ && pango_layout_get_line_count(layout) <= 1 &&
1231 align_ != CairoGraphics::ALIGN_CENTER)
1232 {
1233 PangoDirection dir;
1234 if (visible_)
1235 dir = pango_find_base_dir(tmp_string.c_str(),
1236 static_cast<int>(tmp_string.length()));
1237 else
1238 dir = PANGO_DIRECTION_NEUTRAL;
1239
1240 if (dir == PANGO_DIRECTION_NEUTRAL)
1241 {
1242// GtkWidget *widget = GetWidgetAndCursorLocation(NULL);
1243// if (widget && gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
1244// dir = PANGO_DIRECTION_RTL;
1245// else
1246// dir = PANGO_DIRECTION_LTR;
1247
1248 dir = PANGO_DIRECTION_LTR;
1249 }
1250
1251 // If wordWrap is false then "justify" alignment has no effect.
1252 PangoAlignment pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ?
1253 PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT);
1254
1255 // Invert the alignment if text direction is right to left.
1256 if (dir == PANGO_DIRECTION_RTL)
1257 {
1258 pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ?
1259 PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT);
1260 }
1261
1262 pango_layout_set_alignment(layout, pango_align);
1263 pango_layout_set_justify(layout, FALSE);
1264 }
1265 else if (align_ == CairoGraphics::ALIGN_JUSTIFY)
1266 {
1267 pango_layout_set_justify(layout, TRUE);
1268 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
1269 }
1270 else if (align_ == CairoGraphics::ALIGN_RIGHT)
1271 {
1272 pango_layout_set_justify(layout, FALSE);
1273 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
1274 }
1275 else if (align_ == CairoGraphics::ALIGN_CENTER)
1276 {
1277 pango_layout_set_justify(layout, FALSE);
1278 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
1279 }
1280 else
1281 {
1282 pango_layout_set_justify(layout, FALSE);
1283 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
1284 }
1285
1286 {
1287 PangoContext *context;
1288 PangoFontMetrics *metrics;
1289 int ascent, descent;
1290
1291 context = pango_layout_get_context(layout);
1292 metrics = pango_context_get_metrics(context,
1293 pango_layout_get_font_description(layout),
1294 pango_context_get_language(context));
1295
1296 ascent = pango_font_metrics_get_ascent(metrics);
1297 descent = pango_font_metrics_get_descent(metrics);
1298
1299 int full_height = PANGO_PIXELS(ascent + descent) + (kInnerBorderY * 2);
1300 SetMinimumHeight(full_height);
1301
1302 pango_font_metrics_unref(metrics);
1303 }
1304
1305 return layout;
1306 }
1307
1308 int TextEntryIM::TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor)
1309 {
1310 if (visible_)
1311 {
1312 if (text_index < cursor_)
1313 return text_index;
1314
1315 if (text_index == cursor_ && consider_preedit_cursor)
1316 return text_index + preedit_cursor_;
1317
1318 return text_index + static_cast<int>(preedit_.length());
1319 }
1320
1321 const char *text = text_.c_str();
1322 int offset = static_cast<int>(
1323 g_utf8_pointer_to_offset(text, text + text_index));
1324 int preedit_offset = 0;
1325 int preedit_chars = 0;
1326 if (preedit_.length())
1327 {
1328 const char *preedit_text = preedit_.c_str();
1329 preedit_offset = static_cast<int>(g_utf8_pointer_to_offset(
1330 preedit_text, preedit_text + preedit_cursor_));
1331 preedit_chars = static_cast<int>(g_utf8_strlen(
1332 preedit_text, preedit_.length()));
1333 }
1334
1335 int password_char_length = static_cast<int>(password_char_.length());
1336
1337 if (text_index < cursor_)
1338 return offset * password_char_length;
1339
1340 if (text_index == cursor_ && consider_preedit_cursor)
1341 return (offset + preedit_offset) * password_char_length;
1342
1343 return (offset + preedit_chars) * password_char_length;
1344 }
1345
1346
1347 int TextEntryIM::LayoutIndexToTextIndex(int layout_index)
1348 {
1349 if (visible_)
1350 {
1351 if (layout_index < cursor_)
1352 return layout_index;
1353
1354 int preedit_length = static_cast<int>(preedit_.length());
1355 if (layout_index >= cursor_ + preedit_length)
1356 return layout_index - preedit_length;
1357
1358 return cursor_;
1359 }
1360
1361 int password_char_length = static_cast<int>(password_char_.length());
1362 nuxAssert(layout_index % password_char_length == 0);
1363
1364 int offset = layout_index / password_char_length;
1365
1366 const char *text = text_.c_str();
1367 int cursor_offset = static_cast<int>(
1368 g_utf8_pointer_to_offset(text, text + cursor_));
1369 int preedit_chars = static_cast<int>(
1370 g_utf8_strlen(preedit_.c_str(), preedit_.length()));
1371
1372 if (offset < cursor_offset)
1373 return static_cast<int>(g_utf8_offset_to_pointer(text, offset) - text);
1374
1375 if (offset >= cursor_offset + preedit_chars)
1376 return static_cast<int>(
1377 g_utf8_offset_to_pointer(text, offset - preedit_chars) - text);
1378
1379 return cursor_;
1380 }
1381
1382 int TextEntryIM::GetCharLength(int index)
1383 {
1384 const char *text = text_.c_str();
1385 const char *ptr = text + index;
1386 const char *end = text + text_.length();
1387 const char *next = g_utf8_find_next_char(ptr, end);
1388 return static_cast<int>(next ? static_cast<int>(next - ptr) : end - ptr);
1389 }
1390
1391 int TextEntryIM::GetPrevCharLength(int index)
1392 {
1393 const char *text = text_.c_str();
1394 const char *ptr = text + index;
1395 const char *prev = g_utf8_find_prev_char(text, ptr);
1396 return static_cast<int>(prev ? static_cast<int>(ptr - prev) : ptr - text);
1397 }
1398
1399 void TextEntryIM::EnterText(const char *str)
1400 {
1401 if (readonly_ || !str || !*str) return;
1402
1403 if (GetSelectionBounds(NULL, NULL))
1404 {
1405 DeleteSelection();
1406 }
1407 else if (overwrite_ && cursor_ != static_cast<int>(text_.length()))
1408 {
1409 DeleteText(cursor_, cursor_ + GetCharLength(cursor_));
1410 }
1411
1412 std::string tmp_text;
1413 if (!multiline_)
1414 {
1415 tmp_text = CleanupLineBreaks(str);
1416 str = tmp_text.c_str();
1417 }
1418
1419 const char *end = NULL;
1420 g_utf8_validate(str, -1, &end);
1421 if (end > str)
1422 {
1423 size_t len = end - str;
1424
1425 text_.insert(cursor_, str, len);
1426 cursor_ += static_cast<int>(len);
1427 selection_bound_ += static_cast<int>(len);
1428 }
1429
1430 ResetLayout();
1431 sigTextChanged.emit(this);
1432 }
1433
1434 void TextEntryIM::DeleteText(int start, int end)
1435 {
1436 if (readonly_) return;
1437
1438 int text_length = static_cast<int>(text_.length());
1439 if (start < 0)
1440 start = 0;
1441 else if (start > text_length)
1442 start = text_length;
1443
1444 if (end < 0)
1445 end = 0;
1446 else if (end > text_length)
1447 end = text_length;
1448
1449 if (start > end)
1450 std::swap(start, end);
1451 else if (start == end)
1452 return;
1453
1454 text_.erase(start, end - start);
1455
1456 if (cursor_ >= end)
1457 cursor_ -= (end - start);
1458 if (selection_bound_ >= end)
1459 selection_bound_ -= (end - start);
1460
1461 ResetLayout();
1462 sigTextChanged.emit(this);
1463 }
1464
1465 void TextEntryIM::SelectWord()
1466 {
1467 int selection_bound = MoveWords(cursor_, -1);
1468 int cursor = MoveWords(selection_bound, 1);
1469 SetSelectionBounds(selection_bound, cursor);
1470 }
1471
1472 void TextEntryIM::SelectLine()
1473 {
1474 int selection_bound = MoveLineEnds(cursor_, -1);
1475 int cursor = MoveLineEnds(selection_bound, 1);
1476 SetSelectionBounds(selection_bound, cursor);
1477 }
1478
1479 void TextEntryIM::Select(int start, int end) {
1480 int text_length = static_cast<int>(text_.length());
1481 if (start == -1)
1482 start = text_length;
1483 if (end == -1)
1484 end = text_length;
1485
1486 start = Clamp(start, 0, text_length);
1487 end = Clamp(end, 0, text_length);
1488 SetSelectionBounds(start, end);
1489 QueueRefresh(false, true);
1490 }
1491
1492 void TextEntryIM::SelectAll() {
1493 SetSelectionBounds(0, static_cast<int>(text_.length()));
1494 QueueRefresh(false, true);
1495 }
1496
1497 CairoGraphics::Alignment TextEntryIM::GetAlign() const
1498 {
1499 return align_;
1500 }
1501
1502 void TextEntryIM::SetAlign(CairoGraphics::Alignment align)
1503 {
1504 align_ = align;
1505 QueueRefresh(true, true);
1506 }
1507
1508 bool TextEntryIM::im_active()
1509 {
1510 return ime_active_;
1511 }
1512
1513 void TextEntryIM::DeleteSelection()
1514 {
1515 int start, end;
1516 if (GetSelectionBounds(&start, &end))
1517 DeleteText(start, end);
1518 }
1519
1520 void TextEntryIM::CopyClipboard()
1521 {
1522// int start, end;
1523// if (GetSelectionBounds(&start, &end))
1524// {
1525// GtkWidget *widget = GetWidgetAndCursorLocation(NULL);
1526// if (widget)
1527// {
1528// if (visible_)
1529// {
1530// gtk_clipboard_set_text(
1531// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD),
1532// text_.c_str() + start, end - start);
1533// }
1534// else
1535// {
1536// // Don't copy real content if it's in invisible.
1537// std::string content;
1538// int nchars = static_cast<int>(
1539// g_utf8_strlen(text_.c_str() + start, end - start));
1540// for (int i = 0; i < nchars; ++i)
1541// content.append(password_char_);
1542// gtk_clipboard_set_text(
1543// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD),
1544// content.c_str(), static_cast<int>(content.length()));
1545// }
1546// }
1547// }
1548 }
1549
1550 void TextEntryIM::CutClipboard()
1551 {
1552 CopyClipboard();
1553 DeleteSelection();
1554 }
1555
1556 void TextEntryIM::PasteClipboard()
1557 {
1558// GtkWidget *widget = GetWidgetAndCursorLocation(NULL);
1559// if (widget)
1560// {
1561// gtk_clipboard_request_text(
1562// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD),
1563// PasteCallback, this);
1564// }
1565 }
1566
1567 void TextEntryIM::BackSpace(MovementStep step)
1568 {
1569 if (GetSelectionBounds(NULL, NULL))
1570 {
1571 DeleteSelection();
1572 }
1573 else
1574 {
1575 if (cursor_ == 0)
1576 return;
1577 if (step == VISUALLY)
1578 {
1579 DeleteText(cursor_ - GetPrevCharLength(cursor_), cursor_);
1580 }
1581 else if (step == WORDS)
1582 {
1583 int new_cursor;
1584 new_cursor = MoveWords(cursor_, -1);
1585 DeleteText(new_cursor, cursor_);
1586 }
1587 }
1588 }
1589
1590 void TextEntryIM::Delete(MovementStep step)
1591 {
1592 if (GetSelectionBounds(NULL, NULL))
1593 {
1594 DeleteSelection();
1595 }
1596 else
1597 {
1598 if (cursor_ == static_cast<int>(text_.length()))
1599 return;
1600 if (step == VISUALLY)
1601 {
1602 DeleteText(cursor_, cursor_ + GetCharLength(cursor_));
1603 }
1604 else if (step == WORDS)
1605 {
1606 int new_cursor;
1607 new_cursor = MoveWords(cursor_, 1);
1608 DeleteText(cursor_, new_cursor);
1609 }
1610 }
1611 }
1612
1613 void TextEntryIM::ToggleOverwrite()
1614 {
1615 overwrite_ = !overwrite_;
1616 }
1617
1618 void TextEntryIM::UpdateSelectionRegion()
1619 {
1620 selection_region_.clear();
1621
1622 // Selection in a single line may be not continual, so we use pango to
1623 // get the x-ranges of each selection range in one line, and draw them
1624 // separately.
1625 int start_index, end_index;
1626 if (GetSelectionBounds(&start_index, &end_index))
1627 {
1628 PangoLayout *layout = EnsureLayout();
1629 PangoRectangle line_extents, pos;
1630 int draw_start, draw_end;
1631 int *ranges;
1632 int n_ranges;
1633 int n_lines = pango_layout_get_line_count(layout);
1634
1635 start_index = TextIndexToLayoutIndex(start_index, false);
1636 end_index = TextIndexToLayoutIndex(end_index, false);
1637
1638 for (int line_index = 0; line_index < n_lines; ++line_index)
1639 {
1640#if PANGO_VERSION_CHECK(1,16,0)
1641 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index);
1642#else
1643 PangoLayoutLine *line = pango_layout_get_line(layout, line_index);
1644#endif
1645 if (line->start_index + line->length < start_index)
1646 continue;
1647 if (end_index < line->start_index)
1648 break;
1649 draw_start = Max<int>(start_index, line->start_index);
1650 draw_end = Min<int>(end_index, line->start_index + line->length);
1651 pango_layout_line_get_x_ranges(line, draw_start, draw_end,
1652 &ranges, &n_ranges);
1653 pango_layout_line_get_pixel_extents(line, NULL, &line_extents);
1654 pango_layout_index_to_pos(layout, line->start_index, &pos);
1655 for (int i = 0; i < n_ranges; ++i)
1656 {
1657 selection_region_.push_back(Rect(
1658 kInnerBorderX + scroll_offset_x_ + PANGO_PIXELS(ranges[i * 2]),
1659 kInnerBorderY + scroll_offset_y_ + PANGO_PIXELS(pos.y),
1660 PANGO_PIXELS(ranges[i * 2 + 1] - ranges[i * 2]),
1661 line_extents.height));
1662 }
1663 g_free(ranges);
1664 }
1665 }
1666 }
1667
1668 void TextEntryIM::MoveCursor(MovementStep step, int count, bool extend_selection)
1669 {
1670 if (ime_active_)
1671 return;
1672
1673 ResetImContext();
1674 int new_cursor = 0;
1675 // Clear selection first if not extend it.
1676 if (!extend_selection)
1677 {
1678 selection_changed_ = true;
1679 cursor_moved_ = true;
1680 selection_bound_ = cursor_;
1681 cursor_moved.emit(cursor_);
1682 }
1683
1684 // Calculate the new offset after motion.
1685 switch(step)
1686 {
1687 case VISUALLY:
1688 new_cursor = MoveVisually(cursor_, count);
1689 break;
1690 case WORDS:
1691 new_cursor = MoveWords(cursor_, count);
1692 break;
1693 case DISPLAY_LINES:
1694 new_cursor = MoveDisplayLines(cursor_, count);
1695 break;
1696 case DISPLAY_LINE_ENDS:
1697 new_cursor = MoveLineEnds(cursor_, count);
1698 break;
1699 case PAGES:
1700 new_cursor = MovePages(cursor_, count);
1701 break;
1702 case BUFFER:
1703 nuxAssert(count == -1 || count == 1);
1704 new_cursor = static_cast<int>(count == -1 ? 0 : text_.length());
1705 break;
1706 }
1707
1708 if (extend_selection)
1709 SetSelectionBounds(selection_bound_, new_cursor);
1710 else
1711 SetCursor(new_cursor);
1712
1713 QueueRefresh(true, true);
1714 }
1715
1716 int TextEntryIM::MoveVisually(int current_index, int count)
1717 {
1718 nuxAssert(current_index >= 0 &&
1719 current_index <= static_cast<int>(text_.length()));
1720 nuxAssert(count);
1721 nuxAssert(preedit_.length() == 0);
1722
1723 PangoLayout *layout = EnsureLayout();
1724 const char *text = pango_layout_get_text(layout);
1725 int index = TextIndexToLayoutIndex(current_index, false);
1726 int new_index = 0;
1727 int new_trailing = 0;
1728 while (count != 0)
1729 {
1730 if (count > 0)
1731 {
1732 --count;
1733 pango_layout_move_cursor_visually(layout, true, index, 0, 1,
1734 &new_index, &new_trailing);
1735 }
1736 else if (count < 0)
1737 {
1738 ++count;
1739 pango_layout_move_cursor_visually(layout, true, index, 0, -1,
1740 &new_index, &new_trailing);
1741 }
1742 index = new_index;
1743 if (index < 0 || index == G_MAXINT)
1744 return current_index;
1745 index = static_cast<int>(g_utf8_offset_to_pointer(text + index, new_trailing) - text);
1746 }
1747 return LayoutIndexToTextIndex(index);
1748 }
1749
1750 int TextEntryIM::MoveWords(int current_index, int count)
1751 {
1752 nuxAssert(current_index >= 0 &&
1753 current_index <= static_cast<int>(text_.length()));
1754 nuxAssert(count);
1755 nuxAssert(preedit_.length() == 0);
1756
1757 if (!visible_)
1758 {
1759 return static_cast<int>(count > 0 ? text_.length() : 0);
1760 }
1761
1762 // The cursor movement direction shall be determined by the direction of
1763 // current text line.
1764 PangoLayout *layout = EnsureLayout();
1765 int n_log_attrs;
1766 PangoLogAttr *log_attrs;
1767 pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs);
1768 const char *text = pango_layout_get_text(layout);
1769 int index = TextIndexToLayoutIndex(current_index, false);
1770 int line_index;
1771 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL);
1772
1773 // Weird bug: line_index here may be >= than line count?
1774 int line_count = pango_layout_get_line_count(layout);
1775 if (line_index >= line_count)
1776 {
1777 line_index = line_count - 1;
1778 }
1779
1780#if PANGO_VERSION_CHECK(1,16,0)
1781 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index);
1782#else
1783 PangoLayoutLine *line = pango_layout_get_line(layout, line_index);
1784#endif
1785 bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL);
1786 const char *ptr = text + index;
1787 int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr));
1788 while (count != 0)
1789 {
1790 if (((rtl && count < 0) || (!rtl && count > 0)) && *ptr)
1791 {
1792 if (log_attrs[offset].is_white)
1793 {
1794 while (ptr && *ptr && log_attrs[offset].is_white)
1795 {
1796 ptr = g_utf8_find_next_char(ptr, NULL);
1797 ++offset;
1798 }
1799 }
1800 else
1801 {
1802 if (ptr && *ptr)
1803 {
1804 ptr = g_utf8_find_next_char(ptr, NULL);
1805 ++offset;
1806 }
1807 }
1808 while (ptr && *ptr)
1809 {
1810 ptr = g_utf8_find_next_char(ptr, NULL);
1811 ++offset;
1812 if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end)
1813 break;
1814 }
1815 if (!ptr)
1816 {
1817 ptr = text;
1818 while (*ptr) ++ptr;
1819 }
1820 }
1821 else if (((rtl && count > 0) || (!rtl && count < 0)) && (ptr > text))
1822 {
1823 if (offset > 0 && log_attrs[offset - 1].is_white)
1824 {
1825 while (ptr && offset > 0 && log_attrs[offset - 1].is_white)
1826 {
1827 ptr = g_utf8_find_prev_char(text, ptr);
1828 --offset;
1829 }
1830 }
1831 else
1832 {
1833 if (ptr)
1834 {
1835 ptr = g_utf8_find_prev_char(text, ptr);
1836 --offset;
1837 }
1838 }
1839 while (ptr /*&& *ptr*/) //fix: when at the end of the string, allow ctrl+left arrow to move backward to the start/end of the previous word.
1840 {
1841 ptr = g_utf8_find_prev_char(text, ptr);
1842 --offset;
1843 if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end)
1844 break;
1845 }
1846 if (!ptr)
1847 ptr = text;
1848 }
1849 else
1850 {
1851 break;
1852 }
1853 if (count > 0)
1854 --count;
1855 else
1856 ++count;
1857 }
1858 return LayoutIndexToTextIndex(static_cast<int>(ptr - text));
1859 }
1860
1861 int TextEntryIM::MoveDisplayLines(int current_index, int count)
1862 {
1863 nuxAssert(current_index >= 0 &&
1864 current_index <= static_cast<int>(text_.length()));
1865 nuxAssert(count);
1866 nuxAssert(preedit_.length() == 0);
1867
1868 PangoLayout *layout = EnsureLayout();
1869 const char *text = pango_layout_get_text(layout);
1870 int index = TextIndexToLayoutIndex(current_index, false);
1871 int n_lines = pango_layout_get_line_count(layout);
1872 int line_index = 0;
1873 int x_off = 0;
1874 PangoRectangle rect;
1875
1876 // Find the current cursor X position in layout
1877 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, &x_off);
1878
1879 // Weird bug: line_index here may be >= than line count?
1880 if (line_index >= n_lines)
1881 {
1882 line_index = n_lines - 1;
1883 }
1884
1885 pango_layout_get_cursor_pos(layout, index, &rect, NULL);
1886 x_off = rect.x;
1887
1888 line_index += count;
1889
1890 if (line_index < 0)
1891 {
1892 return 0;
1893 }
1894 else if (line_index >= n_lines)
1895 {
1896 return static_cast<int>(text_.length());
1897 }
1898
1899 int trailing;
1900#if PANGO_VERSION_CHECK(1,16,0)
1901 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index);
1902#else
1903 PangoLayoutLine *line = pango_layout_get_line(layout, line_index);
1904#endif
1905 // Find out the cursor x offset related to the new line position.
1906 if (line->resolved_dir == PANGO_DIRECTION_RTL)
1907 {
1908 pango_layout_get_cursor_pos(layout, line->start_index + line->length,
1909 &rect, NULL);
1910 }
1911 else
1912 {
1913 pango_layout_get_cursor_pos(layout, line->start_index, &rect, NULL);
1914 }
1915
1916 // rect.x is the left edge position of the line in the layout
1917 x_off -= rect.x;
1918 if (x_off < 0) x_off = 0;
1919 pango_layout_line_x_to_index(line, x_off, &index, &trailing);
1920
1921 index = static_cast<int>(g_utf8_offset_to_pointer(text + index, trailing) - text);
1922 return LayoutIndexToTextIndex(index);
1923 }
1924
1925 int TextEntryIM::MovePages(int current_index, int count)
1926 {
1927 nuxAssert(current_index >= 0 &&
1928 current_index <= static_cast<int>(text_.length()));
1929 nuxAssert(count);
1930 nuxAssert(preedit_.length() == 0);
1931
1932 // Transfer pages to display lines.
1933 PangoLayout *layout = EnsureLayout();
1934 int layout_height;
1935 pango_layout_get_pixel_size(layout, NULL, &layout_height);
1936 int n_lines = pango_layout_get_line_count(layout);
1937 int line_height = layout_height / n_lines;
1938 int page_lines = (GetBaseHeight() - kInnerBorderY * 2) / line_height;
1939 return MoveDisplayLines(current_index, count * page_lines);
1940 }
1941
1942 int TextEntryIM::MoveLineEnds(int current_index, int count)
1943 {
1944 nuxAssert(current_index >= 0 &&
1945 current_index <= static_cast<int>(text_.length()));
1946 nuxAssert(count);
1947 nuxAssert(preedit_.length() == 0);
1948
1949 PangoLayout *layout = EnsureLayout();
1950 int index = TextIndexToLayoutIndex(current_index, false);
1951 int line_index = 0;
1952
1953 // Find current line
1954 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL);
1955
1956 // Weird bug: line_index here may be >= than line count?
1957 int line_count = pango_layout_get_line_count(layout);
1958 if (line_index >= line_count)
1959 {
1960 line_index = line_count - 1;
1961 }
1962
1963// #if PANGO_VERSION_CHECK(1,16,0)
1964// PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index);
1965// #else
1966 PangoLayoutLine *line = pango_layout_get_line(layout, line_index);
1967// #endif
1968
1969 if (line->length == 0)
1970 return current_index;
1971
1972 if ((line->resolved_dir == PANGO_DIRECTION_RTL && count < 0) ||
1973 (line->resolved_dir != PANGO_DIRECTION_RTL && count > 0))
1974 {
1975 index = line->start_index + line->length;
1976 }
1977 else
1978 {
1979 index = line->start_index;
1980 }
1981 return LayoutIndexToTextIndex(index);
1982 }
1983
1984 void TextEntryIM::SetCursor(int cursor)
1985 {
1986 if (cursor != cursor_)
1987 {
1988 ResetImContext();
1989 // If there was a selection range, then the selection range will be cleared.
1990 // Then content_modified_ shall be set to true to force redrawing the text.
1991 if (cursor_ != selection_bound_)
1992 selection_changed_ = true;
1993 cursor_ = cursor;
1994 selection_bound_ = cursor;
1995 cursor_moved_ = true;
1996
1997 cursor_moved.emit(cursor);
1998 }
1999 }
2000
2001 int TextEntryIM::XYToTextIndex(int x, int y)
2002 {
2003 int width, height;
2004 PangoLayout *layout = EnsureLayout();
2005 const char *text = pango_layout_get_text(layout);
2006 pango_layout_get_pixel_size(layout, &width, &height);
2007
2008 if (y < 0)
2009 {
2010 return 0;
2011 }
2012 else if (y >= height)
2013 {
2014 return static_cast<int>(text_.length());
2015 }
2016
2017 int trailing;
2018 int index;
2019 pango_layout_xy_to_index(layout, x * PANGO_SCALE, y * PANGO_SCALE,
2020 &index, &trailing);
2021 index = static_cast<int>(
2022 g_utf8_offset_to_pointer(text + index, trailing) - text);
2023
2024 index = LayoutIndexToTextIndex(index);
2025
2026 // Adjust the offset if preedit is not empty and if the offset is after
2027 // current cursor.
2028 int preedit_length = static_cast<int>(preedit_.length());
2029 if (preedit_length && index > cursor_)
2030 {
2031 if (index >= cursor_ + preedit_length)
2032 index -= preedit_length;
2033 else
2034 index = cursor_;
2035 }
2036 return Clamp(index, 0, static_cast<int>(text_.length()));
2037 }
2038
2039 bool TextEntryIM::GetSelectionBounds(int *start, int *end)
2040 {
2041 if (start)
2042 *start = Min<int>(selection_bound_, cursor_);
2043 if (end)
2044 *end = Max<int>(selection_bound_, cursor_);
2045
2046 return (selection_bound_ != cursor_);
2047 }
2048
2049 void TextEntryIM::SetSelectionBounds(int selection_bound, int cursor)
2050 {
2051 if (selection_bound_ != selection_bound || cursor_ != cursor)
2052 {
2053 selection_changed_ = true;
2054 selection_bound_ = selection_bound;
2055 if (cursor_ != cursor)
2056 {
2057 cursor_ = cursor;
2058 cursor_moved_ = true;
2059 cursor_moved.emit(cursor);
2060 }
2061
2062 //ResetImContext();
2063 }
2064 }
2065
2066 void TextEntryIM::SetFontFamily(const char *font)
2067 {
2068 font_family_ = font;
2069 QueueRefresh(true, true);
2070 }
2071
2072 void TextEntryIM::SetFontSize(double font_size)
2073 {
2074 font_size_ = font_size;
2075 QueueRefresh(true, true);
2076 }
2077
2078 void TextEntryIM::SetFontOptions(const cairo_font_options_t *options)
2079 {
2080 g_return_if_fail(options);
2081
2082 cairo_font_options_destroy(font_options_);
2083 font_options_ = cairo_font_options_copy(options);
2084
2085 QueueRefresh(true, true);
2086 }
2087
2088 bool TextEntryIM::InspectKeyEvent(unsigned int eventType,
2089 unsigned int key_sym,
2090 const char* character)
2091 {
2092 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false) && (ime_active_ == false))
2093 {
2094 if (key_sym == NUX_VK_ENTER ||
2095 key_sym == NUX_KP_ENTER ||
2096 key_sym == NUX_VK_UP ||
2097 key_sym == NUX_VK_DOWN ||
2098 key_sym == NUX_VK_LEFT ||
2099 key_sym == NUX_VK_RIGHT ||
2100 key_sym == NUX_VK_LEFT_TAB ||
2101 key_sym == NUX_VK_TAB ||
2102 key_sym == NUX_VK_ESCAPE)
2103 {
2104 return false;
2105 }
2106 }
2107
2108 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true) && (ime_active_ == false))
2109 {
2110 // Enable to exit the TextEntryIM when in write mode(hack for unity dash)
2111 if (key_sym == NUX_VK_UP ||
2112 key_sym == NUX_VK_DOWN ||
2113 key_sym == NUX_VK_ESCAPE)
2114 {
2115 return false;
2116 }
2117 }
2118
2119 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == false) && (text_input_mode_ == false))
2120 {
2121 return false;
2122 }
2123
2124 return true;
2125 }
2126}
21270
=== removed file 'Nux/TextEntryIM.h'
--- Nux/TextEntryIM.h 2012-02-12 01:55:02 +0000
+++ Nux/TextEntryIM.h 1970-01-01 00:00:00 +0000
@@ -1,383 +0,0 @@
1#ifndef TEXTENTRYIM_H
2#define TEXTENTRYIM_H
3
4
5
6// Heavily inspired from google gadget code
7/*
8 Copyright 2008 Google Inc.
9
10 Licensed under the Apache License, Version 2.0(the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21*/
22
23
24#include "cairo/cairo.h"
25#include "pango/pango.h"
26#include "pango/pangocairo.h"
27#include "NuxImage/CairoGraphics.h"
28#include "InputMethodIBus.h"
29
30namespace nux
31{
32 class CairoGraphics;
33 class IBusIMEContext;
34
35 class TextEntryIM: public View
36 {
37 NUX_DECLARE_OBJECT_TYPE(TextEntryIM, View);
38 public:
39 TextEntryIM(const char* text, NUX_FILE_LINE_PROTO);
40 ~TextEntryIM();
41
42 Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type);
43 virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw);
44 virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw);
45 virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw);
46
47 void PreLayoutManagement();
48 long PostLayoutManagement(long layoutResult);
49
50 // Receivers
51
52 void RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags);
53 void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);
54 void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags);
55 void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
56 void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags);
57 void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags);
58 void RecvKeyEvent(
59 unsigned long eventType , /*event type*/
60 unsigned long keysym , /*event keysym*/
61 unsigned long state , /*event state*/
62 const char* character , /*character*/
63 unsigned short keyCount /*key repeat count*/);
64
65 void RecvStartKeyFocus();
66 void RecvEndKeyFocus();
67
68 bool _size_match_text;
69 BaseTexture *_texture2D;
70
71 void MainDraw();
72 void ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
73 void ProcessKeyEvent (
74 unsigned long eventType , /*event type*/
75 unsigned long keysym , /*event keysym*/
76 unsigned long state , /*event state*/
77 const char* character , /*character*/
78 unsigned short keyCount /*key repeat count*/);
79
80 void FocusInx();
81 void FocusOutx();
82
83 //! Text changed signal
84 /*!
85 This signal is emitted when the text has changed.
86 */
87 sigc::signal <void, TextEntryIM*> sigTextChanged;
88 sigc::signal <void> activated;
89 sigc::signal <void, int> cursor_moved;
90
91 void SetText(const char *text);
92 std::string const& GetText() const;
93
94 void SetCompletion(const char *text); // Should use std::string, does not for consistancy
95 std::string const& GetCompletion() const;
96
97 void SetCompletionColor(const Color &color);
98 Color const& GetCompletionColor() const;
99
100 void SetTextColor(const Color &color);
101 Color const& GetTextColor() const;
102 void SetFontFamily(const char *font);
103 void SetFontSize(double font_size);
104 void SetFontOptions(const cairo_font_options_t *options);
105
106 /** Select text between start and end. */
107 void Select(int start, int end);
108 /** Select all text */
109 void SelectAll();
110
111 CairoGraphics::Alignment GetAlign() const;
112 void SetAlign(CairoGraphics::Alignment align);
113
114 bool im_active();
115
116 protected:
117 bool _block_focus; // used to selectively ignore focus keyevents
118
119 virtual void GeometryChanged();
120
121 /**
122 * Enum used to specify different motion types.
123 */
124 enum MovementStep {
125 VISUALLY,
126 WORDS,
127 DISPLAY_LINES,
128 DISPLAY_LINE_ENDS,
129 PAGES,
130 BUFFER
131 };
132
133 void QueueTextDraw();
134 /** Remove the cached layout. */
135 void ResetLayout();
136 /**
137 * Create pango layout on-demand. If the layout is not changed, return the
138 * cached one.
139 */
140 PangoLayout* EnsureLayout();
141 /** Create a new layout containning current edit content */
142 PangoLayout* CreateLayout();
143 /** Create cairo canvas on-demand. */
144 CairoGraphics* EnsureCanvas();
145 /** Adjust the scroll information */
146 void AdjustScroll();
147 /**
148 * Send out a request to refresh all informations of the edit control
149 * and queue a draw request.
150 * If @c relayout is true then the layout will be regenerated.
151 * */
152 void QueueRefresh(bool relayout, bool adjust_scroll);
153 /** Reset the input method context */
154 void ResetImContext();
155 /** Reset preedit text */
156 void ResetPreedit();
157 /** Send out a request to blink the cursor if necessary */
158 void QueueCursorBlink();
159 static bool CursorBlinkCallback(TextEntryIM *data);
160
161 void ShowCursor();
162 void HideCursor();
163
164 /** Draw the Cursor to the canvas */
165 void DrawCursor(CairoGraphics *canvas);
166 /** Draw the text to the canvas */
167 void DrawText(CairoGraphics *canvas);
168
169 void GetCursorRects(Rect *strong, Rect *weak);
170
171 void UpdateCursorRegion();
172
173 void UpdateSelectionRegion();
174
175 /** Move cursor */
176 void MoveCursor(MovementStep step, int count, bool extend_selection);
177 /** Move cursor visually, meaning left or right */
178 int MoveVisually(int current_pos, int count);
179 /** Move cursor in words */
180 int MoveWords(int current_pos, int count);
181 /** Move cursor in display lines */
182 int MoveDisplayLines(int current_pos, int count);
183 /** Move cursor in pages */
184 int MovePages(int current_pos, int count);
185 /** Move cursor to the beginning or end of a display line */
186 int MoveLineEnds(int current_pos, int count);
187
188 /** Set the current cursor offset, in number of characters. */
189 void SetCursor(int cursor);
190 /** Get the most reasonable character offset according to the pixel
191 * coordinate in the layout */
192 int XYToTextIndex(int x, int y);
193 /** Get the offset range that is currently selected,in number of characters.*/
194 bool GetSelectionBounds(int *start, int *end);
195 /** Set the offest range that should be selected, in number of characters. */
196 void SetSelectionBounds(int selection_bound, int cursor);
197
198 /** Convert index in text_ into index in layout text. */
199 int TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor);
200
201 /** Convert index in layout text into index in text_. */
202 int LayoutIndexToTextIndex(int layout_index);
203
204 /** Get char length at index, in number of bytes. */
205 int GetCharLength(int index);
206
207 /** Get previous char length before index, in number of bytes. */
208 int GetPrevCharLength(int index);
209
210 /** Insert text at current caret position */
211 void EnterText(const char *str);
212 /** Delete text in a specified range, in number of characters. */
213 void DeleteText(int start, int end);
214
215 /** Select the current word under cursor */
216 void SelectWord();
217 /** Select the current display line under cursor */
218 void SelectLine();
219 /** Delete the text that is currently selected */
220 void DeleteSelection();
221
222 /** Cut the current selected text to the clipboard */
223 void CutClipboard();
224 /** Copy the current selected text to the clipboard */
225 void CopyClipboard();
226 /** Paste the text in the clipboard to current offset */
227 void PasteClipboard();
228 /** Delete a character before the offset of the cursor */
229 void BackSpace(MovementStep step);
230 /** Delete a character at the offset of the cursor */
231 void Delete(MovementStep step);
232 /** Switch between the overwrite mode and the insert mode*/
233 void ToggleOverwrite();
234
235 /** Gets the color of selection background. */
236 Color GetSelectionBackgroundColor();
237 /** Gets the color of selection text. */
238 Color GetSelectionTextColor();
239
240 /**
241 * Gets the cursor location in pango layout. The unit is pixel.
242 */
243 void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height,
244 int *weak_x, int *weak_y, int *weak_height);
245
246 /** The CairoCanvas which hold cairo_t inside */
247 CairoGraphics* canvas_;
248
249 /** The cached Pango Layout */
250 PangoLayout* cached_layout_;
251
252 /** The text content of the edit control */
253 std::string text_;
254 /** The preedit text of the edit control */
255 std::string preedit_;
256 /** Attribute list of the preedit text */
257 PangoAttrList *preedit_attrs_;
258 /**
259 * The character that should be displayed in invisible mode.
260 * If this is empty, then the edit control is visible
261 */
262 std::string password_char_;
263
264 /** The completion string */
265 std::string completion_;
266
267 /** The completion colour */
268 Color completion_color_;
269
270 /** Last time of mouse double click event. */
271 unsigned long long last_dblclick_time_;
272
273 /** The current cursor position in number of bytes. */
274 int cursor_;
275 /**
276 * The preedit cursor position within the preedit string,
277 * in number of bytes.
278 */
279 int preedit_cursor_;
280 /**
281 * The current selection bound in number of bytes,
282 * range between cursor_ and selection_bound_ are selected.
283 */
284 int selection_bound_;
285
286 /** X offset of current scroll, in pixels */
287 int scroll_offset_x_;
288 /** Y offset of current scroll, in pixels */
289 int scroll_offset_y_;
290 /** Timer id of cursor blink callback */
291 int cursor_blink_timer_;
292 /**
293 * Indicates the status of cursor blinking,
294 * 0 means hide cursor
295 * otherwise means show cursor.
296 * The maximum value would be 2, and decrased by one in each cursor blink
297 * callback, then there would be 2/3 visible time and 1/3 invisible time.
298 */
299 int cursor_blink_status_;
300
301 /** Whether the text is visible, decided by password_char_ */
302 bool visible_;
303 /** Whether the edit control is focused */
304 bool focused_;
305 /** Whether the input method should be reset */
306 bool need_im_reset_;
307 /** Whether the keyboard in overwrite mode */
308 bool overwrite_;
309 /** Whether the button click should select words */
310 bool select_words_;
311 /** Whether the button click should select lines */
312 bool select_lines_;
313 /** Whether the left button is pressed */
314 bool button_;
315 /** Whether the text should be bold */
316 bool bold_;
317 /** Whether the text should be underlined */
318 bool underline_;
319 /** Whether the text should be struck-out */
320 bool strikeout_;
321 /** Whether the text should be italic */
322 bool italic_;
323 /** Whether the text could be shown in multilines */
324 bool multiline_;
325 /** Whether the text should be wrapped */
326 bool wrap_;
327 /** whether the cursor should be displayed */
328 bool cursor_visible_;
329 /** whether the edit control is readonly */
330 bool readonly_;
331 /**
332 * Indicates if the content of the edit control has been modified
333 * since last draw
334 */
335 bool content_modified_;
336
337 /** Indicates if the selection region has been changed since last draw. */
338 bool selection_changed_;
339
340 /** Indicates if the cursor position has been moved since last draw. */
341 bool cursor_moved_;
342
343 /** Indicates if the canvas cache needs updating. */
344 bool update_canvas_;
345
346 /** The font family of the text */
347 std::string font_family_;
348 /** The font size of the text */
349 double font_size_;
350
351 cairo_font_options_t *font_options_;
352 double font_dpi_;
353
354 /** The text color of the edit control */
355 Color _text_color;
356
357 CairoGraphics::Alignment align_;
358
359#if defined(NUX_OS_LINUX)
360 Cursor caret_cursor_;
361#endif
362
363 std::list<Rect> last_selection_region_;
364 std::list<Rect> selection_region_;
365 std::list<Rect> last_cursor_region_;
366 std::list<Rect> cursor_region_;
367
368 IBusIMEContext* ime_;
369 bool ime_active_;
370
371 friend class IBusIMEContext;
372
373 protected:
374 bool text_input_mode_;
375 bool key_nav_mode_;
376
377 virtual bool InspectKeyEvent(unsigned int eventType,
378 unsigned int keysym,
379 const char* character);
380};
381}
382
383#endif // TEXTENTRY_H
3840
=== modified file 'configure.ac'
--- configure.ac 2012-02-21 06:39:45 +0000
+++ configure.ac 2012-02-23 05:24:18 +0000
@@ -22,7 +22,7 @@
22# The number format is : year/month/day22# The number format is : year/month/day
23# e.g.: december 5th, 2011 is: 2011120523# e.g.: december 5th, 2011 is: 20111205
24# To make more than one API change in a day, add a number to the date. Like 20111205.xx24# To make more than one API change in a day, add a number to the date. Like 20111205.xx
25m4_define([nux_abi_version], [20120221.01])25m4_define([nux_abi_version], [20120223.01])
2626
27m4_define([nux_version],27m4_define([nux_version],
28 [nux_major_version.nux_minor_version.nux_micro_version])28 [nux_major_version.nux_minor_version.nux_micro_version])
@@ -186,6 +186,16 @@
186AC_SUBST(UNITY_SUPPORT_TEST_CFLAGS)186AC_SUBST(UNITY_SUPPORT_TEST_CFLAGS)
187AC_SUBST(UNITY_SUPPORT_TEST_LIBS)187AC_SUBST(UNITY_SUPPORT_TEST_LIBS)
188188
189PKG_CHECK_MODULES(IBUS,
190 glib-2.0 >= 2.25.14
191 gio-2.0
192 ibus-1.0
193 gobject-2.0
194 )
195
196AC_SUBST(IBUS_CFLAGS)
197AC_SUBST(IBUS_LIBS)
198
189dnl ************************************199dnl ************************************
190dnl Enable/disable tests200dnl Enable/disable tests
191dnl ************************************201dnl ************************************
192202
=== modified file 'examples/Makefile.am'
--- examples/Makefile.am 2012-02-12 22:25:19 +0000
+++ examples/Makefile.am 2012-02-23 05:24:18 +0000
@@ -46,6 +46,7 @@
46 $(NUX_CORE_CFLAGS) \46 $(NUX_CORE_CFLAGS) \
47 $(NUX_EXAMPLES_CFLAGS) \47 $(NUX_EXAMPLES_CFLAGS) \
48 $(NUX_CFLAGS) \48 $(NUX_CFLAGS) \
49 $(IBUS_CFLAGS) \
49 $(MAINTAINER_CFLAGS)50 $(MAINTAINER_CFLAGS)
5051
51ALL_LIBS = \52ALL_LIBS = \
@@ -54,7 +55,8 @@
54 $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \55 $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \
55 $(top_builddir)/Nux/libnux-@NUX_API_VERSION@.la \56 $(top_builddir)/Nux/libnux-@NUX_API_VERSION@.la \
56 $(NUX_EXAMPLES_LIBS) \57 $(NUX_EXAMPLES_LIBS) \
57 $(NUX_LIBS)58 $(NUX_LIBS) \
59 $(IBUS_LIBS)
5860
59# This is the individual executable build. For every $exe in noinst_PROGRAMS61# This is the individual executable build. For every $exe in noinst_PROGRAMS
60# you need a $exe_SOURCES and $exe_LDADD so it builds62# you need a $exe_SOURCES and $exe_LDADD so it builds
6163
=== modified file 'tests/Makefile.am'
--- tests/Makefile.am 2012-02-19 07:04:11 +0000
+++ tests/Makefile.am 2012-02-23 05:24:18 +0000
@@ -13,8 +13,9 @@
13 xtest-vlayout-key-navigation \13 xtest-vlayout-key-navigation \
14 xtest-scrollbar \14 xtest-scrollbar \
15 xtest-focus-on-mouse-down \15 xtest-focus-on-mouse-down \
16 xtest-focus-on-mouse-enter \16 xtest-keynav-directions \
17 xtest-keynav-directions17 xtest-text-entry \
18 xtest-focus-on-mouse-enter
1819
19# Please keep alphabetical20# Please keep alphabetical
20test_nux_SOURCES = \21test_nux_SOURCES = \
@@ -100,7 +101,8 @@
100 $(NUX_CORE_CFLAGS) \101 $(NUX_CORE_CFLAGS) \
101 $(NUX_EXAMPLES_CFLAGS) \102 $(NUX_EXAMPLES_CFLAGS) \
102 $(NUX_CFLAGS) \103 $(NUX_CFLAGS) \
103 $(MAINTAINER_CFLAGS)104 $(MAINTAINER_CFLAGS) \
105 $(IBUS_CFLAGS)
104106
105gtest_nux_LDADD = \107gtest_nux_LDADD = \
106 $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \108 $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \
@@ -124,7 +126,8 @@
124 $(NUX_CORE_CFLAGS) \126 $(NUX_CORE_CFLAGS) \
125 $(NUX_EXAMPLES_CFLAGS) \127 $(NUX_EXAMPLES_CFLAGS) \
126 $(NUX_CFLAGS) \128 $(NUX_CFLAGS) \
127 $(MAINTAINER_CFLAGS)129 $(MAINTAINER_CFLAGS) \
130 $(IBUS_CFLAGS)
128131
129TestLibs = $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \132TestLibs = $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \
130 $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \133 $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \
@@ -227,6 +230,14 @@
227xtest_keynav_directions_LDADD = $(TestLibs)230xtest_keynav_directions_LDADD = $(TestLibs)
228xtest_keynav_directions_LDFLAGS = -lpthread -lXtst231xtest_keynav_directions_LDFLAGS = -lpthread -lXtst
229232
233xtest_text_entry_SOURCES = xtest-text-entry.cpp \
234 nux_automated_test_framework.cpp \
235 nux_automated_test_framework.h
236
237xtest_text_entry_CPPFLAGS = $(TestFlags)
238xtest_text_entry_LDADD = $(TestLibs)
239xtest_text_entry_LDFLAGS = -lpthread -lXtst
240
230241
231#run make test as part of make check242#run make test as part of make check
232check-local: test gtest test-apps243check-local: test gtest test-apps
233244
=== modified file 'tests/Readme.txt'
--- tests/Readme.txt 2012-02-17 13:43:55 +0000
+++ tests/Readme.txt 2012-02-23 05:24:18 +0000
@@ -53,3 +53,6 @@
5353
54xtest-keynav-direction54xtest-keynav-direction
55 Test key navigation. Especially test the direction a key nav is coming from when the view is getting the focus.55 Test key navigation. Especially test the direction a key nav is coming from when the view is getting the focus.
56
57xtest-text-entry
58 Simulate various operations on the text entry
5659
=== modified file 'tests/nux_automated_test_framework.cpp'
--- tests/nux_automated_test_framework.cpp 2011-12-06 22:44:57 +0000
+++ tests/nux_automated_test_framework.cpp 2012-02-23 05:24:18 +0000
@@ -259,7 +259,16 @@
259 }259 }
260260
261 std::string s(1, c);261 std::string s(1, c);
262 SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier);262
263 if (c == ' ')
264 {
265 SendFakeKeyEvent(XK_space, modifier);
266 }
267 else
268 {
269 SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier);
270 }
271
263 nux::SleepForMilliseconds(300);272 nux::SleepForMilliseconds(300);
264}273}
265274
@@ -282,7 +291,15 @@
282 }291 }
283292
284 std::string s(1, c);293 std::string s(1, c);
285 SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier);294
295 if (c == ' ')
296 {
297 SendFakeKeyEvent(XK_space, modifier);
298 }
299 else
300 {
301 SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier);
302 }
286 nux::SleepForMilliseconds(300);303 nux::SleepForMilliseconds(300);
287 }304 }
288}305}
@@ -367,6 +384,44 @@
367 SendFakeKeyEvent(XK_Return, 0);384 SendFakeKeyEvent(XK_Return, 0);
368}385}
369386
387void NuxAutomatedTestFramework::ViewSendRight()
388{
389 SendFakeKeyEvent(XK_Right, 0);
390}
391
392void NuxAutomatedTestFramework::ViewSendLeft()
393{
394 SendFakeKeyEvent(XK_Left, 0);
395}
396
397void NuxAutomatedTestFramework::ViewSendUp()
398{
399 SendFakeKeyEvent(XK_Up, 0);
400}
401
402void NuxAutomatedTestFramework::ViewSendDown()
403{
404 SendFakeKeyEvent(XK_Down, 0);
405}
406
407void NuxAutomatedTestFramework::ViewSendIBusToggle()
408{
409 KeyCode modcode0 = 0;
410 KeyCode modcode1 = 0;
411
412 modcode0 = XKeysymToKeycode(display_, XK_Control_L);
413 XTestFakeKeyEvent(display_, modcode0, True, 0);
414
415 modcode1 = XKeysymToKeycode(display_, XK_space);
416 XTestFakeKeyEvent(display_, modcode1, True, 0);
417
418 // release
419 /* Generate modkey release */
420 XTestFakeKeyEvent(display_, modcode1, False, 0);
421 XTestFakeKeyEvent(display_, modcode0, False, 0);
422
423}
424
370void NuxAutomatedTestFramework::PutMouseAt(int x, int y)425void NuxAutomatedTestFramework::PutMouseAt(int x, int y)
371{426{
372 XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); 427 XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime);
@@ -458,4 +513,4 @@
458 {513 {
459 nuxAlertMsg("%s: %s", msg, "Failed");514 nuxAlertMsg("%s: %s", msg, "Failed");
460 } 515 }
461}
462\ No newline at end of file516\ No newline at end of file
517}
463518
=== modified file 'tests/nux_automated_test_framework.h'
--- tests/nux_automated_test_framework.h 2011-11-23 04:42:44 +0000
+++ tests/nux_automated_test_framework.h 2012-02-23 05:24:18 +0000
@@ -80,6 +80,16 @@
80 void ViewSendTab();80 void ViewSendTab();
81 //! Simulate Return key.81 //! Simulate Return key.
82 void ViewSendReturn();82 void ViewSendReturn();
83 //! Simulate Left arrow key.
84 void ViewSendLeft();
85 //! Simulate Right arrow key.
86 void ViewSendRight();
87 //! Simulate Up arrow key.
88 void ViewSendUp();
89 //! Simulate Down arrow key.
90 void ViewSendDown();
91
92 void ViewSendIBusToggle();
8393
84 //! Put the mouse pointer anywhere on the display.94 //! Put the mouse pointer anywhere on the display.
85 void PutMouseAt(int x, int y);95 void PutMouseAt(int x, int y);
8696
=== added file 'tests/xtest-text-entry.cpp'
--- tests/xtest-text-entry.cpp 1970-01-01 00:00:00 +0000
+++ tests/xtest-text-entry.cpp 2012-02-23 05:24:18 +0000
@@ -0,0 +1,391 @@
1/*
2 * Copyright 2010 Inalogic Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * version 3 along with this program. If not, see
15 * <http://www.gnu.org/licenses/>
16 *
17 * Authored by: Jay Taoko <jaytaoko@inalogic.com>
18 *
19 */
20
21#include "Nux/Nux.h"
22#include "Nux/WindowThread.h"
23#include "Nux/VLayout.h"
24#include "Nux/TextEntry.h"
25#include "Nux/ProgramFramework/ProgramTemplate.h"
26#include "Nux/ProgramFramework/TestView.h"
27#include <X11/extensions/XTest.h>
28#include <X11/keysym.h>
29#include "nux_automated_test_framework.h"
30
31class TextTextEntry: public ProgramTemplate
32{
33public:
34 TextTextEntry(const char* program_name, int window_width, int window_height, int program_life_span);
35 ~TextTextEntry();
36
37 virtual void UserInterfaceSetup();
38
39 void TextEntryClick(nux::TextEntry* text_entry);
40 void ResetEvents();
41 nux::TextEntry* text_entry_;
42
43 bool clicked_;
44};
45
46TextTextEntry::TextTextEntry(const char* program_name,
47 int window_width,
48 int window_height,
49 int program_life_span)
50 : ProgramTemplate(program_name, window_width, window_height, program_life_span)
51{
52 ResetEvents();
53 text_entry_ = NULL;
54}
55
56TextTextEntry::~TextTextEntry()
57{
58
59}
60
61void TextTextEntry::ResetEvents()
62{
63 clicked_ = false;
64}
65
66void TextTextEntry::TextEntryClick(nux::TextEntry* text_entry)
67{
68 if (text_entry_ == text_entry)
69 {
70 clicked_ = true;
71 }
72}
73
74void TextTextEntry::UserInterfaceSetup()
75{
76 nux::VLayout* main_layout = new nux::VLayout(NUX_TRACKER_LOCATION);
77 text_entry_ = new nux::TextEntry("", NUX_TRACKER_LOCATION);
78 text_entry_->SetFontSize(76);
79
80 main_layout->AddView(text_entry_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
81 main_layout->SetSpaceBetweenChildren(10);
82 main_layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER);
83
84 static_cast<nux::WindowThread*>(window_thread_)->SetLayout(main_layout);
85
86 nux::ColorLayer background(nux::Color(0xFF4D4D4D));
87 static_cast<nux::WindowThread*>(window_thread_)->SetWindowBackgroundPaintLayer(&background);
88}
89
90bool SetEngineActive (IBusBus* bus_, std::string engine)
91{
92 GList* engines = ibus_bus_list_active_engines(bus_);
93 GList* start = engines;
94
95 bool found = false;
96 gboolean global_flag = ibus_bus_get_use_global_engine(bus_);
97
98 // Iterates through the active engines
99 do
100 {
101 IBusEngineDesc *engine_desc = IBUS_ENGINE_DESC (engines->data);
102
103 // Found Engine, make it active!
104 if (g_strcmp0(ibus_engine_desc_get_name(engine_desc), engine.c_str()) == 0)
105 {
106 found = true;
107
108 // Set ibus to use global engines
109 if (!global_flag)
110 ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(true));
111
112 // Set and activate the engine
113 ibus_bus_set_global_engine(bus_,engine.c_str());
114 }
115 } while ((engines = g_list_next(engines)) != NULL);
116
117 // Restores the global setting back to what it was
118 ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(global_flag));
119
120 g_list_free(start);
121 return found;
122}
123
124TextTextEntry* test_textentry = NULL;
125
126void TestingThread(nux::NThread* thread, void* user_data)
127{
128 while (test_textentry->ReadyToGo() == false)
129 {
130 nuxDebugMsg("Waiting to start");
131 nux::SleepForMilliseconds(300);
132 }
133
134 nux::SleepForMilliseconds(1300);
135
136 nux::WindowThread* wnd_thread = static_cast<nux::WindowThread*>(user_data);
137
138 NuxAutomatedTestFramework test(wnd_thread);
139
140 test.Startup();
141
142 test.TestReportMsg(test_textentry->text_entry_, "TextEntry created");
143
144 test_textentry->ResetEvents();
145 test.ViewSendMouseMotionToCenter(test_textentry->text_entry_);
146
147 test.ViewSendMouseClick(0, 1);
148
149 // Type "Nux"
150 // The cursor is at the end of the line
151 // Simulate CTRL+A to select the entire text
152 // Simulate DELETE key to delete the text
153 {
154 test.ViewSendString("Nux");
155 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Nux", "Typed \"Nux\"");
156
157 test.ViewSendCtrlA();
158 nux::SleepForMilliseconds(500);
159 test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "Nux", "Selection is \"Nux\"");
160
161
162 test.ViewSendDelete();
163 nux::SleepForMilliseconds(500);
164 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty");
165 }
166
167 // Type "0123456789"
168 // The cursor is at the end of the line
169 // Simulate BACKSPACE key until the text is enpty
170 {
171 test.ViewSendString("Ubuntu");
172 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubuntu", "Typed \"Ubuntu\"");
173
174 test.ViewSendBackspace();
175 nux::SleepForMilliseconds(500);
176 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubunt", "Text is \"Ubunt\"");
177
178 test.ViewSendBackspace();
179 nux::SleepForMilliseconds(500);
180 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubun", "Text is \"Ubun\"");
181
182 test.ViewSendBackspace();
183 nux::SleepForMilliseconds(500);
184 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubu", "Text is \"Ubu\"");
185
186 test.ViewSendBackspace();
187 nux::SleepForMilliseconds(500);
188 test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ub", "Text is \"Ub\"");
189
190 test.ViewSendBackspace();
191 nux::SleepForMilliseconds(500);
192 test.TestReportMsg(test_textentry->text_entry_->GetText() == "U", "Text is \"U\"");
193
194 test.ViewSendBackspace();
195 nux::SleepForMilliseconds(500);
196 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty");
197 }
198
199 // Type "0123456789"
200 // Move cursor to start of line
201 // Simulate DELETE key until the text is 6789
202 // Simulate CTRL+A to select the entire text
203 // Simulate DELETE key to delete the text
204 {
205 test.ViewSendString("0123456789");
206 test.TestReportMsg(test_textentry->text_entry_->GetText() == "0123456789", "Typed \"0123456789\"");
207
208 test_textentry->text_entry_->MoveCursorToLineStart();
209
210 test.ViewSendDelete();
211 nux::SleepForMilliseconds(500);
212 test.TestReportMsg(test_textentry->text_entry_->GetText() == "123456789", "Text is \"123456789\"");
213
214 test.ViewSendDelete();
215 nux::SleepForMilliseconds(500);
216 test.TestReportMsg(test_textentry->text_entry_->GetText() == "23456789", "Text is \"23456789\"");
217
218 test.ViewSendDelete();
219 nux::SleepForMilliseconds(500);
220 test.TestReportMsg(test_textentry->text_entry_->GetText() == "3456789", "Text is \"3456789\"");
221
222 test.ViewSendDelete();
223 nux::SleepForMilliseconds(500);
224 test.TestReportMsg(test_textentry->text_entry_->GetText() == "456789", "Text is \"456789\"");
225
226 test.ViewSendDelete();
227 nux::SleepForMilliseconds(500);
228 test.TestReportMsg(test_textentry->text_entry_->GetText() == "56789", "Text is \"56789\"");
229
230 test.ViewSendDelete();
231 nux::SleepForMilliseconds(500);
232 test.TestReportMsg(test_textentry->text_entry_->GetText() == "6789", "Text \"6789\"");
233
234
235 test.ViewSendCtrlA();
236 nux::SleepForMilliseconds(500);
237 test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "6789", "Selection is \"6789\"");
238
239
240 test.ViewSendDelete();
241 nux::SleepForMilliseconds(500);
242 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty");
243 }
244
245 // Toggle IBus
246 // Type "qwerty"
247 // Simulate key '1' to select the first IM option
248 {
249 // CTRL+Space to initiate iBus
250 //test.ViewSendIBusToggle();
251
252 IBusBus* bus_;
253 ibus_init();
254 bus_ = ibus_bus_new();
255 bool active = false;
256
257 // Test for ibus-pinyin
258 if (SetEngineActive(bus_,"pinyin"))
259 {
260 // Type random stuff
261 {
262 test.ViewSendString("qwerty");
263 nux::SleepForMilliseconds(500);
264 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit");
265
266 test.ViewSendChar('1');
267 nux::SleepForMilliseconds(500);
268 test.TestReportMsg(test_textentry->text_entry_->GetText() == "请问儿童有", "TextEntry is \"请问儿童有\"");
269
270 test.ViewSendCtrlA();
271 nux::SleepForMilliseconds(500);
272
273 test.ViewSendDelete();
274 nux::SleepForMilliseconds(500);
275 }
276
277 // Testing Cursor in the preedit window
278 {
279 test.ViewSendString("ming");
280 nux::SleepForMilliseconds(500);
281 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit");
282
283 test.ViewSendLeft();
284 nux::SleepForMilliseconds(500);
285
286 test.ViewSendDelete();
287 nux::SleepForMilliseconds(500);
288
289 test.ViewSendChar('1');
290 nux::SleepForMilliseconds(500);
291 test.TestReportMsg(test_textentry->text_entry_->GetText() == "民", "TextEntry is \"民\"");
292
293 test.ViewSendCtrlA();
294 nux::SleepForMilliseconds(500);
295
296 test.ViewSendDelete();
297 nux::SleepForMilliseconds(500);
298 }
299
300 active = true;
301 }
302 else
303 {
304 test.TestReportMsg(false,"Pinyin is NOT an active input method" );
305 }
306
307 // Test for ibus-hangul
308 if (SetEngineActive(bus_,"hangul"))
309 {
310 // Test for the the space in ibus-hangul working correctlly
311 {
312 test.ViewSendString("asd abc ");
313 nux::SleepForMilliseconds(500);
314 test.TestReportMsg(test_textentry->text_entry_->GetText() == "ㅁㄴㅇ 뮻 ", "TextEntry is \"ㅁㄴㅇ 뮻 \"");
315
316 test.ViewSendCtrlA();
317 nux::SleepForMilliseconds(500);
318
319 test.ViewSendDelete();
320 nux::SleepForMilliseconds(500);
321 }
322
323 active = true;
324 }
325 else
326 {
327 test.TestReportMsg(false,"Hangul is NOT an active input method" );
328 }
329
330 // Checking for ibus-anthy - Japanese
331 if (SetEngineActive(bus_,"anthy"))
332 {
333 {
334 test.ViewSendString("shisutemu ");
335 nux::SleepForMilliseconds(500);
336 test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit");
337
338 // Ctrl + J commits for ibus-anthy
339 test.ViewSendKeyCombo(XK_Control_L, 0, 0, 'j');
340 nux::SleepForMilliseconds(500);
341 test.TestReportMsg(test_textentry->text_entry_->GetText() == "システム", "TextEntry is \"システム\"");
342
343 test.ViewSendCtrlA();
344 nux::SleepForMilliseconds(500);
345
346 test.ViewSendDelete();
347 nux::SleepForMilliseconds(500);
348 }
349
350 active = true;
351 }
352 else
353 {
354 test.TestReportMsg(false,"Anthy is NOT an active input method" );
355 }
356
357 g_object_unref (bus_);
358
359 // CTRL+Space to deactivate iBus
360 if (active)
361 test.ViewSendIBusToggle();
362 }
363
364 if (test.WhenDoneTerminateProgram())
365 {
366 wnd_thread->ExitMainLoop();
367 }
368 nuxDebugMsg("Exit testing thread");
369}
370
371int main(int argc, char** argv)
372{
373 int xstatus = XInitThreads();
374 nuxAssertMsg(xstatus > 0, "XInitThreads has failed");
375
376 test_textentry = new TextTextEntry("Text Entry", 600, 200, 40000);
377 test_textentry->Startup();
378 test_textentry->UserInterfaceSetup();
379
380 nux::SystemThread* test_thread = nux::CreateSystemThread(NULL, &TestingThread, test_textentry->GetWindowThread());
381
382 test_thread->Start(test_textentry);
383
384 test_textentry->Run();
385
386 delete test_thread;
387 delete test_textentry;
388
389 return 0;
390}
391

Subscribers

People subscribed via source and target branches

to all changes: