Merge lp:~unity-team/nux/nux.text-entry-im-auto-test into lp:nux/2.0
- nux.text-entry-im-auto-test
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jay Taoko (community) | Approve | ||
Review via email: mp+94304@code.launchpad.net |
Commit message
Description of the change
TextEntry with InputMethod support.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Nux/InputMethodIBus.cpp' | |||
2 | --- Nux/InputMethodIBus.cpp 2012-02-12 01:55:02 +0000 | |||
3 | +++ Nux/InputMethodIBus.cpp 2012-02-23 05:24:18 +0000 | |||
4 | @@ -6,7 +6,7 @@ | |||
5 | 6 | { | 6 | { |
6 | 7 | IBusBus* IBusIMEContext::bus_ = NULL; | 7 | IBusBus* IBusIMEContext::bus_ = NULL; |
7 | 8 | 8 | ||
9 | 9 | IBusIMEContext::IBusIMEContext(TextEntryIM* text_entry) | 9 | IBusIMEContext::IBusIMEContext(TextEntry* text_entry) |
10 | 10 | : text_entry_(text_entry), | 10 | : text_entry_(text_entry), |
11 | 11 | context_(NULL), | 11 | context_(NULL), |
12 | 12 | is_focused_(false) | 12 | is_focused_(false) |
13 | @@ -68,14 +68,16 @@ | |||
14 | 68 | ibus_input_context_reset(context_); | 68 | ibus_input_context_reset(context_); |
15 | 69 | } | 69 | } |
16 | 70 | 70 | ||
17 | 71 | // FIXME Need to get the key_code form nux NOT just the keysym | ||
18 | 72 | bool IBusIMEContext::FilterKeyEvent(const KeyEvent& event) | 71 | bool IBusIMEContext::FilterKeyEvent(const KeyEvent& event) |
19 | 73 | { | 72 | { |
21 | 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()); |
22 | 75 | 74 | ||
23 | 76 | if (context_) { | 75 | if (context_) { |
24 | 77 | guint modifiers = 0; | 76 | guint modifiers = 0; |
25 | 78 | 77 | ||
26 | 78 | if (event.flags() & IBUS_IGNORED_MASK) | ||
27 | 79 | return false; | ||
28 | 80 | |||
29 | 79 | if (event.type() == EVENT_KEY_UP) | 81 | if (event.type() == EVENT_KEY_UP) |
30 | 80 | modifiers |= IBUS_RELEASE_MASK; | 82 | modifiers |= IBUS_RELEASE_MASK; |
31 | 81 | 83 | ||
32 | @@ -88,19 +90,14 @@ | |||
33 | 88 | if (event.IsCapsLockDown()) | 90 | if (event.IsCapsLockDown()) |
34 | 89 | modifiers |= IBUS_LOCK_MASK; | 91 | modifiers |= IBUS_LOCK_MASK; |
35 | 90 | 92 | ||
36 | 91 | // FIXME Not the best way to get the x11_key_code. Should be able to get it from TextEntry::ProcessKeyEvent | ||
37 | 92 | // The x11_keycode is needed for the ibus-hangul engine! | ||
38 | 93 | nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent(); | ||
39 | 94 | |||
40 | 95 | ibus_input_context_process_key_event_async(context_, | 93 | ibus_input_context_process_key_event_async(context_, |
42 | 96 | keyval, cur_event.x11_keycode - 8, modifiers, | 94 | keyval, event.key_code() - 8, modifiers, |
43 | 97 | -1, | 95 | -1, |
44 | 98 | NULL, | 96 | NULL, |
45 | 99 | reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone), | 97 | reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone), |
46 | 100 | new ProcessKeyEventData(this, event)); | 98 | new ProcessKeyEventData(this, event)); |
47 | 101 | return true; | 99 | return true; |
48 | 102 | } | 100 | } |
49 | 103 | |||
50 | 104 | return false; | 101 | return false; |
51 | 105 | } | 102 | } |
52 | 106 | 103 | ||
53 | @@ -113,11 +110,14 @@ | |||
54 | 113 | nuxAssert(bus_ != NULL); | 110 | nuxAssert(bus_ != NULL); |
55 | 114 | nuxAssert(ibus_bus_is_connected(bus_)); | 111 | nuxAssert(ibus_bus_is_connected(bus_)); |
56 | 115 | 112 | ||
58 | 116 | context_ = ibus_bus_create_input_context(bus_, "nux"); | 113 | if (!(context_ = ibus_bus_create_input_context(bus_, "nux"))) |
59 | 114 | { | ||
60 | 115 | nuxDebugMsg("[IBusIMEContext::IBusIMEContext] Cannot create InputContext"); | ||
61 | 116 | return; | ||
62 | 117 | } | ||
63 | 117 | 118 | ||
64 | 118 | // connect input context signals | 119 | // connect input context signals |
65 | 119 | g_signal_connect(context_, "commit-text", G_CALLBACK(OnCommitText_), this); | 120 | g_signal_connect(context_, "commit-text", G_CALLBACK(OnCommitText_), this); |
66 | 120 | g_signal_connect(context_, "forward-key-event", G_CALLBACK(OnForwardKeyEvent_), this); | ||
67 | 121 | g_signal_connect(context_, "update-preedit-text", G_CALLBACK(OnUpdatePreeditText_), this); | 121 | g_signal_connect(context_, "update-preedit-text", G_CALLBACK(OnUpdatePreeditText_), this); |
68 | 122 | g_signal_connect(context_, "show-preedit-text", G_CALLBACK(OnShowPreeditText_), this); | 122 | g_signal_connect(context_, "show-preedit-text", G_CALLBACK(OnShowPreeditText_), this); |
69 | 123 | g_signal_connect(context_, "hide-preedit-text", G_CALLBACK(OnHidePreeditText_), this); | 123 | g_signal_connect(context_, "hide-preedit-text", G_CALLBACK(OnHidePreeditText_), this); |
70 | @@ -138,7 +138,7 @@ | |||
71 | 138 | 138 | ||
72 | 139 | void IBusIMEContext::DestroyContext() | 139 | void IBusIMEContext::DestroyContext() |
73 | 140 | { | 140 | { |
75 | 141 | nuxDebugMsg("***IBusIMEContext::DestroyContext***"); | 141 | //nuxDebugMsg("***IBusIMEContext::DestroyContext***"); |
76 | 142 | if (!context_) | 142 | if (!context_) |
77 | 143 | return; | 143 | return; |
78 | 144 | 144 | ||
79 | @@ -147,25 +147,27 @@ | |||
80 | 147 | nuxAssert(!context_); | 147 | nuxAssert(!context_); |
81 | 148 | } | 148 | } |
82 | 149 | 149 | ||
83 | 150 | // FIXME Also need to figure out how to get the pop up window | ||
84 | 151 | // for a possible ibus-engine to always be on top of the active | ||
85 | 152 | // window | ||
86 | 153 | void IBusIMEContext::UpdateCursorLocation() | 150 | void IBusIMEContext::UpdateCursorLocation() |
87 | 154 | { | 151 | { |
88 | 155 | nux::Rect strong, weak; | 152 | nux::Rect strong, weak; |
89 | 156 | text_entry_->GetCursorRects(&strong, &weak); | 153 | text_entry_->GetCursorRects(&strong, &weak); |
91 | 157 | nux::Geometry geo = text_entry_->GetGeometry(); | 154 | |
92 | 155 | // Get the position of the TextEntry in the Window. | ||
93 | 156 | nux::Geometry geo = text_entry_->GetAbsoluteGeometry(); | ||
94 | 157 | |||
95 | 158 | // Get the Geometry of the window on the display. | ||
96 | 159 | nux::Geometry window_geo = nux::GetGraphicsDisplay()->GetWindowGeometry(); | ||
97 | 158 | 160 | ||
98 | 159 | ibus_input_context_set_cursor_location(context_, | 161 | ibus_input_context_set_cursor_location(context_, |
103 | 160 | strong.x + geo.x, | 162 | geo.x + window_geo.x + strong.x, |
104 | 161 | strong.y + geo.y, | 163 | geo.y + window_geo.y, |
105 | 162 | strong.width, | 164 | 0, |
106 | 163 | strong.height); | 165 | geo.height); |
107 | 164 | } | 166 | } |
108 | 165 | 167 | ||
109 | 166 | void IBusIMEContext::OnConnected(IBusBus *bus) | 168 | void IBusIMEContext::OnConnected(IBusBus *bus) |
110 | 167 | { | 169 | { |
112 | 168 | nuxDebugMsg("***IBusIMEContext::OnConnected***"); | 170 | //nuxDebugMsg("***IBusIMEContext::OnConnected***"); |
113 | 169 | 171 | ||
114 | 170 | nuxAssert(bus_ == bus); | 172 | nuxAssert(bus_ == bus); |
115 | 171 | nuxAssert(ibus_bus_is_connected(bus_)); | 173 | nuxAssert(ibus_bus_is_connected(bus_)); |
116 | @@ -177,13 +179,15 @@ | |||
117 | 177 | 179 | ||
118 | 178 | void IBusIMEContext::OnDisconnected(IBusBus *bus) | 180 | void IBusIMEContext::OnDisconnected(IBusBus *bus) |
119 | 179 | { | 181 | { |
121 | 180 | nuxDebugMsg("***IBusIMEContext::OnDisonnected***"); | 182 | //nuxDebugMsg("***IBusIMEContext::OnDisonnected***"); |
122 | 183 | |||
123 | 184 | if (context_) | ||
124 | 185 | DestroyContext(); | ||
125 | 181 | } | 186 | } |
126 | 182 | 187 | ||
127 | 183 | void IBusIMEContext::OnCommitText(IBusInputContext *context, IBusText* text) | 188 | void IBusIMEContext::OnCommitText(IBusInputContext *context, IBusText* text) |
128 | 184 | { | 189 | { |
131 | 185 | nuxDebugMsg("***IBusIMEContext::OnCommitText***"); | 190 | //nuxDebugMsg("***IBusIMEContext::OnCommitText::%s***", text->text); |
130 | 186 | printf ("OnCommit %s\n", text->text); | ||
132 | 187 | nuxAssert(context_ == context); | 191 | nuxAssert(context_ == context); |
133 | 188 | 192 | ||
134 | 189 | text_entry_->DeleteSelection(); | 193 | text_entry_->DeleteSelection(); |
135 | @@ -201,39 +205,15 @@ | |||
136 | 201 | } | 205 | } |
137 | 202 | } | 206 | } |
138 | 203 | 207 | ||
139 | 204 | void IBusIMEContext::OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state) | ||
140 | 205 | { | ||
141 | 206 | nuxDebugMsg("***IBusIMEContext::OnForwardKeyEvent***"); | ||
142 | 207 | nuxAssert(context_ == context); | ||
143 | 208 | |||
144 | 209 | int flags = 0; | ||
145 | 210 | |||
146 | 211 | if (state & IBUS_LOCK_MASK) | ||
147 | 212 | flags |= KEY_MODIFIER_CAPS_LOCK; | ||
148 | 213 | if (state & IBUS_CONTROL_MASK) | ||
149 | 214 | flags |= KEY_MODIFIER_CTRL; | ||
150 | 215 | if (state & IBUS_SHIFT_MASK) | ||
151 | 216 | flags |= KEY_MODIFIER_SHIFT; | ||
152 | 217 | if (state & IBUS_MOD1_MASK) | ||
153 | 218 | flags |= KEY_MODIFIER_ALT; | ||
154 | 219 | |||
155 | 220 | int mouse_state = 0; | ||
156 | 221 | if (state & IBUS_BUTTON1_MASK) | ||
157 | 222 | mouse_state |= MOUSE_BUTTON1; | ||
158 | 223 | if (state & IBUS_BUTTON2_MASK) | ||
159 | 224 | mouse_state |= MOUSE_BUTTON2; | ||
160 | 225 | if (state & IBUS_BUTTON3_MASK) | ||
161 | 226 | mouse_state |= MOUSE_BUTTON3; | ||
162 | 227 | |||
163 | 228 | //ForwardKeyEvent(KeyEvent(state & IBUS_RELEASE_MASK ? EVENT_KEY_DOWN : EVENT_KEY_UP, keyval /* todo(jaytaoko): ui::WindowsKeyCodeForGdkKeyCode(keyval)*/, mouse_state, flags)); | ||
164 | 229 | } | ||
165 | 230 | |||
166 | 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) |
167 | 232 | { | 209 | { |
169 | 233 | nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***"); | 210 | //nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***"); |
170 | 234 | nuxAssert(context_ == context); | 211 | nuxAssert(context_ == context); |
171 | 235 | nuxAssert(IBUS_IS_TEXT(text)); | 212 | nuxAssert(IBUS_IS_TEXT(text)); |
172 | 236 | 213 | ||
173 | 214 | if (text_entry_->preedit_.empty()) | ||
174 | 215 | UpdateCursorLocation(); | ||
175 | 216 | |||
176 | 237 | if (visible) | 217 | if (visible) |
177 | 238 | { | 218 | { |
178 | 239 | IBusAttrList* attrs = text->attrs; | 219 | IBusAttrList* attrs = text->attrs; |
179 | @@ -279,50 +259,54 @@ | |||
180 | 279 | text_entry_->preedit_ = preedit; | 259 | text_entry_->preedit_ = preedit; |
181 | 280 | text_entry_->preedit_cursor_ = preedit.length(); | 260 | text_entry_->preedit_cursor_ = preedit.length(); |
182 | 281 | text_entry_->QueueRefresh (true, true); | 261 | text_entry_->QueueRefresh (true, true); |
185 | 282 | text_entry_->sigTextChanged.emit(text_entry_); | 262 | text_entry_->text_changed.emit(text_entry_); |
184 | 283 | UpdateCursorLocation(); | ||
186 | 284 | } | 263 | } |
187 | 285 | } | 264 | } |
188 | 265 | else | ||
189 | 266 | { | ||
190 | 267 | OnHidePreeditText(context_); | ||
191 | 268 | } | ||
192 | 286 | } | 269 | } |
193 | 287 | 270 | ||
194 | 288 | void IBusIMEContext::OnShowPreeditText(IBusInputContext *context) | 271 | void IBusIMEContext::OnShowPreeditText(IBusInputContext *context) |
195 | 289 | { | 272 | { |
197 | 290 | nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***"); | 273 | //nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***"); |
198 | 291 | nuxAssert(context_ == context); | 274 | nuxAssert(context_ == context); |
199 | 292 | } | 275 | } |
200 | 293 | 276 | ||
201 | 294 | void IBusIMEContext::OnHidePreeditText(IBusInputContext *context) | 277 | void IBusIMEContext::OnHidePreeditText(IBusInputContext *context) |
202 | 295 | { | 278 | { |
204 | 296 | nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***"); | 279 | //nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***"); |
205 | 297 | nuxAssert(context_ == context); | 280 | nuxAssert(context_ == context); |
206 | 298 | 281 | ||
207 | 299 | text_entry_->ResetPreedit(); | 282 | text_entry_->ResetPreedit(); |
208 | 300 | text_entry_->QueueRefresh (true, true); | 283 | text_entry_->QueueRefresh (true, true); |
210 | 301 | text_entry_->sigTextChanged.emit(text_entry_); | 284 | text_entry_->text_changed.emit(text_entry_); |
211 | 302 | } | 285 | } |
212 | 303 | 286 | ||
213 | 304 | void IBusIMEContext::OnEnable(IBusInputContext *context) | 287 | void IBusIMEContext::OnEnable(IBusInputContext *context) |
214 | 305 | { | 288 | { |
216 | 306 | nuxDebugMsg("***IBusIMEContext::OnEnable***"); | 289 | //nuxDebugMsg("***IBusIMEContext::OnEnable***"); |
217 | 307 | nuxAssert(context_ == context); | 290 | nuxAssert(context_ == context); |
218 | 308 | 291 | ||
219 | 309 | text_entry_->ime_active_ = true; | 292 | text_entry_->ime_active_ = true; |
220 | 293 | UpdateCursorLocation(); | ||
221 | 310 | } | 294 | } |
222 | 311 | 295 | ||
223 | 312 | void IBusIMEContext::OnDisable(IBusInputContext *context) | 296 | void IBusIMEContext::OnDisable(IBusInputContext *context) |
224 | 313 | { | 297 | { |
226 | 314 | nuxDebugMsg("***IBusIMEContext::OnDisable***"); | 298 | //nuxDebugMsg("***IBusIMEContext::OnDisable***"); |
227 | 315 | nuxAssert(context_ == context); | 299 | nuxAssert(context_ == context); |
228 | 316 | 300 | ||
229 | 317 | text_entry_->ime_active_ = false; | 301 | text_entry_->ime_active_ = false; |
230 | 318 | text_entry_->ResetPreedit(); | 302 | text_entry_->ResetPreedit(); |
231 | 319 | text_entry_->QueueRefresh (true, true); | 303 | text_entry_->QueueRefresh (true, true); |
233 | 320 | text_entry_->sigTextChanged.emit(text_entry_); | 304 | text_entry_->text_changed.emit(text_entry_); |
234 | 321 | } | 305 | } |
235 | 322 | 306 | ||
236 | 323 | void IBusIMEContext::OnDestroy(IBusInputContext *context) | 307 | void IBusIMEContext::OnDestroy(IBusInputContext *context) |
237 | 324 | { | 308 | { |
239 | 325 | nuxDebugMsg("***IBusIMEContext::OnDestroy***"); | 309 | //nuxDebugMsg("***IBusIMEContext::OnDestroy***"); |
240 | 326 | nuxAssert(context_ == context); | 310 | nuxAssert(context_ == context); |
241 | 327 | 311 | ||
242 | 328 | g_object_unref(context_); | 312 | g_object_unref(context_); |
243 | @@ -331,7 +315,7 @@ | |||
244 | 331 | 315 | ||
245 | 332 | void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context, GAsyncResult* res, ProcessKeyEventData *data) | 316 | void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context, GAsyncResult* res, ProcessKeyEventData *data) |
246 | 333 | { | 317 | { |
248 | 334 | nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***"); | 318 | //nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***"); |
249 | 335 | nuxAssert(data->context->context_ == context); | 319 | nuxAssert(data->context->context_ == context); |
250 | 336 | 320 | ||
251 | 337 | GError *error = NULL; | 321 | GError *error = NULL; |
252 | @@ -346,9 +330,14 @@ | |||
253 | 346 | g_error_free (error); | 330 | g_error_free (error); |
254 | 347 | } | 331 | } |
255 | 348 | 332 | ||
256 | 349 | // FIXME Need to forward this event somewhere.. | ||
257 | 350 | if (processed == FALSE) | 333 | if (processed == FALSE) |
259 | 351 | printf ("Processed %i\n", processed); | 334 | { |
260 | 335 | data->context->text_entry_->ProcessKeyEvent(data->event.type(), | ||
261 | 336 | data->event.key_sym(), | ||
262 | 337 | data->event.flags() | IBUS_IGNORED_MASK, | ||
263 | 338 | data->event.character().c_str(), | ||
264 | 339 | 0); | ||
265 | 340 | } | ||
266 | 352 | 341 | ||
267 | 353 | delete data; | 342 | delete data; |
268 | 354 | } | 343 | } |
269 | 355 | 344 | ||
270 | === modified file 'Nux/InputMethodIBus.h' | |||
271 | --- Nux/InputMethodIBus.h 2012-02-12 00:39:29 +0000 | |||
272 | +++ Nux/InputMethodIBus.h 2012-02-23 05:24:18 +0000 | |||
273 | @@ -1,14 +1,14 @@ | |||
275 | 1 | #ifndef INPUTMETHODIBUS_H | 1 | #ifndef INPUTMETHODIBUS_H |
276 | 2 | #define INPUTMETHODIBUS_H | 2 | #define INPUTMETHODIBUS_H |
277 | 3 | 3 | ||
279 | 4 | #include <Nux/TextEntryIM.h> | 4 | #include <Nux/TextEntry.h> |
280 | 5 | #include <ibus.h> | 5 | #include <ibus.h> |
281 | 6 | 6 | ||
282 | 7 | namespace nux | 7 | namespace nux |
283 | 8 | { | 8 | { |
284 | 9 | 9 | ||
285 | 10 | class IBusIMEContext; | 10 | class IBusIMEContext; |
287 | 11 | class TextEntryIM; | 11 | class TextEntry; |
288 | 12 | 12 | ||
289 | 13 | // FIXME This class should be reworked to replace the mouse_state | 13 | // FIXME This class should be reworked to replace the mouse_state |
290 | 14 | // with the hardware key_code. | 14 | // with the hardware key_code. |
291 | @@ -17,19 +17,21 @@ | |||
292 | 17 | public: | 17 | public: |
293 | 18 | 18 | ||
294 | 19 | KeyEvent(NuxEventType type, | 19 | KeyEvent(NuxEventType type, |
297 | 20 | unsigned int key_code, | 20 | unsigned int key_sym, |
298 | 21 | unsigned int mouse_state, unsigned int event_flags) | 21 | unsigned int key_code, unsigned int event_flags, const char* character) |
299 | 22 | : type_(type) | 22 | : type_(type) |
300 | 23 | , key_sym_(key_sym) | ||
301 | 23 | , key_code_(key_code) | 24 | , key_code_(key_code) |
302 | 24 | , key_modifiers_(event_flags) | 25 | , key_modifiers_(event_flags) |
304 | 25 | , mouse_state_(mouse_state) | 26 | , character_(character) |
305 | 26 | { | 27 | { |
306 | 27 | } | 28 | } |
307 | 28 | 29 | ||
308 | 29 | NuxEventType type() const {return type_;} | 30 | NuxEventType type() const {return type_;} |
309 | 31 | unsigned int key_sym() const {return key_sym_;} | ||
310 | 30 | unsigned int key_code() const {return key_code_;} | 32 | unsigned int key_code() const {return key_code_;} |
311 | 31 | unsigned int flags() const {return key_modifiers_;} | 33 | unsigned int flags() const {return key_modifiers_;} |
313 | 32 | unsigned int MouseState() const {return mouse_state_;} | 34 | std::string character() const {return character_;} |
314 | 33 | 35 | ||
315 | 34 | bool IsShiftDown() const { return (key_modifiers_ & KEY_MODIFIER_SHIFT) != 0; } | 36 | bool IsShiftDown() const { return (key_modifiers_ & KEY_MODIFIER_SHIFT) != 0; } |
316 | 35 | bool IsControlDown() const { return (key_modifiers_ & KEY_MODIFIER_CTRL) != 0; } | 37 | bool IsControlDown() const { return (key_modifiers_ & KEY_MODIFIER_CTRL) != 0; } |
317 | @@ -38,9 +40,10 @@ | |||
318 | 38 | 40 | ||
319 | 39 | private: | 41 | private: |
320 | 40 | EventType type_; | 42 | EventType type_; |
321 | 43 | unsigned int key_sym_; | ||
322 | 41 | unsigned int key_code_; | 44 | unsigned int key_code_; |
323 | 42 | unsigned int key_modifiers_; | 45 | unsigned int key_modifiers_; |
325 | 43 | unsigned int mouse_state_; | 46 | std::string character_; |
326 | 44 | 47 | ||
327 | 45 | KeyEvent(const KeyEvent&); | 48 | KeyEvent(const KeyEvent&); |
328 | 46 | void operator = (const KeyEvent&); | 49 | void operator = (const KeyEvent&); |
329 | @@ -53,7 +56,7 @@ | |||
330 | 53 | ProcessKeyEventData(IBusIMEContext* context, | 56 | ProcessKeyEventData(IBusIMEContext* context, |
331 | 54 | const KeyEvent& event) | 57 | const KeyEvent& event) |
332 | 55 | : context(context) | 58 | : context(context) |
334 | 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()) |
335 | 57 | { | 60 | { |
336 | 58 | 61 | ||
337 | 59 | } | 62 | } |
338 | @@ -65,7 +68,7 @@ | |||
339 | 65 | class IBusIMEContext | 68 | class IBusIMEContext |
340 | 66 | { | 69 | { |
341 | 67 | public: | 70 | public: |
343 | 68 | explicit IBusIMEContext(TextEntryIM* text_entry); | 71 | explicit IBusIMEContext(TextEntry* text_entry); |
344 | 69 | virtual ~IBusIMEContext(); | 72 | virtual ~IBusIMEContext(); |
345 | 70 | 73 | ||
346 | 71 | // views::IMEContext implementations: | 74 | // views::IMEContext implementations: |
347 | @@ -87,7 +90,7 @@ | |||
348 | 87 | void OnConnected(IBusBus *bus); | 90 | void OnConnected(IBusBus *bus); |
349 | 88 | 91 | ||
350 | 89 | //CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*); | 92 | //CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*); |
352 | 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);} |
353 | 91 | void OnDisconnected(IBusBus *bus); | 94 | void OnDisconnected(IBusBus *bus); |
354 | 92 | 95 | ||
355 | 93 | // // Event handlers for IBusIMEContext: | 96 | // // Event handlers for IBusIMEContext: |
356 | @@ -95,10 +98,6 @@ | |||
357 | 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);} |
358 | 96 | void OnCommitText(IBusInputContext *context, IBusText* text); | 99 | void OnCommitText(IBusInputContext *context, IBusText* text); |
359 | 97 | 100 | ||
360 | 98 | //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnForwardKeyEvent, IBusInputContext*, guint, guint, guint); | ||
361 | 99 | static void OnForwardKeyEvent_(IBusInputContext* context, guint keyval, guint keycode, guint state, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnForwardKeyEvent(context, keyval, keycode, state);} | ||
362 | 100 | void OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state); | ||
363 | 101 | |||
364 | 102 | //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText, IBusInputContext*, IBusText*, guint, gboolean); | 101 | //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText, IBusInputContext*, IBusText*, guint, gboolean); |
365 | 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);} |
366 | 104 | void OnUpdatePreeditText(IBusInputContext *context, IBusText* text, guint cursor_pos, gboolean visible); | 103 | void OnUpdatePreeditText(IBusInputContext *context, IBusText* text, guint cursor_pos, gboolean visible); |
367 | @@ -127,7 +126,7 @@ | |||
368 | 127 | GAsyncResult* res, | 126 | GAsyncResult* res, |
369 | 128 | ProcessKeyEventData* data); | 127 | ProcessKeyEventData* data); |
370 | 129 | 128 | ||
372 | 130 | TextEntryIM* text_entry_; | 129 | TextEntry* text_entry_; |
373 | 131 | IBusInputContext* context_; | 130 | IBusInputContext* context_; |
374 | 132 | bool is_focused_; | 131 | bool is_focused_; |
375 | 133 | 132 | ||
376 | 134 | 133 | ||
377 | === modified file 'Nux/Makefile.am' | |||
378 | --- Nux/Makefile.am 2012-02-13 15:22:30 +0000 | |||
379 | +++ Nux/Makefile.am 2012-02-23 05:24:18 +0000 | |||
380 | @@ -15,13 +15,15 @@ | |||
381 | 15 | -DG_LOG_DOMAIN=\"Nux\" \ | 15 | -DG_LOG_DOMAIN=\"Nux\" \ |
382 | 16 | $(GCC_FLAGS) \ | 16 | $(GCC_FLAGS) \ |
383 | 17 | $(NUX_CFLAGS) \ | 17 | $(NUX_CFLAGS) \ |
384 | 18 | $(IBUS_CFLAGS) \ | ||
385 | 18 | $(MAINTAINER_CFLAGS) | 19 | $(MAINTAINER_CFLAGS) |
386 | 19 | 20 | ||
387 | 20 | libnux_@NUX_API_VERSION@_la_LIBADD = \ | 21 | libnux_@NUX_API_VERSION@_la_LIBADD = \ |
388 | 21 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ | 22 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
389 | 22 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ | 23 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ |
390 | 23 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ | 24 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ |
392 | 24 | $(NUX_LIBS) | 25 | $(NUX_LIBS) \ |
393 | 26 | $(IBUS_LIBS) | ||
394 | 25 | 27 | ||
395 | 26 | libnux_@NUX_API_VERSION@_la_LDFLAGS = \ | 28 | libnux_@NUX_API_VERSION@_la_LDFLAGS = \ |
396 | 27 | $(NUX_LT_LDFLAGS) | 29 | $(NUX_LT_LDFLAGS) |
397 | @@ -93,6 +95,7 @@ | |||
398 | 93 | $(srcdir)/SystemThread.cpp \ | 95 | $(srcdir)/SystemThread.cpp \ |
399 | 94 | $(srcdir)/TabView.cpp \ | 96 | $(srcdir)/TabView.cpp \ |
400 | 95 | $(srcdir)/TextEntry.cpp \ | 97 | $(srcdir)/TextEntry.cpp \ |
401 | 98 | $(srcdir)/InputMethodIBus.cpp \ | ||
402 | 96 | $(srcdir)/TextLoader.cpp \ | 99 | $(srcdir)/TextLoader.cpp \ |
403 | 97 | $(srcdir)/TextureArea.cpp \ | 100 | $(srcdir)/TextureArea.cpp \ |
404 | 98 | $(srcdir)/Timeline.cpp \ | 101 | $(srcdir)/Timeline.cpp \ |
405 | @@ -178,6 +181,7 @@ | |||
406 | 178 | $(srcdir)/SystemThread.h \ | 181 | $(srcdir)/SystemThread.h \ |
407 | 179 | $(srcdir)/TabView.h \ | 182 | $(srcdir)/TabView.h \ |
408 | 180 | $(srcdir)/TextEntry.h \ | 183 | $(srcdir)/TextEntry.h \ |
409 | 184 | $(srcdir)/InputMethodIBus.h \ | ||
410 | 181 | $(srcdir)/TextLoader.h \ | 185 | $(srcdir)/TextLoader.h \ |
411 | 182 | $(srcdir)/TextureArea.h \ | 186 | $(srcdir)/TextureArea.h \ |
412 | 183 | $(srcdir)/Timeline.h \ | 187 | $(srcdir)/Timeline.h \ |
413 | 184 | 188 | ||
414 | === modified file 'Nux/TextEntry.cpp' | |||
415 | --- Nux/TextEntry.cpp 2012-02-18 21:32:06 +0000 | |||
416 | +++ Nux/TextEntry.cpp 2012-02-23 05:24:18 +0000 | |||
417 | @@ -76,8 +76,8 @@ | |||
418 | 76 | return result; | 76 | return result; |
419 | 77 | } | 77 | } |
420 | 78 | 78 | ||
423 | 79 | // Calculate pixel size based on the Windows DPI of 96 for compatibility | 79 | // Calculate pixel size based on the Windows DPI of 96 for compatibility |
424 | 80 | // reasons. | 80 | // reasons. |
425 | 81 | CairoFont::CairoFont(const std::string &family, | 81 | CairoFont::CairoFont(const std::string &family, |
426 | 82 | /*PangoFontDescription *font,*/ | 82 | /*PangoFontDescription *font,*/ |
427 | 83 | double pt_size, | 83 | double pt_size, |
428 | @@ -112,10 +112,10 @@ | |||
429 | 112 | TextEntry::TextEntry(const char* text, NUX_FILE_LINE_DECL) | 112 | TextEntry::TextEntry(const char* text, NUX_FILE_LINE_DECL) |
430 | 113 | : View(NUX_FILE_LINE_PARAM) | 113 | : View(NUX_FILE_LINE_PARAM) |
431 | 114 | , _size_match_text(true) | 114 | , _size_match_text(true) |
436 | 115 | , _texture2D(NULL) | 115 | , _texture2D(nullptr) |
437 | 116 | , canvas_(NULL) | 116 | , canvas_(nullptr) |
438 | 117 | , cached_layout_(NULL) | 117 | , cached_layout_(nullptr) |
439 | 118 | , preedit_attrs_(NULL) | 118 | , preedit_attrs_(nullptr) |
440 | 119 | , completion_color_(color::Gray) | 119 | , completion_color_(color::Gray) |
441 | 120 | , last_dblclick_time_(0) | 120 | , last_dblclick_time_(0) |
442 | 121 | , cursor_(0) | 121 | , cursor_(0) |
443 | @@ -152,7 +152,9 @@ | |||
444 | 152 | , align_(CairoGraphics::ALIGN_LEFT) | 152 | , align_(CairoGraphics::ALIGN_LEFT) |
445 | 153 | #if defined(NUX_OS_LINUX) | 153 | #if defined(NUX_OS_LINUX) |
446 | 154 | , caret_cursor_(None) | 154 | , caret_cursor_(None) |
448 | 155 | #endif | 155 | , ime_(new IBusIMEContext(this)) |
449 | 156 | #endif | ||
450 | 157 | , ime_active_(false) | ||
451 | 156 | , text_input_mode_(false) | 158 | , text_input_mode_(false) |
452 | 157 | , key_nav_mode_(false) | 159 | , key_nav_mode_(false) |
453 | 158 | { | 160 | { |
454 | @@ -182,8 +184,6 @@ | |||
455 | 182 | 184 | ||
456 | 183 | TextEntry::~TextEntry() | 185 | TextEntry::~TextEntry() |
457 | 184 | { | 186 | { |
458 | 185 | ResetLayout(); | ||
459 | 186 | |||
460 | 187 | if (cursor_blink_timer_) | 187 | if (cursor_blink_timer_) |
461 | 188 | g_source_remove(cursor_blink_timer_); | 188 | g_source_remove(cursor_blink_timer_); |
462 | 189 | 189 | ||
463 | @@ -191,8 +191,10 @@ | |||
464 | 191 | if (_texture2D) | 191 | if (_texture2D) |
465 | 192 | _texture2D->UnReference(); | 192 | _texture2D->UnReference(); |
466 | 193 | 193 | ||
469 | 194 | if (canvas_) | 194 | #if defined(NUX_OS_LINUX) |
470 | 195 | delete canvas_; | 195 | if (ime_) |
471 | 196 | delete ime_; | ||
472 | 197 | #endif | ||
473 | 196 | } | 198 | } |
474 | 197 | 199 | ||
475 | 198 | void TextEntry::PreLayoutManagement() | 200 | void TextEntry::PreLayoutManagement() |
476 | @@ -242,7 +244,7 @@ | |||
477 | 242 | { | 244 | { |
478 | 243 | SelectLine(); | 245 | SelectLine(); |
479 | 244 | } | 246 | } |
481 | 245 | else if (event_type == NUX_MOUSE_DOUBLECLICK) | 247 | else if (event_type == NUX_MOUSE_DOUBLECLICK && !ime_active_) |
482 | 246 | { | 248 | { |
483 | 247 | SelectWord(); | 249 | SelectWord(); |
484 | 248 | last_dblclick_time_ = current_time; | 250 | last_dblclick_time_ = current_time; |
485 | @@ -281,26 +283,25 @@ | |||
486 | 281 | const char* character , /*character*/ | 283 | const char* character , /*character*/ |
487 | 282 | unsigned short keyCount /*key repeat count*/) | 284 | unsigned short keyCount /*key repeat count*/) |
488 | 283 | { | 285 | { |
489 | 286 | bool retval = FALSE; | ||
490 | 287 | |||
491 | 284 | if (event_type == NUX_KEYDOWN) | 288 | if (event_type == NUX_KEYDOWN) |
492 | 285 | text_input_mode_ = true; | 289 | text_input_mode_ = true; |
493 | 286 | 290 | ||
494 | 287 | // GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent()); | ||
495 | 288 | // ASSERT(gdk_event); | ||
496 | 289 | // | ||
497 | 290 | // Event::Type type = event.GetType(); | ||
498 | 291 | // Cause the cursor to stop blinking for a while. | ||
499 | 292 | cursor_blink_status_ = 4; | 291 | cursor_blink_status_ = 4; |
500 | 293 | 292 | ||
507 | 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 |
508 | 295 | // { | 294 | nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent(); |
509 | 296 | // need_im_reset_ = true; | 295 | KeyEvent event((NuxEventType)event_type, keysym,cur_event.x11_keycode, state, character); |
510 | 297 | // QueueRefresh(false, true); | 296 | |
511 | 298 | // return EVENT_RESULT_HANDLED; | 297 | #if defined(NUX_OS_LINUX) |
512 | 299 | // } | 298 | retval = ime_->FilterKeyEvent(event); |
513 | 299 | #endif | ||
514 | 300 | 300 | ||
515 | 301 | if (event_type == NUX_KEYUP) | 301 | if (event_type == NUX_KEYUP) |
516 | 302 | return; | 302 | return; |
518 | 303 | 303 | ||
519 | 304 | |||
520 | 304 | // we need to ignore some characters | 305 | // we need to ignore some characters |
521 | 305 | if (keysym == NUX_VK_TAB) | 306 | if (keysym == NUX_VK_TAB) |
522 | 306 | return; | 307 | return; |
523 | @@ -316,8 +317,7 @@ | |||
524 | 316 | bool ctrl = (state & NUX_STATE_CTRL); | 317 | bool ctrl = (state & NUX_STATE_CTRL); |
525 | 317 | 318 | ||
526 | 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); |
529 | 319 | 320 | if (event_type == NUX_KEYDOWN && !retval) | |
528 | 320 | if (event_type == NUX_KEYDOWN) | ||
530 | 321 | { | 321 | { |
531 | 322 | if (keyval == NUX_VK_LEFT) | 322 | if (keyval == NUX_VK_LEFT) |
532 | 323 | { | 323 | { |
533 | @@ -428,7 +428,7 @@ | |||
534 | 428 | // } | 428 | // } |
535 | 429 | } | 429 | } |
536 | 430 | 430 | ||
538 | 431 | if (character != 0 && (strlen(character) != 0)) | 431 | if (!retval && character != 0 && (strlen(character) != 0)) |
539 | 432 | { | 432 | { |
540 | 433 | EnterText(character); | 433 | EnterText(character); |
541 | 434 | } | 434 | } |
542 | @@ -456,7 +456,7 @@ | |||
543 | 456 | { | 456 | { |
544 | 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); |
545 | 458 | } | 458 | } |
547 | 459 | 459 | ||
548 | 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) |
549 | 461 | { | 461 | { |
550 | 462 | #if defined(NUX_OS_LINUX) | 462 | #if defined(NUX_OS_LINUX) |
551 | @@ -464,7 +464,7 @@ | |||
552 | 464 | { | 464 | { |
553 | 465 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); | 465 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
554 | 466 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); | 466 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
556 | 467 | 467 | ||
557 | 468 | if (display && window) | 468 | if (display && window) |
558 | 469 | { | 469 | { |
559 | 470 | caret_cursor_ = XCreateFontCursor(display, XC_xterm); | 470 | caret_cursor_ = XCreateFontCursor(display, XC_xterm); |
560 | @@ -473,7 +473,7 @@ | |||
561 | 473 | } | 473 | } |
562 | 474 | #endif | 474 | #endif |
563 | 475 | } | 475 | } |
565 | 476 | 476 | ||
566 | 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) |
567 | 478 | { | 478 | { |
568 | 479 | #if defined(NUX_OS_LINUX) | 479 | #if defined(NUX_OS_LINUX) |
569 | @@ -481,7 +481,7 @@ | |||
570 | 481 | { | 481 | { |
571 | 482 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); | 482 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
572 | 483 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); | 483 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
574 | 484 | 484 | ||
575 | 485 | if (display && window) | 485 | if (display && window) |
576 | 486 | { | 486 | { |
577 | 487 | XUndefineCursor(display, window->GetInputWindowId()); | 487 | XUndefineCursor(display, window->GetInputWindowId()); |
578 | @@ -559,9 +559,9 @@ | |||
579 | 559 | // intentionally left empty | 559 | // intentionally left empty |
580 | 560 | } | 560 | } |
581 | 561 | 561 | ||
583 | 562 | void TextEntry::SetText(const char *text) | 562 | void TextEntry::SetText(const char* text) |
584 | 563 | { | 563 | { |
586 | 564 | const char *end = NULL; | 564 | const char* end = NULL; |
587 | 565 | g_utf8_validate(text, -1, &end); | 565 | g_utf8_validate(text, -1, &end); |
588 | 566 | 566 | ||
589 | 567 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); | 567 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
590 | @@ -574,7 +574,7 @@ | |||
591 | 574 | need_im_reset_ = true; | 574 | need_im_reset_ = true; |
592 | 575 | //ResetImContext(); | 575 | //ResetImContext(); |
593 | 576 | QueueRefresh(true, true); | 576 | QueueRefresh(true, true); |
595 | 577 | sigTextChanged.emit(this); | 577 | text_changed.emit(this); |
596 | 578 | } | 578 | } |
597 | 579 | 579 | ||
598 | 580 | std::string const& TextEntry::GetText() const | 580 | std::string const& TextEntry::GetText() const |
599 | @@ -582,9 +582,28 @@ | |||
600 | 582 | return text_; | 582 | return text_; |
601 | 583 | } | 583 | } |
602 | 584 | 584 | ||
606 | 585 | void TextEntry::SetCompletion(const char *text) | 585 | std::string TextEntry::GetTextSelection() const |
607 | 586 | { | 586 | { |
608 | 587 | const char *end = NULL; | 587 | if (text_.size() == 0) |
609 | 588 | { | ||
610 | 589 | return std::string(""); | ||
611 | 590 | } | ||
612 | 591 | |||
613 | 592 | int selection_start = 0; | ||
614 | 593 | int selection_end = 0; | ||
615 | 594 | if (GetSelectionBounds(&selection_start, &selection_end)) | ||
616 | 595 | { | ||
617 | 596 | return text_.substr(selection_start, selection_end); | ||
618 | 597 | } | ||
619 | 598 | else | ||
620 | 599 | { | ||
621 | 600 | return std::string(""); | ||
622 | 601 | } | ||
623 | 602 | } | ||
624 | 603 | |||
625 | 604 | void TextEntry::SetCompletion(const char* text) | ||
626 | 605 | { | ||
627 | 606 | const char* end = NULL; | ||
628 | 588 | g_utf8_validate(text, -1, &end); | 607 | g_utf8_validate(text, -1, &end); |
629 | 589 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); | 608 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
630 | 590 | if (txt == completion_) | 609 | if (txt == completion_) |
631 | @@ -628,7 +647,7 @@ | |||
632 | 628 | void TextEntry::MainDraw() | 647 | void TextEntry::MainDraw() |
633 | 629 | { | 648 | { |
634 | 630 | 649 | ||
636 | 631 | CairoGraphics *edit_canvas = EnsureCanvas(); | 650 | CairoGraphics* edit_canvas = EnsureCanvas(); |
637 | 632 | 651 | ||
638 | 633 | if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) | 652 | if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) |
639 | 634 | { | 653 | { |
640 | @@ -677,6 +696,9 @@ | |||
641 | 677 | if (!readonly_ /*&& im_context_*/) | 696 | if (!readonly_ /*&& im_context_*/) |
642 | 678 | { | 697 | { |
643 | 679 | need_im_reset_ = true; | 698 | need_im_reset_ = true; |
644 | 699 | #if defined(NUX_OS_LINUX) | ||
645 | 700 | ime_->Focus(); | ||
646 | 701 | #endif | ||
647 | 680 | //gtk_im_context_focus_in(im_context_); | 702 | //gtk_im_context_focus_in(im_context_); |
648 | 681 | //UpdateIMCursorLocation(); | 703 | //UpdateIMCursorLocation(); |
649 | 682 | } | 704 | } |
650 | @@ -696,6 +718,9 @@ | |||
651 | 696 | if (!readonly_ /*&& im_context_*/) | 718 | if (!readonly_ /*&& im_context_*/) |
652 | 697 | { | 719 | { |
653 | 698 | need_im_reset_ = true; | 720 | need_im_reset_ = true; |
654 | 721 | #if defined(NUX_OS_LINUX) | ||
655 | 722 | ime_->Blur(); | ||
656 | 723 | #endif | ||
657 | 699 | //gtk_im_context_focus_out(im_context_); | 724 | //gtk_im_context_focus_out(im_context_); |
658 | 700 | } | 725 | } |
659 | 701 | cursor_visible_ = false; // hide cursor when losing focus | 726 | cursor_visible_ = false; // hide cursor when losing focus |
660 | @@ -733,7 +758,7 @@ | |||
661 | 733 | int display_width = GetBaseWidth() - kInnerBorderX * 2; | 758 | int display_width = GetBaseWidth() - kInnerBorderX * 2; |
662 | 734 | int display_height = GetBaseHeight() - kInnerBorderY * 2; | 759 | int display_height = GetBaseHeight() - kInnerBorderY * 2; |
663 | 735 | 760 | ||
665 | 736 | PangoLayout *layout = EnsureLayout(); | 761 | PangoLayout* layout = EnsureLayout(); |
666 | 737 | int text_width, text_height; | 762 | int text_width, text_height; |
667 | 738 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | 763 | pango_layout_get_pixel_size(layout, &text_width, &text_height); |
668 | 739 | 764 | ||
669 | @@ -1199,7 +1224,7 @@ | |||
670 | 1199 | 1224 | ||
671 | 1200 | int pre_completion_length = tmp_string.length(); | 1225 | int pre_completion_length = tmp_string.length(); |
672 | 1201 | 1226 | ||
674 | 1202 | if (!completion_.empty() && !wrap_) | 1227 | if (!completion_.empty() && !wrap_ && preedit_.empty()) |
675 | 1203 | { | 1228 | { |
676 | 1204 | tmp_string = text_ + completion_; | 1229 | tmp_string = text_ + completion_; |
677 | 1205 | } | 1230 | } |
678 | @@ -1225,7 +1250,7 @@ | |||
679 | 1225 | } | 1250 | } |
680 | 1226 | if (!completion_.empty() && !wrap_) | 1251 | if (!completion_.empty() && !wrap_) |
681 | 1227 | { | 1252 | { |
683 | 1228 | attr = pango_attr_foreground_new(65535 * completion_color_.red, | 1253 | attr = pango_attr_foreground_new(65535 * completion_color_.red, |
684 | 1229 | 65535 * completion_color_.green, | 1254 | 65535 * completion_color_.green, |
685 | 1230 | 65535 * completion_color_.blue); | 1255 | 65535 * completion_color_.blue); |
686 | 1231 | attr->start_index = static_cast<guint>(pre_completion_length); | 1256 | attr->start_index = static_cast<guint>(pre_completion_length); |
687 | @@ -1455,7 +1480,7 @@ | |||
688 | 1455 | } | 1480 | } |
689 | 1456 | 1481 | ||
690 | 1457 | ResetLayout(); | 1482 | ResetLayout(); |
692 | 1458 | sigTextChanged.emit(this); | 1483 | text_changed.emit(this); |
693 | 1459 | } | 1484 | } |
694 | 1460 | 1485 | ||
695 | 1461 | void TextEntry::DeleteText(int start, int end) | 1486 | void TextEntry::DeleteText(int start, int end) |
696 | @@ -1486,7 +1511,7 @@ | |||
697 | 1486 | selection_bound_ -= (end - start); | 1511 | selection_bound_ -= (end - start); |
698 | 1487 | 1512 | ||
699 | 1488 | ResetLayout(); | 1513 | ResetLayout(); |
701 | 1489 | sigTextChanged.emit(this); | 1514 | text_changed.emit(this); |
702 | 1490 | } | 1515 | } |
703 | 1491 | 1516 | ||
704 | 1492 | void TextEntry::SelectWord() | 1517 | void TextEntry::SelectWord() |
705 | @@ -1532,6 +1557,11 @@ | |||
706 | 1532 | QueueRefresh(true, true); | 1557 | QueueRefresh(true, true); |
707 | 1533 | } | 1558 | } |
708 | 1534 | 1559 | ||
709 | 1560 | bool TextEntry::im_active() | ||
710 | 1561 | { | ||
711 | 1562 | return ime_active_; | ||
712 | 1563 | } | ||
713 | 1564 | |||
714 | 1535 | void TextEntry::DeleteSelection() | 1565 | void TextEntry::DeleteSelection() |
715 | 1536 | { | 1566 | { |
716 | 1537 | int start, end; | 1567 | int start, end; |
717 | @@ -1668,8 +1698,8 @@ | |||
718 | 1668 | continue; | 1698 | continue; |
719 | 1669 | if (end_index < line->start_index) | 1699 | if (end_index < line->start_index) |
720 | 1670 | break; | 1700 | break; |
723 | 1671 | draw_start = Max<int>(start_index, line->start_index); | 1701 | draw_start = std::max<int>(start_index, line->start_index); |
724 | 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); |
725 | 1673 | pango_layout_line_get_x_ranges(line, draw_start, draw_end, | 1703 | pango_layout_line_get_x_ranges(line, draw_start, draw_end, |
726 | 1674 | &ranges, &n_ranges); | 1704 | &ranges, &n_ranges); |
727 | 1675 | pango_layout_line_get_pixel_extents(line, NULL, &line_extents); | 1705 | pango_layout_line_get_pixel_extents(line, NULL, &line_extents); |
728 | @@ -1739,8 +1769,8 @@ | |||
729 | 1739 | nuxAssert(count); | 1769 | nuxAssert(count); |
730 | 1740 | nuxAssert(preedit_.length() == 0); | 1770 | nuxAssert(preedit_.length() == 0); |
731 | 1741 | 1771 | ||
734 | 1742 | PangoLayout *layout = EnsureLayout(); | 1772 | PangoLayout* layout = EnsureLayout(); |
735 | 1743 | const char *text = pango_layout_get_text(layout); | 1773 | const char* text = pango_layout_get_text(layout); |
736 | 1744 | int index = TextIndexToLayoutIndex(current_index, false); | 1774 | int index = TextIndexToLayoutIndex(current_index, false); |
737 | 1745 | int new_index = 0; | 1775 | int new_index = 0; |
738 | 1746 | int new_trailing = 0; | 1776 | int new_trailing = 0; |
739 | @@ -1780,11 +1810,11 @@ | |||
740 | 1780 | 1810 | ||
741 | 1781 | // The cursor movement direction shall be determined by the direction of | 1811 | // The cursor movement direction shall be determined by the direction of |
742 | 1782 | // current text line. | 1812 | // current text line. |
744 | 1783 | PangoLayout *layout = EnsureLayout(); | 1813 | PangoLayout* layout = EnsureLayout(); |
745 | 1784 | int n_log_attrs; | 1814 | int n_log_attrs; |
747 | 1785 | PangoLogAttr *log_attrs; | 1815 | PangoLogAttr* log_attrs; |
748 | 1786 | pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs); | 1816 | pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs); |
750 | 1787 | const char *text = pango_layout_get_text(layout); | 1817 | const char* text = pango_layout_get_text(layout); |
751 | 1788 | int index = TextIndexToLayoutIndex(current_index, false); | 1818 | int index = TextIndexToLayoutIndex(current_index, false); |
752 | 1789 | int line_index; | 1819 | int line_index; |
753 | 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); |
754 | @@ -1797,12 +1827,12 @@ | |||
755 | 1797 | } | 1827 | } |
756 | 1798 | 1828 | ||
757 | 1799 | #if PANGO_VERSION_CHECK(1,16,0) | 1829 | #if PANGO_VERSION_CHECK(1,16,0) |
759 | 1800 | PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); | 1830 | PangoLayoutLine* line = pango_layout_get_line_readonly(layout, line_index); |
760 | 1801 | #else | 1831 | #else |
762 | 1802 | PangoLayoutLine *line = pango_layout_get_line(layout, line_index); | 1832 | PangoLayoutLine* line = pango_layout_get_line(layout, line_index); |
763 | 1803 | #endif | 1833 | #endif |
764 | 1804 | bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); | 1834 | bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); |
766 | 1805 | const char *ptr = text + index; | 1835 | const char* ptr = text + index; |
767 | 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)); |
768 | 1807 | while (count != 0) | 1837 | while (count != 0) |
769 | 1808 | { | 1838 | { |
770 | @@ -2055,12 +2085,17 @@ | |||
771 | 2055 | return Clamp(index, 0, static_cast<int>(text_.length())); | 2085 | return Clamp(index, 0, static_cast<int>(text_.length())); |
772 | 2056 | } | 2086 | } |
773 | 2057 | 2087 | ||
775 | 2058 | bool TextEntry::GetSelectionBounds(int *start, int *end) | 2088 | bool TextEntry::GetSelectionBounds(int* start, int* end) const |
776 | 2059 | { | 2089 | { |
777 | 2060 | if (start) | 2090 | if (start) |
779 | 2061 | *start = Min<int>(selection_bound_, cursor_); | 2091 | { |
780 | 2092 | *start = std::min<int>(selection_bound_, cursor_); | ||
781 | 2093 | } | ||
782 | 2094 | |||
783 | 2062 | if (end) | 2095 | if (end) |
785 | 2063 | *end = Max<int>(selection_bound_, cursor_); | 2096 | { |
786 | 2097 | *end = std::max<int>(selection_bound_, cursor_); | ||
787 | 2098 | } | ||
788 | 2064 | 2099 | ||
789 | 2065 | return (selection_bound_ != cursor_); | 2100 | return (selection_bound_ != cursor_); |
790 | 2066 | } | 2101 | } |
791 | @@ -2108,7 +2143,7 @@ | |||
792 | 2108 | unsigned int key_sym, | 2143 | unsigned int key_sym, |
793 | 2109 | const char* character) | 2144 | const char* character) |
794 | 2110 | { | 2145 | { |
796 | 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)) |
797 | 2112 | { | 2147 | { |
798 | 2113 | if (key_sym == NUX_VK_ENTER || | 2148 | if (key_sym == NUX_VK_ENTER || |
799 | 2114 | key_sym == NUX_KP_ENTER || | 2149 | key_sym == NUX_KP_ENTER || |
800 | @@ -2124,7 +2159,7 @@ | |||
801 | 2124 | } | 2159 | } |
802 | 2125 | } | 2160 | } |
803 | 2126 | 2161 | ||
805 | 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)) |
806 | 2128 | { | 2163 | { |
807 | 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) |
808 | 2130 | if (key_sym == NUX_VK_UP || | 2165 | if (key_sym == NUX_VK_UP || |
809 | @@ -2142,4 +2177,17 @@ | |||
810 | 2142 | 2177 | ||
811 | 2143 | return true; | 2178 | return true; |
812 | 2144 | } | 2179 | } |
813 | 2180 | |||
814 | 2181 | /// Public API | ||
815 | 2182 | |||
816 | 2183 | void TextEntry::MoveCursorToLineStart() | ||
817 | 2184 | { | ||
818 | 2185 | MoveCursor(DISPLAY_LINE_ENDS, -1, 0); | ||
819 | 2186 | } | ||
820 | 2187 | |||
821 | 2188 | void TextEntry::MoveCursorToLineEnd() | ||
822 | 2189 | { | ||
823 | 2190 | MoveCursor(DISPLAY_LINE_ENDS, 1, 0); | ||
824 | 2191 | } | ||
825 | 2192 | |||
826 | 2145 | } | 2193 | } |
827 | 2146 | 2194 | ||
828 | === modified file 'Nux/TextEntry.h' | |||
829 | --- Nux/TextEntry.h 2012-02-12 02:24:29 +0000 | |||
830 | +++ Nux/TextEntry.h 2012-02-23 05:24:18 +0000 | |||
831 | @@ -25,10 +25,14 @@ | |||
832 | 25 | #include "pango/pango.h" | 25 | #include "pango/pango.h" |
833 | 26 | #include "pango/pangocairo.h" | 26 | #include "pango/pangocairo.h" |
834 | 27 | #include "NuxImage/CairoGraphics.h" | 27 | #include "NuxImage/CairoGraphics.h" |
835 | 28 | #if defined(NUX_OS_LINUX) | ||
836 | 29 | #include "InputMethodIBus.h" | ||
837 | 30 | #endif | ||
838 | 28 | 31 | ||
839 | 29 | namespace nux | 32 | namespace nux |
840 | 30 | { | 33 | { |
841 | 31 | class CairoGraphics; | 34 | class CairoGraphics; |
842 | 35 | class IBusIMEContext; | ||
843 | 32 | 36 | ||
844 | 33 | class CairoFont | 37 | class CairoFont |
845 | 34 | { | 38 | { |
846 | @@ -81,9 +85,9 @@ | |||
847 | 81 | ~TextEntry(); | 85 | ~TextEntry(); |
848 | 82 | 86 | ||
849 | 83 | Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type); | 87 | Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type); |
853 | 84 | virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw); | 88 | virtual void Draw(GraphicsEngine& graphics_engine, bool force_draw); |
854 | 85 | virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw); | 89 | virtual void DrawContent(GraphicsEngine& graphics_engine, bool force_draw); |
855 | 86 | virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw); | 90 | virtual void PostDraw(GraphicsEngine& graphics_engine, bool force_draw); |
856 | 87 | 91 | ||
857 | 88 | void PreLayoutManagement(); | 92 | void PreLayoutManagement(); |
858 | 89 | long PostLayoutManagement(long layoutResult); | 93 | long PostLayoutManagement(long layoutResult); |
859 | @@ -125,24 +129,25 @@ | |||
860 | 125 | /*! | 129 | /*! |
861 | 126 | This signal is emitted when the text has changed. | 130 | This signal is emitted when the text has changed. |
862 | 127 | */ | 131 | */ |
864 | 128 | sigc::signal <void, TextEntry*> sigTextChanged; | 132 | sigc::signal <void, TextEntry*> text_changed; |
865 | 129 | sigc::signal <void> activated; | 133 | sigc::signal <void> activated; |
866 | 130 | sigc::signal <void, int> cursor_moved; | 134 | sigc::signal <void, int> cursor_moved; |
867 | 131 | 135 | ||
869 | 132 | void SetText(const char *text); | 136 | void SetText(const char* text); |
870 | 133 | std::string const& GetText() const; | 137 | std::string const& GetText() const; |
871 | 138 | std::string GetTextSelection() const; | ||
872 | 134 | 139 | ||
874 | 135 | void SetCompletion(const char *text); // Should use std::string, does not for consistancy | 140 | void SetCompletion(const char* text); // Should use std::string, does not for consistancy |
875 | 136 | std::string const& GetCompletion() const; | 141 | std::string const& GetCompletion() const; |
876 | 137 | 142 | ||
878 | 138 | void SetCompletionColor(const Color &color); | 143 | void SetCompletionColor(const Color& color); |
879 | 139 | Color const& GetCompletionColor() const; | 144 | Color const& GetCompletionColor() const; |
880 | 140 | 145 | ||
882 | 141 | void SetTextColor(const Color &color); | 146 | void SetTextColor(const Color& color); |
883 | 142 | Color const& GetTextColor() const; | 147 | Color const& GetTextColor() const; |
885 | 143 | void SetFontFamily(const char *font); | 148 | void SetFontFamily(const char* font); |
886 | 144 | void SetFontSize(double font_size); | 149 | void SetFontSize(double font_size); |
888 | 145 | void SetFontOptions(const cairo_font_options_t *options); | 150 | void SetFontOptions(const cairo_font_options_t* options); |
889 | 146 | 151 | ||
890 | 147 | /** Select text between start and end. */ | 152 | /** Select text between start and end. */ |
891 | 148 | void Select(int start, int end); | 153 | void Select(int start, int end); |
892 | @@ -152,6 +157,11 @@ | |||
893 | 152 | CairoGraphics::Alignment GetAlign() const; | 157 | CairoGraphics::Alignment GetAlign() const; |
894 | 153 | void SetAlign(CairoGraphics::Alignment align); | 158 | void SetAlign(CairoGraphics::Alignment align); |
895 | 154 | 159 | ||
896 | 160 | bool im_active(); | ||
897 | 161 | |||
898 | 162 | void MoveCursorToLineStart(); | ||
899 | 163 | void MoveCursorToLineEnd(); | ||
900 | 164 | |||
901 | 155 | protected: | 165 | protected: |
902 | 156 | bool _block_focus; // used to selectively ignore focus keyevents | 166 | bool _block_focus; // used to selectively ignore focus keyevents |
903 | 157 | 167 | ||
904 | @@ -195,17 +205,17 @@ | |||
905 | 195 | void ResetPreedit(); | 205 | void ResetPreedit(); |
906 | 196 | /** Send out a request to blink the cursor if necessary */ | 206 | /** Send out a request to blink the cursor if necessary */ |
907 | 197 | void QueueCursorBlink(); | 207 | void QueueCursorBlink(); |
909 | 198 | static bool CursorBlinkCallback(TextEntry *data); | 208 | static bool CursorBlinkCallback(TextEntry* data); |
910 | 199 | 209 | ||
911 | 200 | void ShowCursor(); | 210 | void ShowCursor(); |
912 | 201 | void HideCursor(); | 211 | void HideCursor(); |
913 | 202 | 212 | ||
914 | 203 | /** Draw the Cursor to the canvas */ | 213 | /** Draw the Cursor to the canvas */ |
916 | 204 | void DrawCursor(CairoGraphics *canvas); | 214 | void DrawCursor(CairoGraphics* canvas); |
917 | 205 | /** Draw the text to the canvas */ | 215 | /** Draw the text to the canvas */ |
919 | 206 | void DrawText(CairoGraphics *canvas); | 216 | void DrawText(CairoGraphics* canvas); |
920 | 207 | 217 | ||
922 | 208 | void GetCursorRects(Rect *strong, Rect *weak); | 218 | void GetCursorRects(Rect* strong, Rect* weak); |
923 | 209 | 219 | ||
924 | 210 | void UpdateCursorRegion(); | 220 | void UpdateCursorRegion(); |
925 | 211 | 221 | ||
926 | @@ -230,7 +240,7 @@ | |||
927 | 230 | * coordinate in the layout */ | 240 | * coordinate in the layout */ |
928 | 231 | int XYToTextIndex(int x, int y); | 241 | int XYToTextIndex(int x, int y); |
929 | 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.*/ |
931 | 233 | bool GetSelectionBounds(int *start, int *end); | 243 | bool GetSelectionBounds(int* start, int* end) const; |
932 | 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. */ |
933 | 235 | void SetSelectionBounds(int selection_bound, int cursor); | 245 | void SetSelectionBounds(int selection_bound, int cursor); |
934 | 236 | 246 | ||
935 | @@ -247,7 +257,7 @@ | |||
936 | 247 | int GetPrevCharLength(int index); | 257 | int GetPrevCharLength(int index); |
937 | 248 | 258 | ||
938 | 249 | /** Insert text at current caret position */ | 259 | /** Insert text at current caret position */ |
940 | 250 | void EnterText(const char *str); | 260 | void EnterText(const char* str); |
941 | 251 | /** Delete text in a specified range, in number of characters. */ | 261 | /** Delete text in a specified range, in number of characters. */ |
942 | 252 | void DeleteText(int start, int end); | 262 | void DeleteText(int start, int end); |
943 | 253 | 263 | ||
944 | @@ -279,8 +289,8 @@ | |||
945 | 279 | /** | 289 | /** |
946 | 280 | * Gets the cursor location in pango layout. The unit is pixel. | 290 | * Gets the cursor location in pango layout. The unit is pixel. |
947 | 281 | */ | 291 | */ |
950 | 282 | void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height, | 292 | void GetCursorLocationInLayout(int* strong_x, int* strong_y, int* strong_height, |
951 | 283 | int *weak_x, int *weak_y, int *weak_height); | 293 | int* weak_x, int* weak_y, int* weak_height); |
952 | 284 | 294 | ||
953 | 285 | /** The CairoCanvas which hold cairo_t inside */ | 295 | /** The CairoCanvas which hold cairo_t inside */ |
954 | 286 | CairoGraphics* canvas_; | 296 | CairoGraphics* canvas_; |
955 | @@ -293,7 +303,7 @@ | |||
956 | 293 | /** The preedit text of the edit control */ | 303 | /** The preedit text of the edit control */ |
957 | 294 | std::string preedit_; | 304 | std::string preedit_; |
958 | 295 | /** Attribute list of the preedit text */ | 305 | /** Attribute list of the preedit text */ |
960 | 296 | PangoAttrList *preedit_attrs_; | 306 | PangoAttrList* preedit_attrs_; |
961 | 297 | /** | 307 | /** |
962 | 298 | * The character that should be displayed in invisible mode. | 308 | * The character that should be displayed in invisible mode. |
963 | 299 | * If this is empty, then the edit control is visible | 309 | * If this is empty, then the edit control is visible |
964 | @@ -387,7 +397,7 @@ | |||
965 | 387 | /** The font size of the text */ | 397 | /** The font size of the text */ |
966 | 388 | double font_size_; | 398 | double font_size_; |
967 | 389 | 399 | ||
969 | 390 | cairo_font_options_t *font_options_; | 400 | cairo_font_options_t* font_options_; |
970 | 391 | double font_dpi_; | 401 | double font_dpi_; |
971 | 392 | 402 | ||
972 | 393 | /** The text color of the edit control */ | 403 | /** The text color of the edit control */ |
973 | @@ -404,6 +414,12 @@ | |||
974 | 404 | std::list<Rect> last_cursor_region_; | 414 | std::list<Rect> last_cursor_region_; |
975 | 405 | std::list<Rect> cursor_region_; | 415 | std::list<Rect> cursor_region_; |
976 | 406 | 416 | ||
977 | 417 | #if defined(NUX_OS_LINUX) | ||
978 | 418 | IBusIMEContext* ime_; | ||
979 | 419 | friend class IBusIMEContext; | ||
980 | 420 | #endif | ||
981 | 421 | bool ime_active_; | ||
982 | 422 | |||
983 | 407 | protected: | 423 | protected: |
984 | 408 | bool text_input_mode_; | 424 | bool text_input_mode_; |
985 | 409 | bool key_nav_mode_; | 425 | bool key_nav_mode_; |
986 | 410 | 426 | ||
987 | === removed file 'Nux/TextEntryIM.cpp' | |||
988 | --- Nux/TextEntryIM.cpp 2012-02-12 01:55:02 +0000 | |||
989 | +++ Nux/TextEntryIM.cpp 1970-01-01 00:00:00 +0000 | |||
990 | @@ -1,2126 +0,0 @@ | |||
991 | 1 | /* | ||
992 | 2 | Copyright 2008 Google Inc. | ||
993 | 3 | |||
994 | 4 | Licensed under the Apache License, Version 2.0(the "License"); | ||
995 | 5 | you may not use this file except in compliance with the License. | ||
996 | 6 | You may obtain a copy of the License at | ||
997 | 7 | |||
998 | 8 | http://www.apache.org/licenses/LICENSE-2.0 | ||
999 | 9 | |||
1000 | 10 | Unless required by applicable law or agreed to in writing, software | ||
1001 | 11 | distributed under the License is distributed on an "AS IS" BASIS, | ||
1002 | 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
1003 | 13 | See the License for the specific language governing permissions and | ||
1004 | 14 | limitations under the License. | ||
1005 | 15 | */ | ||
1006 | 16 | |||
1007 | 17 | |||
1008 | 18 | #include "Nux.h" | ||
1009 | 19 | #include "Layout.h" | ||
1010 | 20 | #include "HLayout.h" | ||
1011 | 21 | #include "VLayout.h" | ||
1012 | 22 | #include "Validator.h" | ||
1013 | 23 | |||
1014 | 24 | #include "cairo/cairo.h" | ||
1015 | 25 | #include "pango/pango.h" | ||
1016 | 26 | #include "pango/pangocairo.h" | ||
1017 | 27 | #include "NuxImage/CairoGraphics.h" | ||
1018 | 28 | |||
1019 | 29 | #include "TextEntryIM.h" | ||
1020 | 30 | #include "TextEntry.h" | ||
1021 | 31 | |||
1022 | 32 | #if defined(NUX_OS_LINUX) | ||
1023 | 33 | #include <X11/cursorfont.h> | ||
1024 | 34 | #endif | ||
1025 | 35 | |||
1026 | 36 | namespace nux | ||
1027 | 37 | { | ||
1028 | 38 | static const int kInnerBorderX = 2; | ||
1029 | 39 | static const int kInnerBorderY = 0; //1; | ||
1030 | 40 | static const int kCursorBlinkTimeout = 400; | ||
1031 | 41 | static const double kStrongCursorLineWidth = 1; | ||
1032 | 42 | static const double kStrongCursorBarWidth = 1; | ||
1033 | 43 | static const double kWeakCursorLineWidth = 3; | ||
1034 | 44 | static const double kWeakCursorBarWidth = 3; | ||
1035 | 45 | static const Color kStrongCursorColor(0.9f, 0.9f, 0.9f, 1.0f); | ||
1036 | 46 | static const Color kWeakCursorColor(1.0f, 1.0f, 1.0f, 0.5f); | ||
1037 | 47 | static const Color kDefaultTextColor(0, 0, 0, 1.0f); | ||
1038 | 48 | static const Color kDefaultBackgroundColor(1, 1, 1, 1.0f); | ||
1039 | 49 | static const Color kDefaultSelectionBackgroundColor(0.5, 0.5, 0.5, 1.0f); | ||
1040 | 50 | static const Color kDefaultSelectionTextColor(1, 1, 1, 1.0f); | ||
1041 | 51 | static const unsigned long long kTripleClickTimeout = 500; | ||
1042 | 52 | static const std::string kDefaultFontName = "Ubuntu"; | ||
1043 | 53 | |||
1044 | 54 | static unsigned long long GetCurrentTime() | ||
1045 | 55 | { | ||
1046 | 56 | GTimeVal tv; | ||
1047 | 57 | g_get_current_time(&tv); | ||
1048 | 58 | return static_cast<unsigned long long>(tv.tv_sec) * 1000 + tv.tv_usec / 1000; | ||
1049 | 59 | } | ||
1050 | 60 | |||
1051 | 61 | static std::string CleanupLineBreaks(const char *source) | ||
1052 | 62 | { | ||
1053 | 63 | nuxAssert(source); | ||
1054 | 64 | std::string result; | ||
1055 | 65 | while (*source) { | ||
1056 | 66 | if (*source == '\r') { | ||
1057 | 67 | result += ' '; | ||
1058 | 68 | if (source[1] == '\n') | ||
1059 | 69 | source++; | ||
1060 | 70 | } else if (*source == '\n') { | ||
1061 | 71 | result += ' '; | ||
1062 | 72 | } else { | ||
1063 | 73 | result += *source; | ||
1064 | 74 | } | ||
1065 | 75 | source++; | ||
1066 | 76 | } | ||
1067 | 77 | return result; | ||
1068 | 78 | } | ||
1069 | 79 | |||
1070 | 80 | NUX_IMPLEMENT_OBJECT_TYPE(TextEntryIM); | ||
1071 | 81 | |||
1072 | 82 | TextEntryIM::TextEntryIM(const char* text, NUX_FILE_LINE_DECL) | ||
1073 | 83 | : View(NUX_FILE_LINE_PARAM) | ||
1074 | 84 | , _size_match_text(true) | ||
1075 | 85 | , _texture2D(nullptr) | ||
1076 | 86 | , canvas_(nullptr) | ||
1077 | 87 | , cached_layout_(nullptr) | ||
1078 | 88 | , preedit_attrs_(nullptr) | ||
1079 | 89 | , completion_color_(color::Gray) | ||
1080 | 90 | , last_dblclick_time_(0) | ||
1081 | 91 | , cursor_(0) | ||
1082 | 92 | , preedit_cursor_(0) | ||
1083 | 93 | , selection_bound_(0) | ||
1084 | 94 | , scroll_offset_x_(0) | ||
1085 | 95 | , scroll_offset_y_(0) | ||
1086 | 96 | , cursor_blink_timer_(0) | ||
1087 | 97 | , cursor_blink_status_(0) | ||
1088 | 98 | , visible_(true) | ||
1089 | 99 | , focused_(false) | ||
1090 | 100 | , need_im_reset_(false) | ||
1091 | 101 | , overwrite_(false) | ||
1092 | 102 | , select_words_(false) | ||
1093 | 103 | , select_lines_(false) | ||
1094 | 104 | , button_(false) | ||
1095 | 105 | , bold_(false) | ||
1096 | 106 | , underline_(false) | ||
1097 | 107 | , strikeout_(false) | ||
1098 | 108 | , italic_(false) | ||
1099 | 109 | , multiline_(false) | ||
1100 | 110 | , wrap_(false) | ||
1101 | 111 | , cursor_visible_(false) | ||
1102 | 112 | , readonly_(false) | ||
1103 | 113 | , content_modified_(false) | ||
1104 | 114 | , selection_changed_(false) | ||
1105 | 115 | , cursor_moved_(false) | ||
1106 | 116 | , update_canvas_(true) | ||
1107 | 117 | , font_family_("Ubuntu") | ||
1108 | 118 | , font_size_(12) | ||
1109 | 119 | , font_options_(cairo_font_options_create()) | ||
1110 | 120 | , font_dpi_(96.0) | ||
1111 | 121 | , _text_color(color::White) | ||
1112 | 122 | , align_(CairoGraphics::ALIGN_LEFT) | ||
1113 | 123 | #if defined(NUX_OS_LINUX) | ||
1114 | 124 | , caret_cursor_(None) | ||
1115 | 125 | #endif | ||
1116 | 126 | , ime_(new IBusIMEContext(this)) | ||
1117 | 127 | , ime_active_(false) | ||
1118 | 128 | , text_input_mode_(false) | ||
1119 | 129 | , key_nav_mode_(false) | ||
1120 | 130 | { | ||
1121 | 131 | cairo_font_options_set_antialias(font_options_, CAIRO_ANTIALIAS_SUBPIXEL); | ||
1122 | 132 | cairo_font_options_set_hint_style(font_options_, CAIRO_HINT_STYLE_FULL); | ||
1123 | 133 | cairo_font_options_set_hint_metrics(font_options_, CAIRO_HINT_METRICS_ON); | ||
1124 | 134 | cairo_font_options_set_subpixel_order(font_options_, CAIRO_SUBPIXEL_ORDER_RGB); | ||
1125 | 135 | |||
1126 | 136 | mouse_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDown)); | ||
1127 | 137 | mouse_drag.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDrag)); | ||
1128 | 138 | mouse_up.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseUp)); | ||
1129 | 139 | mouse_double_click.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDoubleClick)); | ||
1130 | 140 | mouse_enter.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseEnter)); | ||
1131 | 141 | mouse_leave.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseLeave)); | ||
1132 | 142 | |||
1133 | 143 | key_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvKeyEvent)); | ||
1134 | 144 | |||
1135 | 145 | begin_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvStartKeyFocus)); | ||
1136 | 146 | end_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvEndKeyFocus)); | ||
1137 | 147 | |||
1138 | 148 | SetMinimumSize(DEFAULT_WIDGET_WIDTH, PRACTICAL_WIDGET_HEIGHT); | ||
1139 | 149 | SetText(text); | ||
1140 | 150 | |||
1141 | 151 | SetAcceptKeyboardEvent(true); | ||
1142 | 152 | EnableDoubleClick(true); | ||
1143 | 153 | } | ||
1144 | 154 | |||
1145 | 155 | TextEntryIM::~TextEntryIM() | ||
1146 | 156 | { | ||
1147 | 157 | if (cursor_blink_timer_) | ||
1148 | 158 | g_source_remove(cursor_blink_timer_); | ||
1149 | 159 | |||
1150 | 160 | cairo_font_options_destroy(font_options_); | ||
1151 | 161 | if (_texture2D) | ||
1152 | 162 | _texture2D->UnReference(); | ||
1153 | 163 | } | ||
1154 | 164 | |||
1155 | 165 | void TextEntryIM::PreLayoutManagement() | ||
1156 | 166 | { | ||
1157 | 167 | View::PreLayoutManagement(); | ||
1158 | 168 | } | ||
1159 | 169 | |||
1160 | 170 | long TextEntryIM::PostLayoutManagement(long layoutResult) | ||
1161 | 171 | { | ||
1162 | 172 | long result = View::PostLayoutManagement(layoutResult); | ||
1163 | 173 | MainDraw(); | ||
1164 | 174 | return result; | ||
1165 | 175 | } | ||
1166 | 176 | |||
1167 | 177 | void TextEntryIM::GeometryChanged() | ||
1168 | 178 | { | ||
1169 | 179 | |||
1170 | 180 | update_canvas_ = true; | ||
1171 | 181 | View::GeometryChanged(); | ||
1172 | 182 | |||
1173 | 183 | } | ||
1174 | 184 | |||
1175 | 185 | Area* TextEntryIM::FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type) | ||
1176 | 186 | { | ||
1177 | 187 | Area* area = View::FindAreaUnderMouse(mouse_position, event_type); | ||
1178 | 188 | |||
1179 | 189 | return area; | ||
1180 | 190 | } | ||
1181 | 191 | |||
1182 | 192 | void TextEntryIM::ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) | ||
1183 | 193 | { | ||
1184 | 194 | if (GetEventButton(button_flags) != 1 && event_type != NUX_MOUSE_MOVE) | ||
1185 | 195 | return; | ||
1186 | 196 | |||
1187 | 197 | //ResetImContext(); | ||
1188 | 198 | //Event::Type type = event.GetType(); | ||
1189 | 199 | |||
1190 | 200 | int X = static_cast<int>(x /*round(event.GetX())*/) - kInnerBorderX - scroll_offset_x_; | ||
1191 | 201 | int Y = static_cast<int>(y /*round(event.GetY())*/) - kInnerBorderY - scroll_offset_y_; | ||
1192 | 202 | int index = XYToTextIndex(X, Y); | ||
1193 | 203 | int sel_start, sel_end; | ||
1194 | 204 | GetSelectionBounds(&sel_start, &sel_end); | ||
1195 | 205 | |||
1196 | 206 | unsigned long long current_time = GetCurrentTime(); | ||
1197 | 207 | |||
1198 | 208 | if ((event_type == NUX_MOUSE_PRESSED) && (current_time - last_dblclick_time_ <= kTripleClickTimeout)) | ||
1199 | 209 | { | ||
1200 | 210 | SelectLine(); | ||
1201 | 211 | } | ||
1202 | 212 | else if (event_type == NUX_MOUSE_DOUBLECLICK) | ||
1203 | 213 | { | ||
1204 | 214 | SelectWord(); | ||
1205 | 215 | last_dblclick_time_ = current_time; | ||
1206 | 216 | } | ||
1207 | 217 | else if (event_type == NUX_MOUSE_PRESSED) | ||
1208 | 218 | { | ||
1209 | 219 | if (key_flags & NUX_STATE_SHIFT) | ||
1210 | 220 | { | ||
1211 | 221 | // If current click position is inside the selection range, then just | ||
1212 | 222 | // cancel the selection. | ||
1213 | 223 | if (index > sel_start && index < sel_end) | ||
1214 | 224 | SetCursor(index); | ||
1215 | 225 | else if (index <= sel_start) | ||
1216 | 226 | SetSelectionBounds(sel_end, index); | ||
1217 | 227 | else if (index >= sel_end) | ||
1218 | 228 | SetSelectionBounds(sel_start, index); | ||
1219 | 229 | } | ||
1220 | 230 | else | ||
1221 | 231 | { | ||
1222 | 232 | SetCursor(index); | ||
1223 | 233 | } | ||
1224 | 234 | } | ||
1225 | 235 | else if (event_type == NUX_MOUSE_MOVE) | ||
1226 | 236 | { | ||
1227 | 237 | SetSelectionBounds(selection_bound_, index); | ||
1228 | 238 | } | ||
1229 | 239 | |||
1230 | 240 | QueueRefresh(false, true); | ||
1231 | 241 | //return EVENT_RESULT_HANDLED; | ||
1232 | 242 | } | ||
1233 | 243 | |||
1234 | 244 | void TextEntryIM::ProcessKeyEvent( | ||
1235 | 245 | unsigned long event_type , /*event type*/ | ||
1236 | 246 | unsigned long keysym , /*event keysym*/ | ||
1237 | 247 | unsigned long state , /*event state*/ | ||
1238 | 248 | const char* character , /*character*/ | ||
1239 | 249 | unsigned short keyCount /*key repeat count*/) | ||
1240 | 250 | { | ||
1241 | 251 | |||
1242 | 252 | KeyEvent event((NuxEventType)event_type, keysym, 0, state); | ||
1243 | 253 | ime_->FilterKeyEvent(event); | ||
1244 | 254 | |||
1245 | 255 | if (event_type == NUX_KEYDOWN) | ||
1246 | 256 | text_input_mode_ = true; | ||
1247 | 257 | |||
1248 | 258 | // GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent()); | ||
1249 | 259 | // ASSERT(gdk_event); | ||
1250 | 260 | // | ||
1251 | 261 | // Event::Type type = event.GetType(); | ||
1252 | 262 | // Cause the cursor to stop blinking for a while. | ||
1253 | 263 | cursor_blink_status_ = 4; | ||
1254 | 264 | |||
1255 | 265 | // if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/) | ||
1256 | 266 | // { | ||
1257 | 267 | // need_im_reset_ = true; | ||
1258 | 268 | // QueueRefresh(false, true); | ||
1259 | 269 | // return EVENT_RESULT_HANDLED; | ||
1260 | 270 | // } | ||
1261 | 271 | |||
1262 | 272 | if (event_type == NUX_KEYUP) | ||
1263 | 273 | return; | ||
1264 | 274 | |||
1265 | 275 | // we need to ignore some characters | ||
1266 | 276 | if (keysym == NUX_VK_TAB) | ||
1267 | 277 | return; | ||
1268 | 278 | |||
1269 | 279 | if (keysym == NUX_VK_ENTER || keysym == NUX_KP_ENTER) | ||
1270 | 280 | { | ||
1271 | 281 | activated.emit(); | ||
1272 | 282 | return; | ||
1273 | 283 | } | ||
1274 | 284 | |||
1275 | 285 | unsigned int keyval = keysym; | ||
1276 | 286 | bool shift = (state & NUX_STATE_SHIFT); | ||
1277 | 287 | bool ctrl = (state & NUX_STATE_CTRL); | ||
1278 | 288 | |||
1279 | 289 | // DLOG("TextEntryIM::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl); | ||
1280 | 290 | |||
1281 | 291 | if (event_type == NUX_KEYDOWN) | ||
1282 | 292 | { | ||
1283 | 293 | if (keyval == NUX_VK_LEFT) | ||
1284 | 294 | { | ||
1285 | 295 | if (!ctrl) | ||
1286 | 296 | MoveCursor(VISUALLY, -1, shift); | ||
1287 | 297 | else | ||
1288 | 298 | MoveCursor(WORDS, -1, shift); | ||
1289 | 299 | } | ||
1290 | 300 | else if (keyval == NUX_VK_RIGHT) | ||
1291 | 301 | { | ||
1292 | 302 | if (!ctrl) | ||
1293 | 303 | MoveCursor(VISUALLY, 1, shift); | ||
1294 | 304 | else | ||
1295 | 305 | MoveCursor(WORDS, 1, shift); | ||
1296 | 306 | } | ||
1297 | 307 | else if (keyval == NUX_VK_UP) | ||
1298 | 308 | { | ||
1299 | 309 | MoveCursor(DISPLAY_LINES, -1, shift); | ||
1300 | 310 | } | ||
1301 | 311 | else if (keyval == NUX_VK_DOWN) | ||
1302 | 312 | { | ||
1303 | 313 | MoveCursor(DISPLAY_LINES, 1, shift); | ||
1304 | 314 | } | ||
1305 | 315 | else if (keyval == NUX_VK_HOME) | ||
1306 | 316 | { | ||
1307 | 317 | if (!ctrl) | ||
1308 | 318 | MoveCursor(DISPLAY_LINE_ENDS, -1, shift); | ||
1309 | 319 | else | ||
1310 | 320 | MoveCursor(BUFFER, -1, shift); | ||
1311 | 321 | } | ||
1312 | 322 | else if (keyval == NUX_VK_END) | ||
1313 | 323 | { | ||
1314 | 324 | if (!ctrl) | ||
1315 | 325 | MoveCursor(DISPLAY_LINE_ENDS, 1, shift); | ||
1316 | 326 | else | ||
1317 | 327 | MoveCursor(BUFFER, 1, shift); | ||
1318 | 328 | } | ||
1319 | 329 | else if (keyval == NUX_VK_PAGE_UP) | ||
1320 | 330 | { | ||
1321 | 331 | if (!ctrl) | ||
1322 | 332 | MoveCursor(PAGES, -1, shift); | ||
1323 | 333 | else | ||
1324 | 334 | MoveCursor(BUFFER, -1, shift); | ||
1325 | 335 | } | ||
1326 | 336 | else if (keyval == NUX_VK_PAGE_DOWN) | ||
1327 | 337 | { | ||
1328 | 338 | if (!ctrl) | ||
1329 | 339 | MoveCursor(PAGES, 1, shift); | ||
1330 | 340 | else | ||
1331 | 341 | MoveCursor(BUFFER, 1, shift); | ||
1332 | 342 | } | ||
1333 | 343 | else if (((keyval == NUX_VK_x) && ctrl && !shift) || ((keyval == NUX_VK_DELETE) && shift && !ctrl)) | ||
1334 | 344 | { | ||
1335 | 345 | CutClipboard(); | ||
1336 | 346 | } | ||
1337 | 347 | else if (((keyval == NUX_VK_c) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && ctrl && (!shift))) | ||
1338 | 348 | { | ||
1339 | 349 | CopyClipboard(); | ||
1340 | 350 | } | ||
1341 | 351 | else if (((keyval == NUX_VK_v) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && shift && (!ctrl))) | ||
1342 | 352 | { | ||
1343 | 353 | PasteClipboard(); | ||
1344 | 354 | } | ||
1345 | 355 | else if ((keyval == NUX_VK_a) && ctrl) | ||
1346 | 356 | { | ||
1347 | 357 | // Select all | ||
1348 | 358 | int text_length = static_cast<int>(text_.length()); | ||
1349 | 359 | SetSelectionBounds(0, text_length); | ||
1350 | 360 | QueueRefresh(false, true); | ||
1351 | 361 | return; | ||
1352 | 362 | } | ||
1353 | 363 | else if (keyval == NUX_VK_BACKSPACE) | ||
1354 | 364 | { | ||
1355 | 365 | if (!ctrl) | ||
1356 | 366 | BackSpace(VISUALLY); | ||
1357 | 367 | else | ||
1358 | 368 | BackSpace(WORDS); | ||
1359 | 369 | } | ||
1360 | 370 | else if ((keyval == NUX_VK_DELETE) && (!shift)) | ||
1361 | 371 | { | ||
1362 | 372 | if (!ctrl) | ||
1363 | 373 | Delete(VISUALLY); | ||
1364 | 374 | else | ||
1365 | 375 | Delete(WORDS); | ||
1366 | 376 | } | ||
1367 | 377 | else if ((keyval == NUX_VK_INSERT) && (!shift) && (!ctrl)) | ||
1368 | 378 | { | ||
1369 | 379 | ToggleOverwrite(); | ||
1370 | 380 | } | ||
1371 | 381 | // else | ||
1372 | 382 | // { | ||
1373 | 383 | // return EVENT_RESULT_UNHANDLED; | ||
1374 | 384 | // } | ||
1375 | 385 | } | ||
1376 | 386 | else | ||
1377 | 387 | { // EVENT_KEY_PRESS | ||
1378 | 388 | // if (keyval == GDK_Return || keyval == GDK_KP_Enter) | ||
1379 | 389 | // { | ||
1380 | 390 | // // If multiline_ is unset, just ignore new_line. | ||
1381 | 391 | // if (multiline_) | ||
1382 | 392 | // EnterText("\n"); | ||
1383 | 393 | // else | ||
1384 | 394 | // return; | ||
1385 | 395 | // } | ||
1386 | 396 | // else | ||
1387 | 397 | // { | ||
1388 | 398 | // return; | ||
1389 | 399 | // } | ||
1390 | 400 | } | ||
1391 | 401 | |||
1392 | 402 | if (character != 0 && (strlen(character) != 0) && !ime_active_) | ||
1393 | 403 | { | ||
1394 | 404 | EnterText(character); | ||
1395 | 405 | } | ||
1396 | 406 | |||
1397 | 407 | QueueRefresh(false, true); | ||
1398 | 408 | return; | ||
1399 | 409 | } | ||
1400 | 410 | |||
1401 | 411 | void TextEntryIM::RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags) | ||
1402 | 412 | { | ||
1403 | 413 | ProcessMouseEvent(NUX_MOUSE_DOUBLECLICK, x, y, 0, 0, button_flags, key_flags); | ||
1404 | 414 | } | ||
1405 | 415 | |||
1406 | 416 | void TextEntryIM::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) | ||
1407 | 417 | { | ||
1408 | 418 | ProcessMouseEvent(NUX_MOUSE_RELEASED, x, y, 0, 0, button_flags, key_flags); | ||
1409 | 419 | } | ||
1410 | 420 | |||
1411 | 421 | void TextEntryIM::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) | ||
1412 | 422 | { | ||
1413 | 423 | ProcessMouseEvent(NUX_MOUSE_PRESSED, x, y, 0, 0, button_flags, key_flags); | ||
1414 | 424 | } | ||
1415 | 425 | |||
1416 | 426 | void TextEntryIM::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) | ||
1417 | 427 | { | ||
1418 | 428 | ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags); | ||
1419 | 429 | } | ||
1420 | 430 | |||
1421 | 431 | void TextEntryIM::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) | ||
1422 | 432 | { | ||
1423 | 433 | #if defined(NUX_OS_LINUX) | ||
1424 | 434 | if (caret_cursor_ == None) | ||
1425 | 435 | { | ||
1426 | 436 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); | ||
1427 | 437 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); | ||
1428 | 438 | |||
1429 | 439 | if (display && window) | ||
1430 | 440 | { | ||
1431 | 441 | caret_cursor_ = XCreateFontCursor(display, XC_xterm); | ||
1432 | 442 | XDefineCursor(display, window->GetInputWindowId(), caret_cursor_); | ||
1433 | 443 | } | ||
1434 | 444 | } | ||
1435 | 445 | #endif | ||
1436 | 446 | } | ||
1437 | 447 | |||
1438 | 448 | void TextEntryIM::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) | ||
1439 | 449 | { | ||
1440 | 450 | #if defined(NUX_OS_LINUX) | ||
1441 | 451 | if (caret_cursor_ != None) | ||
1442 | 452 | { | ||
1443 | 453 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); | ||
1444 | 454 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); | ||
1445 | 455 | |||
1446 | 456 | if (display && window) | ||
1447 | 457 | { | ||
1448 | 458 | XUndefineCursor(display, window->GetInputWindowId()); | ||
1449 | 459 | XFreeCursor(display, caret_cursor_); | ||
1450 | 460 | caret_cursor_ = None; | ||
1451 | 461 | } | ||
1452 | 462 | } | ||
1453 | 463 | #endif | ||
1454 | 464 | } | ||
1455 | 465 | |||
1456 | 466 | void TextEntryIM::RecvKeyEvent( | ||
1457 | 467 | unsigned long eventType , /*event type*/ | ||
1458 | 468 | unsigned long keysym , /*event keysym*/ | ||
1459 | 469 | unsigned long state , /*event state*/ | ||
1460 | 470 | const char* character , /*character*/ | ||
1461 | 471 | unsigned short keyCount /*key repeat count*/) | ||
1462 | 472 | { | ||
1463 | 473 | ProcessKeyEvent(eventType, keysym, state, character, keyCount); | ||
1464 | 474 | } | ||
1465 | 475 | |||
1466 | 476 | void TextEntryIM::RecvStartKeyFocus() | ||
1467 | 477 | { | ||
1468 | 478 | key_nav_mode_ = true; | ||
1469 | 479 | text_input_mode_ = false; | ||
1470 | 480 | |||
1471 | 481 | FocusInx(); | ||
1472 | 482 | } | ||
1473 | 483 | |||
1474 | 484 | void TextEntryIM::RecvEndKeyFocus() | ||
1475 | 485 | { | ||
1476 | 486 | key_nav_mode_ = false; | ||
1477 | 487 | text_input_mode_ = false; | ||
1478 | 488 | |||
1479 | 489 | FocusOutx(); | ||
1480 | 490 | } | ||
1481 | 491 | |||
1482 | 492 | void TextEntryIM::Draw(GraphicsEngine& gfxContext, bool forceDraw) | ||
1483 | 493 | { | ||
1484 | 494 | MainDraw(); | ||
1485 | 495 | Geometry base = GetGeometry(); | ||
1486 | 496 | |||
1487 | 497 | gfxContext.PushClippingRectangle(base); | ||
1488 | 498 | |||
1489 | 499 | nux::GetPainter().PaintBackground(gfxContext, base); | ||
1490 | 500 | |||
1491 | 501 | Color col = color::Black; | ||
1492 | 502 | col.alpha = 0; | ||
1493 | 503 | gfxContext.QRP_Color(base.x, | ||
1494 | 504 | base.y, | ||
1495 | 505 | base.width, | ||
1496 | 506 | base.height, | ||
1497 | 507 | col); | ||
1498 | 508 | |||
1499 | 509 | TexCoordXForm texxform; | ||
1500 | 510 | texxform.SetWrap(TEXWRAP_REPEAT, TEXWRAP_REPEAT); | ||
1501 | 511 | texxform.SetTexCoordType(TexCoordXForm::OFFSET_COORD); | ||
1502 | 512 | gfxContext.QRP_1Tex(base.x, | ||
1503 | 513 | base.y, | ||
1504 | 514 | base.width, | ||
1505 | 515 | base.height, | ||
1506 | 516 | _texture2D->GetDeviceTexture(), | ||
1507 | 517 | texxform, | ||
1508 | 518 | _text_color); | ||
1509 | 519 | |||
1510 | 520 | gfxContext.PopClippingRectangle(); | ||
1511 | 521 | } | ||
1512 | 522 | |||
1513 | 523 | void TextEntryIM::DrawContent(GraphicsEngine& gfxContext, bool forceDraw) | ||
1514 | 524 | { | ||
1515 | 525 | //MainDraw(); | ||
1516 | 526 | } | ||
1517 | 527 | |||
1518 | 528 | void TextEntryIM::PostDraw(GraphicsEngine& gfxContext, bool forceDraw) | ||
1519 | 529 | { | ||
1520 | 530 | // intentionally left empty | ||
1521 | 531 | } | ||
1522 | 532 | |||
1523 | 533 | void TextEntryIM::SetText(const char *text) | ||
1524 | 534 | { | ||
1525 | 535 | const char *end = NULL; | ||
1526 | 536 | g_utf8_validate(text, -1, &end); | ||
1527 | 537 | |||
1528 | 538 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); | ||
1529 | 539 | if (txt == text_) | ||
1530 | 540 | return; // prevent some redraws | ||
1531 | 541 | |||
1532 | 542 | text_ = multiline_ ? txt : CleanupLineBreaks(txt.c_str()); | ||
1533 | 543 | cursor_ = 0; | ||
1534 | 544 | selection_bound_ = 0; | ||
1535 | 545 | need_im_reset_ = true; | ||
1536 | 546 | //ResetImContext(); | ||
1537 | 547 | QueueRefresh(true, true); | ||
1538 | 548 | sigTextChanged.emit(this); | ||
1539 | 549 | } | ||
1540 | 550 | |||
1541 | 551 | std::string const& TextEntryIM::GetText() const | ||
1542 | 552 | { | ||
1543 | 553 | return text_; | ||
1544 | 554 | } | ||
1545 | 555 | |||
1546 | 556 | void TextEntryIM::SetCompletion(const char *text) | ||
1547 | 557 | { | ||
1548 | 558 | const char *end = NULL; | ||
1549 | 559 | g_utf8_validate(text, -1, &end); | ||
1550 | 560 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); | ||
1551 | 561 | if (txt == completion_) | ||
1552 | 562 | return; | ||
1553 | 563 | |||
1554 | 564 | completion_ = txt; | ||
1555 | 565 | QueueRefresh(true, true); | ||
1556 | 566 | } | ||
1557 | 567 | |||
1558 | 568 | std::string const& TextEntryIM::GetCompletion() const | ||
1559 | 569 | { | ||
1560 | 570 | return completion_; | ||
1561 | 571 | } | ||
1562 | 572 | |||
1563 | 573 | void TextEntryIM::SetCompletionColor(const Color &color) | ||
1564 | 574 | { | ||
1565 | 575 | completion_color_ = color; | ||
1566 | 576 | QueueRefresh(true, true); | ||
1567 | 577 | } | ||
1568 | 578 | |||
1569 | 579 | Color const& TextEntryIM::GetCompletionColor() const | ||
1570 | 580 | { | ||
1571 | 581 | return completion_color_; | ||
1572 | 582 | } | ||
1573 | 583 | |||
1574 | 584 | void TextEntryIM::SetTextColor(const Color &text_color) | ||
1575 | 585 | { | ||
1576 | 586 | if (_text_color != text_color) | ||
1577 | 587 | { | ||
1578 | 588 | _text_color = text_color; | ||
1579 | 589 | QueueRefresh(true, true); | ||
1580 | 590 | } | ||
1581 | 591 | } | ||
1582 | 592 | |||
1583 | 593 | Color const& TextEntryIM::GetTextColor() const | ||
1584 | 594 | { | ||
1585 | 595 | return _text_color; | ||
1586 | 596 | } | ||
1587 | 597 | |||
1588 | 598 | |||
1589 | 599 | void TextEntryIM::MainDraw() | ||
1590 | 600 | { | ||
1591 | 601 | |||
1592 | 602 | CairoGraphics *edit_canvas = EnsureCanvas(); | ||
1593 | 603 | |||
1594 | 604 | if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) | ||
1595 | 605 | { | ||
1596 | 606 | edit_canvas->PushState(); | ||
1597 | 607 | DrawText(edit_canvas); | ||
1598 | 608 | edit_canvas->PopState(); | ||
1599 | 609 | } | ||
1600 | 610 | |||
1601 | 611 | // if (background_) | ||
1602 | 612 | // background_->Draw(canvas, 0, 0, GetBaseWidth, GetBaseHeight); | ||
1603 | 613 | |||
1604 | 614 | CairoGraphics* final_canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, edit_canvas->GetWidth(), edit_canvas->GetHeight()); | ||
1605 | 615 | |||
1606 | 616 | final_canvas->PushState(); | ||
1607 | 617 | final_canvas->IntersectRectClipRegion(kInnerBorderX, | ||
1608 | 618 | kInnerBorderY, | ||
1609 | 619 | GetBaseWidth() - kInnerBorderX, | ||
1610 | 620 | GetBaseHeight() - kInnerBorderY); | ||
1611 | 621 | final_canvas->DrawCanvas(0, 0, edit_canvas); | ||
1612 | 622 | final_canvas->PopState(); | ||
1613 | 623 | DrawCursor(final_canvas); | ||
1614 | 624 | |||
1615 | 625 | update_canvas_ = false; | ||
1616 | 626 | last_selection_region_ = selection_region_; | ||
1617 | 627 | last_cursor_region_ = cursor_region_; | ||
1618 | 628 | |||
1619 | 629 | NBitmapData* bitmap = final_canvas->GetBitmap(); | ||
1620 | 630 | delete final_canvas; | ||
1621 | 631 | |||
1622 | 632 | if (!_texture2D || _texture2D->GetWidth() != bitmap->GetWidth() || _texture2D->GetHeight() != bitmap->GetHeight()) | ||
1623 | 633 | { | ||
1624 | 634 | if (_texture2D) | ||
1625 | 635 | _texture2D->UnReference(); | ||
1626 | 636 | _texture2D = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); | ||
1627 | 637 | } | ||
1628 | 638 | |||
1629 | 639 | _texture2D->Update(bitmap); | ||
1630 | 640 | delete bitmap; | ||
1631 | 641 | } | ||
1632 | 642 | |||
1633 | 643 | void TextEntryIM::FocusInx() | ||
1634 | 644 | { | ||
1635 | 645 | if (!focused_) | ||
1636 | 646 | { | ||
1637 | 647 | focused_ = true; | ||
1638 | 648 | if (!readonly_ /*&& im_context_*/) | ||
1639 | 649 | { | ||
1640 | 650 | need_im_reset_ = true; | ||
1641 | 651 | ime_->Focus(); | ||
1642 | 652 | //gtk_im_context_focus_in(im_context_); | ||
1643 | 653 | //UpdateIMCursorLocation(); | ||
1644 | 654 | } | ||
1645 | 655 | cursor_visible_ = true; // show cursor when getting focus | ||
1646 | 656 | selection_changed_ = true; | ||
1647 | 657 | cursor_moved_ = true; | ||
1648 | 658 | // Don't adjust scroll. | ||
1649 | 659 | QueueRefresh(true, false); | ||
1650 | 660 | } | ||
1651 | 661 | } | ||
1652 | 662 | |||
1653 | 663 | void TextEntryIM::FocusOutx() | ||
1654 | 664 | { | ||
1655 | 665 | if (focused_) | ||
1656 | 666 | { | ||
1657 | 667 | focused_ = false; | ||
1658 | 668 | if (!readonly_ /*&& im_context_*/) | ||
1659 | 669 | { | ||
1660 | 670 | need_im_reset_ = true; | ||
1661 | 671 | ime_->Blur(); | ||
1662 | 672 | //gtk_im_context_focus_out(im_context_); | ||
1663 | 673 | } | ||
1664 | 674 | cursor_visible_ = false; // hide cursor when losing focus | ||
1665 | 675 | selection_changed_ = true; | ||
1666 | 676 | cursor_moved_ = true; | ||
1667 | 677 | // Don't adjust scroll. | ||
1668 | 678 | QueueRefresh(true, false); | ||
1669 | 679 | } | ||
1670 | 680 | } | ||
1671 | 681 | |||
1672 | 682 | CairoGraphics* TextEntryIM::EnsureCanvas() | ||
1673 | 683 | { | ||
1674 | 684 | if (canvas_) | ||
1675 | 685 | { | ||
1676 | 686 | if ((GetBaseWidth() == canvas_->GetWidth()) && (GetBaseHeight() == canvas_->GetHeight())) | ||
1677 | 687 | { | ||
1678 | 688 | return canvas_; | ||
1679 | 689 | } | ||
1680 | 690 | else | ||
1681 | 691 | { | ||
1682 | 692 | nuxDebugMsg("[TextEntryIM::EnsureCanvas] Recreate canvas"); | ||
1683 | 693 | delete canvas_; | ||
1684 | 694 | canvas_ = NULL; | ||
1685 | 695 | } | ||
1686 | 696 | } | ||
1687 | 697 | canvas_ = new CairoGraphics(CAIRO_FORMAT_ARGB32, GetBaseWidth(), GetBaseHeight()); | ||
1688 | 698 | nuxAssert(canvas_); | ||
1689 | 699 | return canvas_; | ||
1690 | 700 | } | ||
1691 | 701 | |||
1692 | 702 | void TextEntryIM::AdjustScroll() | ||
1693 | 703 | { | ||
1694 | 704 | int old_offset_x = scroll_offset_x_; | ||
1695 | 705 | int old_offset_y = scroll_offset_y_; | ||
1696 | 706 | int display_width = GetBaseWidth() - kInnerBorderX * 2; | ||
1697 | 707 | int display_height = GetBaseHeight() - kInnerBorderY * 2; | ||
1698 | 708 | |||
1699 | 709 | PangoLayout *layout = EnsureLayout(); | ||
1700 | 710 | int text_width, text_height; | ||
1701 | 711 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | ||
1702 | 712 | |||
1703 | 713 | int strong_x, strong_y, strong_height; | ||
1704 | 714 | int weak_x, weak_y, weak_height; | ||
1705 | 715 | GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, | ||
1706 | 716 | &weak_x, &weak_y, &weak_height); | ||
1707 | 717 | |||
1708 | 718 | if (!wrap_ && display_width > text_width) | ||
1709 | 719 | { | ||
1710 | 720 | PangoAlignment align = pango_layout_get_alignment(layout); | ||
1711 | 721 | if (align == PANGO_ALIGN_RIGHT) | ||
1712 | 722 | scroll_offset_x_ = display_width - text_width; | ||
1713 | 723 | else if (align == PANGO_ALIGN_CENTER) | ||
1714 | 724 | scroll_offset_x_ = (display_width - text_width) / 2; | ||
1715 | 725 | else | ||
1716 | 726 | scroll_offset_x_ = 0; | ||
1717 | 727 | } | ||
1718 | 728 | else | ||
1719 | 729 | { | ||
1720 | 730 | if (scroll_offset_x_ + strong_x < 0) | ||
1721 | 731 | scroll_offset_x_ = -strong_x; | ||
1722 | 732 | else if (scroll_offset_x_ + strong_x > display_width) | ||
1723 | 733 | scroll_offset_x_ = display_width - strong_x; | ||
1724 | 734 | |||
1725 | 735 | if (std::abs(weak_x - strong_x) < display_width) | ||
1726 | 736 | { | ||
1727 | 737 | if (scroll_offset_x_ + weak_x < 0) | ||
1728 | 738 | scroll_offset_x_ = - weak_x; | ||
1729 | 739 | else if (scroll_offset_x_ + weak_x > display_width) | ||
1730 | 740 | scroll_offset_x_ = display_width - weak_x; | ||
1731 | 741 | } | ||
1732 | 742 | } | ||
1733 | 743 | |||
1734 | 744 | if (display_height > text_height) | ||
1735 | 745 | { | ||
1736 | 746 | scroll_offset_y_ = 0; | ||
1737 | 747 | } | ||
1738 | 748 | else | ||
1739 | 749 | { | ||
1740 | 750 | if (scroll_offset_y_ + strong_y + strong_height > display_height) | ||
1741 | 751 | scroll_offset_y_ = display_height - strong_y - strong_height; | ||
1742 | 752 | if (scroll_offset_y_ + strong_y < 0) | ||
1743 | 753 | scroll_offset_y_ = -strong_y; | ||
1744 | 754 | } | ||
1745 | 755 | |||
1746 | 756 | if (old_offset_x != scroll_offset_x_ || old_offset_y != scroll_offset_y_) | ||
1747 | 757 | content_modified_ = true; | ||
1748 | 758 | } | ||
1749 | 759 | |||
1750 | 760 | void TextEntryIM::QueueRefresh(bool relayout, bool adjust_scroll) | ||
1751 | 761 | { | ||
1752 | 762 | if (relayout) | ||
1753 | 763 | ResetLayout(); | ||
1754 | 764 | |||
1755 | 765 | if (adjust_scroll) | ||
1756 | 766 | AdjustScroll(); | ||
1757 | 767 | |||
1758 | 768 | QueueTextDraw(); | ||
1759 | 769 | QueueCursorBlink(); | ||
1760 | 770 | } | ||
1761 | 771 | |||
1762 | 772 | void TextEntryIM::ResetImContext() | ||
1763 | 773 | { | ||
1764 | 774 | if (need_im_reset_) | ||
1765 | 775 | { | ||
1766 | 776 | need_im_reset_ = false; | ||
1767 | 777 | // if (im_context_) | ||
1768 | 778 | // gtk_im_context_reset(im_context_); | ||
1769 | 779 | ResetPreedit(); | ||
1770 | 780 | } | ||
1771 | 781 | } | ||
1772 | 782 | |||
1773 | 783 | void TextEntryIM::ResetPreedit() { | ||
1774 | 784 | // Reset layout if there were some content in preedit string | ||
1775 | 785 | if (preedit_.length()) | ||
1776 | 786 | ResetLayout(); | ||
1777 | 787 | |||
1778 | 788 | preedit_.clear(); | ||
1779 | 789 | preedit_cursor_ = 0; | ||
1780 | 790 | if (preedit_attrs_) { | ||
1781 | 791 | pango_attr_list_unref(preedit_attrs_); | ||
1782 | 792 | preedit_attrs_ = NULL; | ||
1783 | 793 | } | ||
1784 | 794 | } | ||
1785 | 795 | |||
1786 | 796 | void TextEntryIM::DrawText(CairoGraphics *canvas) | ||
1787 | 797 | { | ||
1788 | 798 | PangoLayout *layout = EnsureLayout(); | ||
1789 | 799 | |||
1790 | 800 | bool redraw_text = false; | ||
1791 | 801 | if (update_canvas_) | ||
1792 | 802 | { | ||
1793 | 803 | canvas->ClearCanvas(); | ||
1794 | 804 | canvas->PushState(); | ||
1795 | 805 | redraw_text = true; | ||
1796 | 806 | } | ||
1797 | 807 | else if (!last_selection_region_.empty()) | ||
1798 | 808 | { | ||
1799 | 809 | //last_selection_region_.Integerize(); | ||
1800 | 810 | canvas->PushState(); | ||
1801 | 811 | canvas->IntersectGeneralClipRegion(last_selection_region_); | ||
1802 | 812 | canvas->ClearRect(0, 0, GetBaseWidth(), GetBaseHeight()); | ||
1803 | 813 | redraw_text = true; | ||
1804 | 814 | } | ||
1805 | 815 | |||
1806 | 816 | if (redraw_text) | ||
1807 | 817 | { | ||
1808 | 818 | cairo_set_source_rgb(canvas->GetInternalContext(), | ||
1809 | 819 | _text_color.red, | ||
1810 | 820 | _text_color.green, | ||
1811 | 821 | _text_color.blue); | ||
1812 | 822 | |||
1813 | 823 | cairo_move_to(canvas->GetInternalContext(), | ||
1814 | 824 | scroll_offset_x_ + kInnerBorderX, | ||
1815 | 825 | scroll_offset_y_ + kInnerBorderY); | ||
1816 | 826 | |||
1817 | 827 | pango_cairo_show_layout(canvas->GetInternalContext(), layout); | ||
1818 | 828 | |||
1819 | 829 | canvas->PopState(); | ||
1820 | 830 | } | ||
1821 | 831 | |||
1822 | 832 | // Draw selection background. | ||
1823 | 833 | // Selection in a single line may be not continual, so we use pango to | ||
1824 | 834 | // get the x-ranges of each selection range in one line, and draw them | ||
1825 | 835 | // separately. | ||
1826 | 836 | if (!selection_region_.empty()) | ||
1827 | 837 | { | ||
1828 | 838 | canvas->PushState(); | ||
1829 | 839 | //selection_region_.Integerize(); | ||
1830 | 840 | canvas->IntersectGeneralClipRegion(selection_region_); | ||
1831 | 841 | |||
1832 | 842 | Color selection_color = GetSelectionBackgroundColor(); | ||
1833 | 843 | Color text_color = GetSelectionTextColor(); | ||
1834 | 844 | |||
1835 | 845 | cairo_set_source_rgb(canvas->GetInternalContext(), | ||
1836 | 846 | selection_color.red, | ||
1837 | 847 | selection_color.green, | ||
1838 | 848 | selection_color.blue); | ||
1839 | 849 | cairo_paint(canvas->GetInternalContext()); | ||
1840 | 850 | |||
1841 | 851 | cairo_move_to(canvas->GetInternalContext(), | ||
1842 | 852 | scroll_offset_x_ + kInnerBorderX, | ||
1843 | 853 | scroll_offset_y_ + kInnerBorderY); | ||
1844 | 854 | cairo_set_source_rgb(canvas->GetInternalContext(), | ||
1845 | 855 | text_color.red, | ||
1846 | 856 | text_color.green, | ||
1847 | 857 | text_color.blue); | ||
1848 | 858 | pango_cairo_show_layout(canvas->GetInternalContext(), layout); | ||
1849 | 859 | canvas->PopState(); | ||
1850 | 860 | } | ||
1851 | 861 | } | ||
1852 | 862 | |||
1853 | 863 | bool TextEntryIM::CursorBlinkCallback(TextEntryIM *self) | ||
1854 | 864 | { | ||
1855 | 865 | if (self->cursor_blink_status_) | ||
1856 | 866 | self->ShowCursor(); | ||
1857 | 867 | else | ||
1858 | 868 | self->HideCursor(); | ||
1859 | 869 | |||
1860 | 870 | if (--self->cursor_blink_status_ < 0) | ||
1861 | 871 | self->cursor_blink_status_ = 2; | ||
1862 | 872 | |||
1863 | 873 | return true; | ||
1864 | 874 | } | ||
1865 | 875 | |||
1866 | 876 | void TextEntryIM::QueueCursorBlink() | ||
1867 | 877 | { | ||
1868 | 878 | if (!cursor_blink_timer_) | ||
1869 | 879 | cursor_blink_timer_ = g_timeout_add(kCursorBlinkTimeout, | ||
1870 | 880 | (GSourceFunc)&CursorBlinkCallback, | ||
1871 | 881 | this); | ||
1872 | 882 | } | ||
1873 | 883 | |||
1874 | 884 | void TextEntryIM::ShowCursor() | ||
1875 | 885 | { | ||
1876 | 886 | if (!cursor_visible_) | ||
1877 | 887 | { | ||
1878 | 888 | cursor_visible_ = true; | ||
1879 | 889 | if (focused_ && !readonly_) | ||
1880 | 890 | { | ||
1881 | 891 | cursor_moved_ = true; | ||
1882 | 892 | QueueRefresh(false, false); | ||
1883 | 893 | } | ||
1884 | 894 | } | ||
1885 | 895 | } | ||
1886 | 896 | |||
1887 | 897 | void TextEntryIM::HideCursor() | ||
1888 | 898 | { | ||
1889 | 899 | if (cursor_visible_) | ||
1890 | 900 | { | ||
1891 | 901 | cursor_visible_ = false; | ||
1892 | 902 | if (focused_ && !readonly_) | ||
1893 | 903 | { | ||
1894 | 904 | cursor_moved_ = true; | ||
1895 | 905 | QueueRefresh(false, false); | ||
1896 | 906 | } | ||
1897 | 907 | } | ||
1898 | 908 | } | ||
1899 | 909 | |||
1900 | 910 | void TextEntryIM::GetCursorRects(Rect *strong, Rect *weak) | ||
1901 | 911 | { | ||
1902 | 912 | int strong_x, strong_y, strong_height; | ||
1903 | 913 | int weak_x, weak_y, weak_height; | ||
1904 | 914 | GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, | ||
1905 | 915 | &weak_x, &weak_y, &weak_height); | ||
1906 | 916 | |||
1907 | 917 | strong->x = | ||
1908 | 918 | strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth; | ||
1909 | 919 | strong->width = kStrongCursorBarWidth * 2; | ||
1910 | 920 | strong->y = strong_y + kInnerBorderY + scroll_offset_y_; | ||
1911 | 921 | strong->height = strong_height; | ||
1912 | 922 | |||
1913 | 923 | if (weak_x != strong_x) | ||
1914 | 924 | { | ||
1915 | 925 | weak->x = weak_x+ kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth; | ||
1916 | 926 | weak->width = kWeakCursorBarWidth * 2; | ||
1917 | 927 | weak->y = weak_y+ kInnerBorderY + scroll_offset_y_; | ||
1918 | 928 | weak->height = weak_height; | ||
1919 | 929 | } | ||
1920 | 930 | else | ||
1921 | 931 | { | ||
1922 | 932 | *weak = *strong; | ||
1923 | 933 | } | ||
1924 | 934 | } | ||
1925 | 935 | |||
1926 | 936 | void TextEntryIM::UpdateCursorRegion() | ||
1927 | 937 | { | ||
1928 | 938 | cursor_region_.clear(); | ||
1929 | 939 | |||
1930 | 940 | Rect strong, weak; | ||
1931 | 941 | GetCursorRects(&strong, &weak); | ||
1932 | 942 | |||
1933 | 943 | cursor_region_.push_back(strong); | ||
1934 | 944 | cursor_region_.push_back(weak); | ||
1935 | 945 | } | ||
1936 | 946 | |||
1937 | 947 | |||
1938 | 948 | void TextEntryIM::DrawCursor(CairoGraphics *canvas) | ||
1939 | 949 | { | ||
1940 | 950 | if (!cursor_visible_) | ||
1941 | 951 | return; | ||
1942 | 952 | |||
1943 | 953 | int strong_x, strong_y, strong_height; | ||
1944 | 954 | int weak_x, weak_y, weak_height; | ||
1945 | 955 | GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, | ||
1946 | 956 | &weak_x, &weak_y, &weak_height); | ||
1947 | 957 | |||
1948 | 958 | // Draw strong cursor. | ||
1949 | 959 | // 0.5 is for cairo drawing between the grid | ||
1950 | 960 | canvas->DrawLine(strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, | ||
1951 | 961 | strong_y + kInnerBorderY + scroll_offset_y_, | ||
1952 | 962 | strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, | ||
1953 | 963 | strong_y + strong_height + kInnerBorderY + scroll_offset_y_, | ||
1954 | 964 | kStrongCursorLineWidth, kStrongCursorColor); | ||
1955 | 965 | // Draw a small arror towards weak cursor | ||
1956 | 966 | if (strong_x > weak_x) | ||
1957 | 967 | { | ||
1958 | 968 | canvas->DrawLine( | ||
1959 | 969 | strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth, | ||
1960 | 970 | strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, | ||
1961 | 971 | strong_x + kInnerBorderX + scroll_offset_x_, | ||
1962 | 972 | strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, | ||
1963 | 973 | kStrongCursorLineWidth, kStrongCursorColor); | ||
1964 | 974 | } | ||
1965 | 975 | else if (strong_x < weak_x) | ||
1966 | 976 | { | ||
1967 | 977 | canvas->DrawLine( | ||
1968 | 978 | strong_x + kInnerBorderX + scroll_offset_x_, | ||
1969 | 979 | strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, | ||
1970 | 980 | strong_x + kInnerBorderX + scroll_offset_x_ + kStrongCursorBarWidth, | ||
1971 | 981 | strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, | ||
1972 | 982 | kStrongCursorLineWidth, kStrongCursorColor); | ||
1973 | 983 | } | ||
1974 | 984 | |||
1975 | 985 | if (strong_x != weak_x ) | ||
1976 | 986 | { | ||
1977 | 987 | // Draw weak cursor. | ||
1978 | 988 | canvas->DrawLine(weak_x + kInnerBorderX + scroll_offset_x_, | ||
1979 | 989 | weak_y + kInnerBorderY + scroll_offset_y_, | ||
1980 | 990 | weak_x + kInnerBorderX + scroll_offset_x_, | ||
1981 | 991 | weak_y + weak_height + kInnerBorderY + scroll_offset_y_, | ||
1982 | 992 | kWeakCursorLineWidth, kWeakCursorColor); | ||
1983 | 993 | // Draw a small arror towards strong cursor | ||
1984 | 994 | if (weak_x > strong_x) | ||
1985 | 995 | { | ||
1986 | 996 | canvas->DrawLine( | ||
1987 | 997 | weak_x + kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth, | ||
1988 | 998 | weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, | ||
1989 | 999 | weak_x + kInnerBorderX + scroll_offset_x_, | ||
1990 | 1000 | weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, | ||
1991 | 1001 | kWeakCursorLineWidth, kWeakCursorColor); | ||
1992 | 1002 | } | ||
1993 | 1003 | else | ||
1994 | 1004 | { | ||
1995 | 1005 | canvas->DrawLine( | ||
1996 | 1006 | weak_x + kInnerBorderX + scroll_offset_x_, | ||
1997 | 1007 | weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, | ||
1998 | 1008 | weak_x + kInnerBorderX + scroll_offset_x_ + kWeakCursorBarWidth, | ||
1999 | 1009 | weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, | ||
2000 | 1010 | kWeakCursorLineWidth, kWeakCursorColor); | ||
2001 | 1011 | } | ||
2002 | 1012 | } | ||
2003 | 1013 | } | ||
2004 | 1014 | |||
2005 | 1015 | Color TextEntryIM::GetSelectionBackgroundColor() | ||
2006 | 1016 | { | ||
2007 | 1017 | return kDefaultSelectionBackgroundColor; | ||
2008 | 1018 | } | ||
2009 | 1019 | |||
2010 | 1020 | Color TextEntryIM::GetSelectionTextColor() | ||
2011 | 1021 | { | ||
2012 | 1022 | return kDefaultSelectionTextColor; | ||
2013 | 1023 | } | ||
2014 | 1024 | |||
2015 | 1025 | void TextEntryIM::GetCursorLocationInLayout(int *strong_x, int *strong_y, | ||
2016 | 1026 | int *strong_height, | ||
2017 | 1027 | int *weak_x, int *weak_y, | ||
2018 | 1028 | int *weak_height) | ||
2019 | 1029 | { | ||
2020 | 1030 | PangoLayout *layout = EnsureLayout(); | ||
2021 | 1031 | int cursor_index = TextIndexToLayoutIndex(cursor_, true); | ||
2022 | 1032 | |||
2023 | 1033 | PangoRectangle strong, weak; | ||
2024 | 1034 | pango_layout_get_cursor_pos(layout, cursor_index, &strong, &weak); | ||
2025 | 1035 | |||
2026 | 1036 | if (strong_x) | ||
2027 | 1037 | *strong_x = PANGO_PIXELS(strong.x); | ||
2028 | 1038 | if (strong_y) | ||
2029 | 1039 | *strong_y = PANGO_PIXELS(strong.y); | ||
2030 | 1040 | if (strong_height) | ||
2031 | 1041 | *strong_height = PANGO_PIXELS(strong.height); | ||
2032 | 1042 | if (weak_x) | ||
2033 | 1043 | *weak_x = PANGO_PIXELS(weak.x); | ||
2034 | 1044 | if (weak_y) | ||
2035 | 1045 | *weak_y = PANGO_PIXELS(weak.y); | ||
2036 | 1046 | if (weak_height) | ||
2037 | 1047 | *weak_height = PANGO_PIXELS(weak.height); | ||
2038 | 1048 | } | ||
2039 | 1049 | |||
2040 | 1050 | PangoLayout* TextEntryIM::EnsureLayout() | ||
2041 | 1051 | { | ||
2042 | 1052 | if (!cached_layout_) | ||
2043 | 1053 | { | ||
2044 | 1054 | cached_layout_ = CreateLayout(); | ||
2045 | 1055 | } | ||
2046 | 1056 | return cached_layout_; | ||
2047 | 1057 | } | ||
2048 | 1058 | |||
2049 | 1059 | void TextEntryIM::QueueTextDraw() | ||
2050 | 1060 | { | ||
2051 | 1061 | if (content_modified_) | ||
2052 | 1062 | { | ||
2053 | 1063 | UpdateSelectionRegion(); | ||
2054 | 1064 | UpdateCursorRegion(); | ||
2055 | 1065 | QueueDraw(); //owner->QueueDraw(); | ||
2056 | 1066 | content_modified_ = false; | ||
2057 | 1067 | update_canvas_ = true; | ||
2058 | 1068 | } | ||
2059 | 1069 | else | ||
2060 | 1070 | { | ||
2061 | 1071 | if (selection_changed_) | ||
2062 | 1072 | { | ||
2063 | 1073 | UpdateSelectionRegion(); | ||
2064 | 1074 | if (!last_selection_region_.empty()) | ||
2065 | 1075 | QueueDraw(); //owner_->QueueDrawRegion(last_selection_region_); | ||
2066 | 1076 | if (!selection_region_.empty()) | ||
2067 | 1077 | QueueDraw(); //owner_->QueueDrawRegion(selection_region_); | ||
2068 | 1078 | selection_changed_ = false; | ||
2069 | 1079 | } | ||
2070 | 1080 | if (cursor_moved_) | ||
2071 | 1081 | { | ||
2072 | 1082 | UpdateCursorRegion(); | ||
2073 | 1083 | if (!last_cursor_region_.empty()) | ||
2074 | 1084 | QueueDraw(); //owner_->QueueDrawRegion(last_cursor_region_); | ||
2075 | 1085 | if (!cursor_region_.empty()) | ||
2076 | 1086 | QueueDraw(); //owner_->QueueDrawRegion(cursor_region_); | ||
2077 | 1087 | cursor_moved_ = false; | ||
2078 | 1088 | } | ||
2079 | 1089 | } | ||
2080 | 1090 | } | ||
2081 | 1091 | |||
2082 | 1092 | void TextEntryIM::ResetLayout() | ||
2083 | 1093 | { | ||
2084 | 1094 | if (cached_layout_) | ||
2085 | 1095 | { | ||
2086 | 1096 | g_object_unref(cached_layout_); | ||
2087 | 1097 | cached_layout_ = NULL; | ||
2088 | 1098 | content_modified_ = true; | ||
2089 | 1099 | } | ||
2090 | 1100 | } | ||
2091 | 1101 | |||
2092 | 1102 | PangoLayout* TextEntryIM::CreateLayout() | ||
2093 | 1103 | { | ||
2094 | 1104 | // Creates the pango layout with a temporary canvas that is not zoomed. | ||
2095 | 1105 | CairoGraphics *canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, 1, 1); | ||
2096 | 1106 | PangoLayout *layout = pango_cairo_create_layout(canvas->GetInternalContext()); | ||
2097 | 1107 | delete canvas; | ||
2098 | 1108 | PangoAttrList *tmp_attrs = pango_attr_list_new(); | ||
2099 | 1109 | std::string tmp_string; | ||
2100 | 1110 | |||
2101 | 1111 | /* Set necessary parameters */ | ||
2102 | 1112 | pango_cairo_context_set_font_options(pango_layout_get_context(layout), | ||
2103 | 1113 | font_options_); | ||
2104 | 1114 | pango_cairo_context_set_resolution(pango_layout_get_context(layout), | ||
2105 | 1115 | font_dpi_); | ||
2106 | 1116 | |||
2107 | 1117 | if (wrap_) | ||
2108 | 1118 | { | ||
2109 | 1119 | pango_layout_set_width(layout, (GetBaseWidth() - kInnerBorderX * 2) * PANGO_SCALE); | ||
2110 | 1120 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); | ||
2111 | 1121 | } | ||
2112 | 1122 | else | ||
2113 | 1123 | { | ||
2114 | 1124 | pango_layout_set_width(layout, -1); | ||
2115 | 1125 | } | ||
2116 | 1126 | |||
2117 | 1127 | pango_layout_set_single_paragraph_mode(layout, !multiline_); | ||
2118 | 1128 | |||
2119 | 1129 | if (preedit_.length()) | ||
2120 | 1130 | { | ||
2121 | 1131 | size_t cursor_index = static_cast<size_t>(cursor_); | ||
2122 | 1132 | size_t text_length = text_.length(); | ||
2123 | 1133 | size_t preedit_length = preedit_.length(); | ||
2124 | 1134 | if (visible_) | ||
2125 | 1135 | { | ||
2126 | 1136 | tmp_string = text_; | ||
2127 | 1137 | tmp_string.insert(cursor_index, preedit_); | ||
2128 | 1138 | } | ||
2129 | 1139 | else | ||
2130 | 1140 | { | ||
2131 | 1141 | size_t nchars = g_utf8_strlen(text_.c_str(), text_length); | ||
2132 | 1142 | size_t preedit_nchars = g_utf8_strlen(preedit_.c_str(), preedit_length); | ||
2133 | 1143 | nchars += preedit_nchars; | ||
2134 | 1144 | tmp_string.reserve(password_char_.length() * nchars); | ||
2135 | 1145 | for (size_t i = 0; i < nchars; ++i) | ||
2136 | 1146 | tmp_string.append(password_char_); | ||
2137 | 1147 | size_t cursor_offset = | ||
2138 | 1148 | g_utf8_pointer_to_offset(text_.c_str(), text_.c_str() + cursor_index); | ||
2139 | 1149 | /* Fix cursor index and preedit_length */ | ||
2140 | 1150 | cursor_index = cursor_offset * password_char_.length(); | ||
2141 | 1151 | preedit_length = preedit_nchars * password_char_.length(); | ||
2142 | 1152 | } | ||
2143 | 1153 | if (preedit_attrs_) | ||
2144 | 1154 | pango_attr_list_splice(tmp_attrs, preedit_attrs_, | ||
2145 | 1155 | static_cast<int>(cursor_index), | ||
2146 | 1156 | static_cast<int>(preedit_length)); | ||
2147 | 1157 | } | ||
2148 | 1158 | else | ||
2149 | 1159 | { | ||
2150 | 1160 | if (visible_) | ||
2151 | 1161 | { | ||
2152 | 1162 | tmp_string = text_; | ||
2153 | 1163 | } | ||
2154 | 1164 | else | ||
2155 | 1165 | { | ||
2156 | 1166 | size_t nchars = g_utf8_strlen(text_.c_str(), text_.length()); | ||
2157 | 1167 | tmp_string.reserve(password_char_.length() * nchars); | ||
2158 | 1168 | for (size_t i = 0; i < nchars; ++i) | ||
2159 | 1169 | tmp_string.append(password_char_); | ||
2160 | 1170 | } | ||
2161 | 1171 | } | ||
2162 | 1172 | |||
2163 | 1173 | int pre_completion_length = tmp_string.length(); | ||
2164 | 1174 | |||
2165 | 1175 | if (!completion_.empty() && !wrap_) | ||
2166 | 1176 | { | ||
2167 | 1177 | tmp_string = text_ + completion_; | ||
2168 | 1178 | } | ||
2169 | 1179 | |||
2170 | 1180 | pango_layout_set_text(layout, tmp_string.c_str(), | ||
2171 | 1181 | static_cast<int>(tmp_string.length())); | ||
2172 | 1182 | |||
2173 | 1183 | /* Set necessary attributes */ | ||
2174 | 1184 | PangoAttribute *attr; | ||
2175 | 1185 | if (underline_) | ||
2176 | 1186 | { | ||
2177 | 1187 | attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); | ||
2178 | 1188 | attr->start_index = 0; | ||
2179 | 1189 | attr->end_index = static_cast<guint>(pre_completion_length); | ||
2180 | 1190 | pango_attr_list_insert(tmp_attrs, attr); | ||
2181 | 1191 | } | ||
2182 | 1192 | if (strikeout_) | ||
2183 | 1193 | { | ||
2184 | 1194 | attr = pango_attr_strikethrough_new(TRUE); | ||
2185 | 1195 | attr->start_index = 0; | ||
2186 | 1196 | attr->end_index = static_cast<guint>(pre_completion_length); | ||
2187 | 1197 | pango_attr_list_insert(tmp_attrs, attr); | ||
2188 | 1198 | } | ||
2189 | 1199 | if (!completion_.empty() && !wrap_) | ||
2190 | 1200 | { | ||
2191 | 1201 | attr = pango_attr_foreground_new(65535 * completion_color_.red, | ||
2192 | 1202 | 65535 * completion_color_.green, | ||
2193 | 1203 | 65535 * completion_color_.blue); | ||
2194 | 1204 | attr->start_index = static_cast<guint>(pre_completion_length); | ||
2195 | 1205 | attr->end_index = static_cast<guint>(tmp_string.length()); | ||
2196 | 1206 | pango_attr_list_insert(tmp_attrs, attr); | ||
2197 | 1207 | } | ||
2198 | 1208 | /* Set font desc */ | ||
2199 | 1209 | { | ||
2200 | 1210 | /* safe to down_cast here, because we know the actual implementation. */ | ||
2201 | 1211 | CairoFont *font = new CairoFont( | ||
2202 | 1212 | font_family_.empty() ? kDefaultFontName : font_family_.c_str(), | ||
2203 | 1213 | font_size_, | ||
2204 | 1214 | italic_ ? CairoFont::STYLE_ITALIC : CairoFont::STYLE_NORMAL, | ||
2205 | 1215 | bold_ ? CairoFont::WEIGHT_BOLD : CairoFont::WEIGHT_NORMAL); | ||
2206 | 1216 | nuxAssert(font); | ||
2207 | 1217 | attr = pango_attr_font_desc_new(font->GetFontDescription()); | ||
2208 | 1218 | attr->start_index = 0; | ||
2209 | 1219 | attr->end_index = static_cast<unsigned int>(tmp_string.length()); | ||
2210 | 1220 | pango_attr_list_insert(tmp_attrs, attr); | ||
2211 | 1221 | pango_layout_set_font_description(layout, font->GetFontDescription()); | ||
2212 | 1222 | font->Destroy(); | ||
2213 | 1223 | } | ||
2214 | 1224 | pango_layout_set_attributes(layout, tmp_attrs); | ||
2215 | 1225 | pango_attr_list_unref(tmp_attrs); | ||
2216 | 1226 | |||
2217 | 1227 | /* Set alignment according to text direction. Only set layout's alignment | ||
2218 | 1228 | * when it's not wrapped and in single line mode. | ||
2219 | 1229 | */ | ||
2220 | 1230 | if (!wrap_ && pango_layout_get_line_count(layout) <= 1 && | ||
2221 | 1231 | align_ != CairoGraphics::ALIGN_CENTER) | ||
2222 | 1232 | { | ||
2223 | 1233 | PangoDirection dir; | ||
2224 | 1234 | if (visible_) | ||
2225 | 1235 | dir = pango_find_base_dir(tmp_string.c_str(), | ||
2226 | 1236 | static_cast<int>(tmp_string.length())); | ||
2227 | 1237 | else | ||
2228 | 1238 | dir = PANGO_DIRECTION_NEUTRAL; | ||
2229 | 1239 | |||
2230 | 1240 | if (dir == PANGO_DIRECTION_NEUTRAL) | ||
2231 | 1241 | { | ||
2232 | 1242 | // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); | ||
2233 | 1243 | // if (widget && gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) | ||
2234 | 1244 | // dir = PANGO_DIRECTION_RTL; | ||
2235 | 1245 | // else | ||
2236 | 1246 | // dir = PANGO_DIRECTION_LTR; | ||
2237 | 1247 | |||
2238 | 1248 | dir = PANGO_DIRECTION_LTR; | ||
2239 | 1249 | } | ||
2240 | 1250 | |||
2241 | 1251 | // If wordWrap is false then "justify" alignment has no effect. | ||
2242 | 1252 | PangoAlignment pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? | ||
2243 | 1253 | PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT); | ||
2244 | 1254 | |||
2245 | 1255 | // Invert the alignment if text direction is right to left. | ||
2246 | 1256 | if (dir == PANGO_DIRECTION_RTL) | ||
2247 | 1257 | { | ||
2248 | 1258 | pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? | ||
2249 | 1259 | PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); | ||
2250 | 1260 | } | ||
2251 | 1261 | |||
2252 | 1262 | pango_layout_set_alignment(layout, pango_align); | ||
2253 | 1263 | pango_layout_set_justify(layout, FALSE); | ||
2254 | 1264 | } | ||
2255 | 1265 | else if (align_ == CairoGraphics::ALIGN_JUSTIFY) | ||
2256 | 1266 | { | ||
2257 | 1267 | pango_layout_set_justify(layout, TRUE); | ||
2258 | 1268 | pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); | ||
2259 | 1269 | } | ||
2260 | 1270 | else if (align_ == CairoGraphics::ALIGN_RIGHT) | ||
2261 | 1271 | { | ||
2262 | 1272 | pango_layout_set_justify(layout, FALSE); | ||
2263 | 1273 | pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); | ||
2264 | 1274 | } | ||
2265 | 1275 | else if (align_ == CairoGraphics::ALIGN_CENTER) | ||
2266 | 1276 | { | ||
2267 | 1277 | pango_layout_set_justify(layout, FALSE); | ||
2268 | 1278 | pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); | ||
2269 | 1279 | } | ||
2270 | 1280 | else | ||
2271 | 1281 | { | ||
2272 | 1282 | pango_layout_set_justify(layout, FALSE); | ||
2273 | 1283 | pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); | ||
2274 | 1284 | } | ||
2275 | 1285 | |||
2276 | 1286 | { | ||
2277 | 1287 | PangoContext *context; | ||
2278 | 1288 | PangoFontMetrics *metrics; | ||
2279 | 1289 | int ascent, descent; | ||
2280 | 1290 | |||
2281 | 1291 | context = pango_layout_get_context(layout); | ||
2282 | 1292 | metrics = pango_context_get_metrics(context, | ||
2283 | 1293 | pango_layout_get_font_description(layout), | ||
2284 | 1294 | pango_context_get_language(context)); | ||
2285 | 1295 | |||
2286 | 1296 | ascent = pango_font_metrics_get_ascent(metrics); | ||
2287 | 1297 | descent = pango_font_metrics_get_descent(metrics); | ||
2288 | 1298 | |||
2289 | 1299 | int full_height = PANGO_PIXELS(ascent + descent) + (kInnerBorderY * 2); | ||
2290 | 1300 | SetMinimumHeight(full_height); | ||
2291 | 1301 | |||
2292 | 1302 | pango_font_metrics_unref(metrics); | ||
2293 | 1303 | } | ||
2294 | 1304 | |||
2295 | 1305 | return layout; | ||
2296 | 1306 | } | ||
2297 | 1307 | |||
2298 | 1308 | int TextEntryIM::TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor) | ||
2299 | 1309 | { | ||
2300 | 1310 | if (visible_) | ||
2301 | 1311 | { | ||
2302 | 1312 | if (text_index < cursor_) | ||
2303 | 1313 | return text_index; | ||
2304 | 1314 | |||
2305 | 1315 | if (text_index == cursor_ && consider_preedit_cursor) | ||
2306 | 1316 | return text_index + preedit_cursor_; | ||
2307 | 1317 | |||
2308 | 1318 | return text_index + static_cast<int>(preedit_.length()); | ||
2309 | 1319 | } | ||
2310 | 1320 | |||
2311 | 1321 | const char *text = text_.c_str(); | ||
2312 | 1322 | int offset = static_cast<int>( | ||
2313 | 1323 | g_utf8_pointer_to_offset(text, text + text_index)); | ||
2314 | 1324 | int preedit_offset = 0; | ||
2315 | 1325 | int preedit_chars = 0; | ||
2316 | 1326 | if (preedit_.length()) | ||
2317 | 1327 | { | ||
2318 | 1328 | const char *preedit_text = preedit_.c_str(); | ||
2319 | 1329 | preedit_offset = static_cast<int>(g_utf8_pointer_to_offset( | ||
2320 | 1330 | preedit_text, preedit_text + preedit_cursor_)); | ||
2321 | 1331 | preedit_chars = static_cast<int>(g_utf8_strlen( | ||
2322 | 1332 | preedit_text, preedit_.length())); | ||
2323 | 1333 | } | ||
2324 | 1334 | |||
2325 | 1335 | int password_char_length = static_cast<int>(password_char_.length()); | ||
2326 | 1336 | |||
2327 | 1337 | if (text_index < cursor_) | ||
2328 | 1338 | return offset * password_char_length; | ||
2329 | 1339 | |||
2330 | 1340 | if (text_index == cursor_ && consider_preedit_cursor) | ||
2331 | 1341 | return (offset + preedit_offset) * password_char_length; | ||
2332 | 1342 | |||
2333 | 1343 | return (offset + preedit_chars) * password_char_length; | ||
2334 | 1344 | } | ||
2335 | 1345 | |||
2336 | 1346 | |||
2337 | 1347 | int TextEntryIM::LayoutIndexToTextIndex(int layout_index) | ||
2338 | 1348 | { | ||
2339 | 1349 | if (visible_) | ||
2340 | 1350 | { | ||
2341 | 1351 | if (layout_index < cursor_) | ||
2342 | 1352 | return layout_index; | ||
2343 | 1353 | |||
2344 | 1354 | int preedit_length = static_cast<int>(preedit_.length()); | ||
2345 | 1355 | if (layout_index >= cursor_ + preedit_length) | ||
2346 | 1356 | return layout_index - preedit_length; | ||
2347 | 1357 | |||
2348 | 1358 | return cursor_; | ||
2349 | 1359 | } | ||
2350 | 1360 | |||
2351 | 1361 | int password_char_length = static_cast<int>(password_char_.length()); | ||
2352 | 1362 | nuxAssert(layout_index % password_char_length == 0); | ||
2353 | 1363 | |||
2354 | 1364 | int offset = layout_index / password_char_length; | ||
2355 | 1365 | |||
2356 | 1366 | const char *text = text_.c_str(); | ||
2357 | 1367 | int cursor_offset = static_cast<int>( | ||
2358 | 1368 | g_utf8_pointer_to_offset(text, text + cursor_)); | ||
2359 | 1369 | int preedit_chars = static_cast<int>( | ||
2360 | 1370 | g_utf8_strlen(preedit_.c_str(), preedit_.length())); | ||
2361 | 1371 | |||
2362 | 1372 | if (offset < cursor_offset) | ||
2363 | 1373 | return static_cast<int>(g_utf8_offset_to_pointer(text, offset) - text); | ||
2364 | 1374 | |||
2365 | 1375 | if (offset >= cursor_offset + preedit_chars) | ||
2366 | 1376 | return static_cast<int>( | ||
2367 | 1377 | g_utf8_offset_to_pointer(text, offset - preedit_chars) - text); | ||
2368 | 1378 | |||
2369 | 1379 | return cursor_; | ||
2370 | 1380 | } | ||
2371 | 1381 | |||
2372 | 1382 | int TextEntryIM::GetCharLength(int index) | ||
2373 | 1383 | { | ||
2374 | 1384 | const char *text = text_.c_str(); | ||
2375 | 1385 | const char *ptr = text + index; | ||
2376 | 1386 | const char *end = text + text_.length(); | ||
2377 | 1387 | const char *next = g_utf8_find_next_char(ptr, end); | ||
2378 | 1388 | return static_cast<int>(next ? static_cast<int>(next - ptr) : end - ptr); | ||
2379 | 1389 | } | ||
2380 | 1390 | |||
2381 | 1391 | int TextEntryIM::GetPrevCharLength(int index) | ||
2382 | 1392 | { | ||
2383 | 1393 | const char *text = text_.c_str(); | ||
2384 | 1394 | const char *ptr = text + index; | ||
2385 | 1395 | const char *prev = g_utf8_find_prev_char(text, ptr); | ||
2386 | 1396 | return static_cast<int>(prev ? static_cast<int>(ptr - prev) : ptr - text); | ||
2387 | 1397 | } | ||
2388 | 1398 | |||
2389 | 1399 | void TextEntryIM::EnterText(const char *str) | ||
2390 | 1400 | { | ||
2391 | 1401 | if (readonly_ || !str || !*str) return; | ||
2392 | 1402 | |||
2393 | 1403 | if (GetSelectionBounds(NULL, NULL)) | ||
2394 | 1404 | { | ||
2395 | 1405 | DeleteSelection(); | ||
2396 | 1406 | } | ||
2397 | 1407 | else if (overwrite_ && cursor_ != static_cast<int>(text_.length())) | ||
2398 | 1408 | { | ||
2399 | 1409 | DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); | ||
2400 | 1410 | } | ||
2401 | 1411 | |||
2402 | 1412 | std::string tmp_text; | ||
2403 | 1413 | if (!multiline_) | ||
2404 | 1414 | { | ||
2405 | 1415 | tmp_text = CleanupLineBreaks(str); | ||
2406 | 1416 | str = tmp_text.c_str(); | ||
2407 | 1417 | } | ||
2408 | 1418 | |||
2409 | 1419 | const char *end = NULL; | ||
2410 | 1420 | g_utf8_validate(str, -1, &end); | ||
2411 | 1421 | if (end > str) | ||
2412 | 1422 | { | ||
2413 | 1423 | size_t len = end - str; | ||
2414 | 1424 | |||
2415 | 1425 | text_.insert(cursor_, str, len); | ||
2416 | 1426 | cursor_ += static_cast<int>(len); | ||
2417 | 1427 | selection_bound_ += static_cast<int>(len); | ||
2418 | 1428 | } | ||
2419 | 1429 | |||
2420 | 1430 | ResetLayout(); | ||
2421 | 1431 | sigTextChanged.emit(this); | ||
2422 | 1432 | } | ||
2423 | 1433 | |||
2424 | 1434 | void TextEntryIM::DeleteText(int start, int end) | ||
2425 | 1435 | { | ||
2426 | 1436 | if (readonly_) return; | ||
2427 | 1437 | |||
2428 | 1438 | int text_length = static_cast<int>(text_.length()); | ||
2429 | 1439 | if (start < 0) | ||
2430 | 1440 | start = 0; | ||
2431 | 1441 | else if (start > text_length) | ||
2432 | 1442 | start = text_length; | ||
2433 | 1443 | |||
2434 | 1444 | if (end < 0) | ||
2435 | 1445 | end = 0; | ||
2436 | 1446 | else if (end > text_length) | ||
2437 | 1447 | end = text_length; | ||
2438 | 1448 | |||
2439 | 1449 | if (start > end) | ||
2440 | 1450 | std::swap(start, end); | ||
2441 | 1451 | else if (start == end) | ||
2442 | 1452 | return; | ||
2443 | 1453 | |||
2444 | 1454 | text_.erase(start, end - start); | ||
2445 | 1455 | |||
2446 | 1456 | if (cursor_ >= end) | ||
2447 | 1457 | cursor_ -= (end - start); | ||
2448 | 1458 | if (selection_bound_ >= end) | ||
2449 | 1459 | selection_bound_ -= (end - start); | ||
2450 | 1460 | |||
2451 | 1461 | ResetLayout(); | ||
2452 | 1462 | sigTextChanged.emit(this); | ||
2453 | 1463 | } | ||
2454 | 1464 | |||
2455 | 1465 | void TextEntryIM::SelectWord() | ||
2456 | 1466 | { | ||
2457 | 1467 | int selection_bound = MoveWords(cursor_, -1); | ||
2458 | 1468 | int cursor = MoveWords(selection_bound, 1); | ||
2459 | 1469 | SetSelectionBounds(selection_bound, cursor); | ||
2460 | 1470 | } | ||
2461 | 1471 | |||
2462 | 1472 | void TextEntryIM::SelectLine() | ||
2463 | 1473 | { | ||
2464 | 1474 | int selection_bound = MoveLineEnds(cursor_, -1); | ||
2465 | 1475 | int cursor = MoveLineEnds(selection_bound, 1); | ||
2466 | 1476 | SetSelectionBounds(selection_bound, cursor); | ||
2467 | 1477 | } | ||
2468 | 1478 | |||
2469 | 1479 | void TextEntryIM::Select(int start, int end) { | ||
2470 | 1480 | int text_length = static_cast<int>(text_.length()); | ||
2471 | 1481 | if (start == -1) | ||
2472 | 1482 | start = text_length; | ||
2473 | 1483 | if (end == -1) | ||
2474 | 1484 | end = text_length; | ||
2475 | 1485 | |||
2476 | 1486 | start = Clamp(start, 0, text_length); | ||
2477 | 1487 | end = Clamp(end, 0, text_length); | ||
2478 | 1488 | SetSelectionBounds(start, end); | ||
2479 | 1489 | QueueRefresh(false, true); | ||
2480 | 1490 | } | ||
2481 | 1491 | |||
2482 | 1492 | void TextEntryIM::SelectAll() { | ||
2483 | 1493 | SetSelectionBounds(0, static_cast<int>(text_.length())); | ||
2484 | 1494 | QueueRefresh(false, true); | ||
2485 | 1495 | } | ||
2486 | 1496 | |||
2487 | 1497 | CairoGraphics::Alignment TextEntryIM::GetAlign() const | ||
2488 | 1498 | { | ||
2489 | 1499 | return align_; | ||
2490 | 1500 | } | ||
2491 | 1501 | |||
2492 | 1502 | void TextEntryIM::SetAlign(CairoGraphics::Alignment align) | ||
2493 | 1503 | { | ||
2494 | 1504 | align_ = align; | ||
2495 | 1505 | QueueRefresh(true, true); | ||
2496 | 1506 | } | ||
2497 | 1507 | |||
2498 | 1508 | bool TextEntryIM::im_active() | ||
2499 | 1509 | { | ||
2500 | 1510 | return ime_active_; | ||
2501 | 1511 | } | ||
2502 | 1512 | |||
2503 | 1513 | void TextEntryIM::DeleteSelection() | ||
2504 | 1514 | { | ||
2505 | 1515 | int start, end; | ||
2506 | 1516 | if (GetSelectionBounds(&start, &end)) | ||
2507 | 1517 | DeleteText(start, end); | ||
2508 | 1518 | } | ||
2509 | 1519 | |||
2510 | 1520 | void TextEntryIM::CopyClipboard() | ||
2511 | 1521 | { | ||
2512 | 1522 | // int start, end; | ||
2513 | 1523 | // if (GetSelectionBounds(&start, &end)) | ||
2514 | 1524 | // { | ||
2515 | 1525 | // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); | ||
2516 | 1526 | // if (widget) | ||
2517 | 1527 | // { | ||
2518 | 1528 | // if (visible_) | ||
2519 | 1529 | // { | ||
2520 | 1530 | // gtk_clipboard_set_text( | ||
2521 | 1531 | // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), | ||
2522 | 1532 | // text_.c_str() + start, end - start); | ||
2523 | 1533 | // } | ||
2524 | 1534 | // else | ||
2525 | 1535 | // { | ||
2526 | 1536 | // // Don't copy real content if it's in invisible. | ||
2527 | 1537 | // std::string content; | ||
2528 | 1538 | // int nchars = static_cast<int>( | ||
2529 | 1539 | // g_utf8_strlen(text_.c_str() + start, end - start)); | ||
2530 | 1540 | // for (int i = 0; i < nchars; ++i) | ||
2531 | 1541 | // content.append(password_char_); | ||
2532 | 1542 | // gtk_clipboard_set_text( | ||
2533 | 1543 | // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), | ||
2534 | 1544 | // content.c_str(), static_cast<int>(content.length())); | ||
2535 | 1545 | // } | ||
2536 | 1546 | // } | ||
2537 | 1547 | // } | ||
2538 | 1548 | } | ||
2539 | 1549 | |||
2540 | 1550 | void TextEntryIM::CutClipboard() | ||
2541 | 1551 | { | ||
2542 | 1552 | CopyClipboard(); | ||
2543 | 1553 | DeleteSelection(); | ||
2544 | 1554 | } | ||
2545 | 1555 | |||
2546 | 1556 | void TextEntryIM::PasteClipboard() | ||
2547 | 1557 | { | ||
2548 | 1558 | // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); | ||
2549 | 1559 | // if (widget) | ||
2550 | 1560 | // { | ||
2551 | 1561 | // gtk_clipboard_request_text( | ||
2552 | 1562 | // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), | ||
2553 | 1563 | // PasteCallback, this); | ||
2554 | 1564 | // } | ||
2555 | 1565 | } | ||
2556 | 1566 | |||
2557 | 1567 | void TextEntryIM::BackSpace(MovementStep step) | ||
2558 | 1568 | { | ||
2559 | 1569 | if (GetSelectionBounds(NULL, NULL)) | ||
2560 | 1570 | { | ||
2561 | 1571 | DeleteSelection(); | ||
2562 | 1572 | } | ||
2563 | 1573 | else | ||
2564 | 1574 | { | ||
2565 | 1575 | if (cursor_ == 0) | ||
2566 | 1576 | return; | ||
2567 | 1577 | if (step == VISUALLY) | ||
2568 | 1578 | { | ||
2569 | 1579 | DeleteText(cursor_ - GetPrevCharLength(cursor_), cursor_); | ||
2570 | 1580 | } | ||
2571 | 1581 | else if (step == WORDS) | ||
2572 | 1582 | { | ||
2573 | 1583 | int new_cursor; | ||
2574 | 1584 | new_cursor = MoveWords(cursor_, -1); | ||
2575 | 1585 | DeleteText(new_cursor, cursor_); | ||
2576 | 1586 | } | ||
2577 | 1587 | } | ||
2578 | 1588 | } | ||
2579 | 1589 | |||
2580 | 1590 | void TextEntryIM::Delete(MovementStep step) | ||
2581 | 1591 | { | ||
2582 | 1592 | if (GetSelectionBounds(NULL, NULL)) | ||
2583 | 1593 | { | ||
2584 | 1594 | DeleteSelection(); | ||
2585 | 1595 | } | ||
2586 | 1596 | else | ||
2587 | 1597 | { | ||
2588 | 1598 | if (cursor_ == static_cast<int>(text_.length())) | ||
2589 | 1599 | return; | ||
2590 | 1600 | if (step == VISUALLY) | ||
2591 | 1601 | { | ||
2592 | 1602 | DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); | ||
2593 | 1603 | } | ||
2594 | 1604 | else if (step == WORDS) | ||
2595 | 1605 | { | ||
2596 | 1606 | int new_cursor; | ||
2597 | 1607 | new_cursor = MoveWords(cursor_, 1); | ||
2598 | 1608 | DeleteText(cursor_, new_cursor); | ||
2599 | 1609 | } | ||
2600 | 1610 | } | ||
2601 | 1611 | } | ||
2602 | 1612 | |||
2603 | 1613 | void TextEntryIM::ToggleOverwrite() | ||
2604 | 1614 | { | ||
2605 | 1615 | overwrite_ = !overwrite_; | ||
2606 | 1616 | } | ||
2607 | 1617 | |||
2608 | 1618 | void TextEntryIM::UpdateSelectionRegion() | ||
2609 | 1619 | { | ||
2610 | 1620 | selection_region_.clear(); | ||
2611 | 1621 | |||
2612 | 1622 | // Selection in a single line may be not continual, so we use pango to | ||
2613 | 1623 | // get the x-ranges of each selection range in one line, and draw them | ||
2614 | 1624 | // separately. | ||
2615 | 1625 | int start_index, end_index; | ||
2616 | 1626 | if (GetSelectionBounds(&start_index, &end_index)) | ||
2617 | 1627 | { | ||
2618 | 1628 | PangoLayout *layout = EnsureLayout(); | ||
2619 | 1629 | PangoRectangle line_extents, pos; | ||
2620 | 1630 | int draw_start, draw_end; | ||
2621 | 1631 | int *ranges; | ||
2622 | 1632 | int n_ranges; | ||
2623 | 1633 | int n_lines = pango_layout_get_line_count(layout); | ||
2624 | 1634 | |||
2625 | 1635 | start_index = TextIndexToLayoutIndex(start_index, false); | ||
2626 | 1636 | end_index = TextIndexToLayoutIndex(end_index, false); | ||
2627 | 1637 | |||
2628 | 1638 | for (int line_index = 0; line_index < n_lines; ++line_index) | ||
2629 | 1639 | { | ||
2630 | 1640 | #if PANGO_VERSION_CHECK(1,16,0) | ||
2631 | 1641 | PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); | ||
2632 | 1642 | #else | ||
2633 | 1643 | PangoLayoutLine *line = pango_layout_get_line(layout, line_index); | ||
2634 | 1644 | #endif | ||
2635 | 1645 | if (line->start_index + line->length < start_index) | ||
2636 | 1646 | continue; | ||
2637 | 1647 | if (end_index < line->start_index) | ||
2638 | 1648 | break; | ||
2639 | 1649 | draw_start = Max<int>(start_index, line->start_index); | ||
2640 | 1650 | draw_end = Min<int>(end_index, line->start_index + line->length); | ||
2641 | 1651 | pango_layout_line_get_x_ranges(line, draw_start, draw_end, | ||
2642 | 1652 | &ranges, &n_ranges); | ||
2643 | 1653 | pango_layout_line_get_pixel_extents(line, NULL, &line_extents); | ||
2644 | 1654 | pango_layout_index_to_pos(layout, line->start_index, &pos); | ||
2645 | 1655 | for (int i = 0; i < n_ranges; ++i) | ||
2646 | 1656 | { | ||
2647 | 1657 | selection_region_.push_back(Rect( | ||
2648 | 1658 | kInnerBorderX + scroll_offset_x_ + PANGO_PIXELS(ranges[i * 2]), | ||
2649 | 1659 | kInnerBorderY + scroll_offset_y_ + PANGO_PIXELS(pos.y), | ||
2650 | 1660 | PANGO_PIXELS(ranges[i * 2 + 1] - ranges[i * 2]), | ||
2651 | 1661 | line_extents.height)); | ||
2652 | 1662 | } | ||
2653 | 1663 | g_free(ranges); | ||
2654 | 1664 | } | ||
2655 | 1665 | } | ||
2656 | 1666 | } | ||
2657 | 1667 | |||
2658 | 1668 | void TextEntryIM::MoveCursor(MovementStep step, int count, bool extend_selection) | ||
2659 | 1669 | { | ||
2660 | 1670 | if (ime_active_) | ||
2661 | 1671 | return; | ||
2662 | 1672 | |||
2663 | 1673 | ResetImContext(); | ||
2664 | 1674 | int new_cursor = 0; | ||
2665 | 1675 | // Clear selection first if not extend it. | ||
2666 | 1676 | if (!extend_selection) | ||
2667 | 1677 | { | ||
2668 | 1678 | selection_changed_ = true; | ||
2669 | 1679 | cursor_moved_ = true; | ||
2670 | 1680 | selection_bound_ = cursor_; | ||
2671 | 1681 | cursor_moved.emit(cursor_); | ||
2672 | 1682 | } | ||
2673 | 1683 | |||
2674 | 1684 | // Calculate the new offset after motion. | ||
2675 | 1685 | switch(step) | ||
2676 | 1686 | { | ||
2677 | 1687 | case VISUALLY: | ||
2678 | 1688 | new_cursor = MoveVisually(cursor_, count); | ||
2679 | 1689 | break; | ||
2680 | 1690 | case WORDS: | ||
2681 | 1691 | new_cursor = MoveWords(cursor_, count); | ||
2682 | 1692 | break; | ||
2683 | 1693 | case DISPLAY_LINES: | ||
2684 | 1694 | new_cursor = MoveDisplayLines(cursor_, count); | ||
2685 | 1695 | break; | ||
2686 | 1696 | case DISPLAY_LINE_ENDS: | ||
2687 | 1697 | new_cursor = MoveLineEnds(cursor_, count); | ||
2688 | 1698 | break; | ||
2689 | 1699 | case PAGES: | ||
2690 | 1700 | new_cursor = MovePages(cursor_, count); | ||
2691 | 1701 | break; | ||
2692 | 1702 | case BUFFER: | ||
2693 | 1703 | nuxAssert(count == -1 || count == 1); | ||
2694 | 1704 | new_cursor = static_cast<int>(count == -1 ? 0 : text_.length()); | ||
2695 | 1705 | break; | ||
2696 | 1706 | } | ||
2697 | 1707 | |||
2698 | 1708 | if (extend_selection) | ||
2699 | 1709 | SetSelectionBounds(selection_bound_, new_cursor); | ||
2700 | 1710 | else | ||
2701 | 1711 | SetCursor(new_cursor); | ||
2702 | 1712 | |||
2703 | 1713 | QueueRefresh(true, true); | ||
2704 | 1714 | } | ||
2705 | 1715 | |||
2706 | 1716 | int TextEntryIM::MoveVisually(int current_index, int count) | ||
2707 | 1717 | { | ||
2708 | 1718 | nuxAssert(current_index >= 0 && | ||
2709 | 1719 | current_index <= static_cast<int>(text_.length())); | ||
2710 | 1720 | nuxAssert(count); | ||
2711 | 1721 | nuxAssert(preedit_.length() == 0); | ||
2712 | 1722 | |||
2713 | 1723 | PangoLayout *layout = EnsureLayout(); | ||
2714 | 1724 | const char *text = pango_layout_get_text(layout); | ||
2715 | 1725 | int index = TextIndexToLayoutIndex(current_index, false); | ||
2716 | 1726 | int new_index = 0; | ||
2717 | 1727 | int new_trailing = 0; | ||
2718 | 1728 | while (count != 0) | ||
2719 | 1729 | { | ||
2720 | 1730 | if (count > 0) | ||
2721 | 1731 | { | ||
2722 | 1732 | --count; | ||
2723 | 1733 | pango_layout_move_cursor_visually(layout, true, index, 0, 1, | ||
2724 | 1734 | &new_index, &new_trailing); | ||
2725 | 1735 | } | ||
2726 | 1736 | else if (count < 0) | ||
2727 | 1737 | { | ||
2728 | 1738 | ++count; | ||
2729 | 1739 | pango_layout_move_cursor_visually(layout, true, index, 0, -1, | ||
2730 | 1740 | &new_index, &new_trailing); | ||
2731 | 1741 | } | ||
2732 | 1742 | index = new_index; | ||
2733 | 1743 | if (index < 0 || index == G_MAXINT) | ||
2734 | 1744 | return current_index; | ||
2735 | 1745 | index = static_cast<int>(g_utf8_offset_to_pointer(text + index, new_trailing) - text); | ||
2736 | 1746 | } | ||
2737 | 1747 | return LayoutIndexToTextIndex(index); | ||
2738 | 1748 | } | ||
2739 | 1749 | |||
2740 | 1750 | int TextEntryIM::MoveWords(int current_index, int count) | ||
2741 | 1751 | { | ||
2742 | 1752 | nuxAssert(current_index >= 0 && | ||
2743 | 1753 | current_index <= static_cast<int>(text_.length())); | ||
2744 | 1754 | nuxAssert(count); | ||
2745 | 1755 | nuxAssert(preedit_.length() == 0); | ||
2746 | 1756 | |||
2747 | 1757 | if (!visible_) | ||
2748 | 1758 | { | ||
2749 | 1759 | return static_cast<int>(count > 0 ? text_.length() : 0); | ||
2750 | 1760 | } | ||
2751 | 1761 | |||
2752 | 1762 | // The cursor movement direction shall be determined by the direction of | ||
2753 | 1763 | // current text line. | ||
2754 | 1764 | PangoLayout *layout = EnsureLayout(); | ||
2755 | 1765 | int n_log_attrs; | ||
2756 | 1766 | PangoLogAttr *log_attrs; | ||
2757 | 1767 | pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs); | ||
2758 | 1768 | const char *text = pango_layout_get_text(layout); | ||
2759 | 1769 | int index = TextIndexToLayoutIndex(current_index, false); | ||
2760 | 1770 | int line_index; | ||
2761 | 1771 | pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); | ||
2762 | 1772 | |||
2763 | 1773 | // Weird bug: line_index here may be >= than line count? | ||
2764 | 1774 | int line_count = pango_layout_get_line_count(layout); | ||
2765 | 1775 | if (line_index >= line_count) | ||
2766 | 1776 | { | ||
2767 | 1777 | line_index = line_count - 1; | ||
2768 | 1778 | } | ||
2769 | 1779 | |||
2770 | 1780 | #if PANGO_VERSION_CHECK(1,16,0) | ||
2771 | 1781 | PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); | ||
2772 | 1782 | #else | ||
2773 | 1783 | PangoLayoutLine *line = pango_layout_get_line(layout, line_index); | ||
2774 | 1784 | #endif | ||
2775 | 1785 | bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); | ||
2776 | 1786 | const char *ptr = text + index; | ||
2777 | 1787 | int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr)); | ||
2778 | 1788 | while (count != 0) | ||
2779 | 1789 | { | ||
2780 | 1790 | if (((rtl && count < 0) || (!rtl && count > 0)) && *ptr) | ||
2781 | 1791 | { | ||
2782 | 1792 | if (log_attrs[offset].is_white) | ||
2783 | 1793 | { | ||
2784 | 1794 | while (ptr && *ptr && log_attrs[offset].is_white) | ||
2785 | 1795 | { | ||
2786 | 1796 | ptr = g_utf8_find_next_char(ptr, NULL); | ||
2787 | 1797 | ++offset; | ||
2788 | 1798 | } | ||
2789 | 1799 | } | ||
2790 | 1800 | else | ||
2791 | 1801 | { | ||
2792 | 1802 | if (ptr && *ptr) | ||
2793 | 1803 | { | ||
2794 | 1804 | ptr = g_utf8_find_next_char(ptr, NULL); | ||
2795 | 1805 | ++offset; | ||
2796 | 1806 | } | ||
2797 | 1807 | } | ||
2798 | 1808 | while (ptr && *ptr) | ||
2799 | 1809 | { | ||
2800 | 1810 | ptr = g_utf8_find_next_char(ptr, NULL); | ||
2801 | 1811 | ++offset; | ||
2802 | 1812 | if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) | ||
2803 | 1813 | break; | ||
2804 | 1814 | } | ||
2805 | 1815 | if (!ptr) | ||
2806 | 1816 | { | ||
2807 | 1817 | ptr = text; | ||
2808 | 1818 | while (*ptr) ++ptr; | ||
2809 | 1819 | } | ||
2810 | 1820 | } | ||
2811 | 1821 | else if (((rtl && count > 0) || (!rtl && count < 0)) && (ptr > text)) | ||
2812 | 1822 | { | ||
2813 | 1823 | if (offset > 0 && log_attrs[offset - 1].is_white) | ||
2814 | 1824 | { | ||
2815 | 1825 | while (ptr && offset > 0 && log_attrs[offset - 1].is_white) | ||
2816 | 1826 | { | ||
2817 | 1827 | ptr = g_utf8_find_prev_char(text, ptr); | ||
2818 | 1828 | --offset; | ||
2819 | 1829 | } | ||
2820 | 1830 | } | ||
2821 | 1831 | else | ||
2822 | 1832 | { | ||
2823 | 1833 | if (ptr) | ||
2824 | 1834 | { | ||
2825 | 1835 | ptr = g_utf8_find_prev_char(text, ptr); | ||
2826 | 1836 | --offset; | ||
2827 | 1837 | } | ||
2828 | 1838 | } | ||
2829 | 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. | ||
2830 | 1840 | { | ||
2831 | 1841 | ptr = g_utf8_find_prev_char(text, ptr); | ||
2832 | 1842 | --offset; | ||
2833 | 1843 | if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) | ||
2834 | 1844 | break; | ||
2835 | 1845 | } | ||
2836 | 1846 | if (!ptr) | ||
2837 | 1847 | ptr = text; | ||
2838 | 1848 | } | ||
2839 | 1849 | else | ||
2840 | 1850 | { | ||
2841 | 1851 | break; | ||
2842 | 1852 | } | ||
2843 | 1853 | if (count > 0) | ||
2844 | 1854 | --count; | ||
2845 | 1855 | else | ||
2846 | 1856 | ++count; | ||
2847 | 1857 | } | ||
2848 | 1858 | return LayoutIndexToTextIndex(static_cast<int>(ptr - text)); | ||
2849 | 1859 | } | ||
2850 | 1860 | |||
2851 | 1861 | int TextEntryIM::MoveDisplayLines(int current_index, int count) | ||
2852 | 1862 | { | ||
2853 | 1863 | nuxAssert(current_index >= 0 && | ||
2854 | 1864 | current_index <= static_cast<int>(text_.length())); | ||
2855 | 1865 | nuxAssert(count); | ||
2856 | 1866 | nuxAssert(preedit_.length() == 0); | ||
2857 | 1867 | |||
2858 | 1868 | PangoLayout *layout = EnsureLayout(); | ||
2859 | 1869 | const char *text = pango_layout_get_text(layout); | ||
2860 | 1870 | int index = TextIndexToLayoutIndex(current_index, false); | ||
2861 | 1871 | int n_lines = pango_layout_get_line_count(layout); | ||
2862 | 1872 | int line_index = 0; | ||
2863 | 1873 | int x_off = 0; | ||
2864 | 1874 | PangoRectangle rect; | ||
2865 | 1875 | |||
2866 | 1876 | // Find the current cursor X position in layout | ||
2867 | 1877 | pango_layout_index_to_line_x(layout, index, FALSE, &line_index, &x_off); | ||
2868 | 1878 | |||
2869 | 1879 | // Weird bug: line_index here may be >= than line count? | ||
2870 | 1880 | if (line_index >= n_lines) | ||
2871 | 1881 | { | ||
2872 | 1882 | line_index = n_lines - 1; | ||
2873 | 1883 | } | ||
2874 | 1884 | |||
2875 | 1885 | pango_layout_get_cursor_pos(layout, index, &rect, NULL); | ||
2876 | 1886 | x_off = rect.x; | ||
2877 | 1887 | |||
2878 | 1888 | line_index += count; | ||
2879 | 1889 | |||
2880 | 1890 | if (line_index < 0) | ||
2881 | 1891 | { | ||
2882 | 1892 | return 0; | ||
2883 | 1893 | } | ||
2884 | 1894 | else if (line_index >= n_lines) | ||
2885 | 1895 | { | ||
2886 | 1896 | return static_cast<int>(text_.length()); | ||
2887 | 1897 | } | ||
2888 | 1898 | |||
2889 | 1899 | int trailing; | ||
2890 | 1900 | #if PANGO_VERSION_CHECK(1,16,0) | ||
2891 | 1901 | PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); | ||
2892 | 1902 | #else | ||
2893 | 1903 | PangoLayoutLine *line = pango_layout_get_line(layout, line_index); | ||
2894 | 1904 | #endif | ||
2895 | 1905 | // Find out the cursor x offset related to the new line position. | ||
2896 | 1906 | if (line->resolved_dir == PANGO_DIRECTION_RTL) | ||
2897 | 1907 | { | ||
2898 | 1908 | pango_layout_get_cursor_pos(layout, line->start_index + line->length, | ||
2899 | 1909 | &rect, NULL); | ||
2900 | 1910 | } | ||
2901 | 1911 | else | ||
2902 | 1912 | { | ||
2903 | 1913 | pango_layout_get_cursor_pos(layout, line->start_index, &rect, NULL); | ||
2904 | 1914 | } | ||
2905 | 1915 | |||
2906 | 1916 | // rect.x is the left edge position of the line in the layout | ||
2907 | 1917 | x_off -= rect.x; | ||
2908 | 1918 | if (x_off < 0) x_off = 0; | ||
2909 | 1919 | pango_layout_line_x_to_index(line, x_off, &index, &trailing); | ||
2910 | 1920 | |||
2911 | 1921 | index = static_cast<int>(g_utf8_offset_to_pointer(text + index, trailing) - text); | ||
2912 | 1922 | return LayoutIndexToTextIndex(index); | ||
2913 | 1923 | } | ||
2914 | 1924 | |||
2915 | 1925 | int TextEntryIM::MovePages(int current_index, int count) | ||
2916 | 1926 | { | ||
2917 | 1927 | nuxAssert(current_index >= 0 && | ||
2918 | 1928 | current_index <= static_cast<int>(text_.length())); | ||
2919 | 1929 | nuxAssert(count); | ||
2920 | 1930 | nuxAssert(preedit_.length() == 0); | ||
2921 | 1931 | |||
2922 | 1932 | // Transfer pages to display lines. | ||
2923 | 1933 | PangoLayout *layout = EnsureLayout(); | ||
2924 | 1934 | int layout_height; | ||
2925 | 1935 | pango_layout_get_pixel_size(layout, NULL, &layout_height); | ||
2926 | 1936 | int n_lines = pango_layout_get_line_count(layout); | ||
2927 | 1937 | int line_height = layout_height / n_lines; | ||
2928 | 1938 | int page_lines = (GetBaseHeight() - kInnerBorderY * 2) / line_height; | ||
2929 | 1939 | return MoveDisplayLines(current_index, count * page_lines); | ||
2930 | 1940 | } | ||
2931 | 1941 | |||
2932 | 1942 | int TextEntryIM::MoveLineEnds(int current_index, int count) | ||
2933 | 1943 | { | ||
2934 | 1944 | nuxAssert(current_index >= 0 && | ||
2935 | 1945 | current_index <= static_cast<int>(text_.length())); | ||
2936 | 1946 | nuxAssert(count); | ||
2937 | 1947 | nuxAssert(preedit_.length() == 0); | ||
2938 | 1948 | |||
2939 | 1949 | PangoLayout *layout = EnsureLayout(); | ||
2940 | 1950 | int index = TextIndexToLayoutIndex(current_index, false); | ||
2941 | 1951 | int line_index = 0; | ||
2942 | 1952 | |||
2943 | 1953 | // Find current line | ||
2944 | 1954 | pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); | ||
2945 | 1955 | |||
2946 | 1956 | // Weird bug: line_index here may be >= than line count? | ||
2947 | 1957 | int line_count = pango_layout_get_line_count(layout); | ||
2948 | 1958 | if (line_index >= line_count) | ||
2949 | 1959 | { | ||
2950 | 1960 | line_index = line_count - 1; | ||
2951 | 1961 | } | ||
2952 | 1962 | |||
2953 | 1963 | // #if PANGO_VERSION_CHECK(1,16,0) | ||
2954 | 1964 | // PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); | ||
2955 | 1965 | // #else | ||
2956 | 1966 | PangoLayoutLine *line = pango_layout_get_line(layout, line_index); | ||
2957 | 1967 | // #endif | ||
2958 | 1968 | |||
2959 | 1969 | if (line->length == 0) | ||
2960 | 1970 | return current_index; | ||
2961 | 1971 | |||
2962 | 1972 | if ((line->resolved_dir == PANGO_DIRECTION_RTL && count < 0) || | ||
2963 | 1973 | (line->resolved_dir != PANGO_DIRECTION_RTL && count > 0)) | ||
2964 | 1974 | { | ||
2965 | 1975 | index = line->start_index + line->length; | ||
2966 | 1976 | } | ||
2967 | 1977 | else | ||
2968 | 1978 | { | ||
2969 | 1979 | index = line->start_index; | ||
2970 | 1980 | } | ||
2971 | 1981 | return LayoutIndexToTextIndex(index); | ||
2972 | 1982 | } | ||
2973 | 1983 | |||
2974 | 1984 | void TextEntryIM::SetCursor(int cursor) | ||
2975 | 1985 | { | ||
2976 | 1986 | if (cursor != cursor_) | ||
2977 | 1987 | { | ||
2978 | 1988 | ResetImContext(); | ||
2979 | 1989 | // If there was a selection range, then the selection range will be cleared. | ||
2980 | 1990 | // Then content_modified_ shall be set to true to force redrawing the text. | ||
2981 | 1991 | if (cursor_ != selection_bound_) | ||
2982 | 1992 | selection_changed_ = true; | ||
2983 | 1993 | cursor_ = cursor; | ||
2984 | 1994 | selection_bound_ = cursor; | ||
2985 | 1995 | cursor_moved_ = true; | ||
2986 | 1996 | |||
2987 | 1997 | cursor_moved.emit(cursor); | ||
2988 | 1998 | } | ||
2989 | 1999 | } | ||
2990 | 2000 | |||
2991 | 2001 | int TextEntryIM::XYToTextIndex(int x, int y) | ||
2992 | 2002 | { | ||
2993 | 2003 | int width, height; | ||
2994 | 2004 | PangoLayout *layout = EnsureLayout(); | ||
2995 | 2005 | const char *text = pango_layout_get_text(layout); | ||
2996 | 2006 | pango_layout_get_pixel_size(layout, &width, &height); | ||
2997 | 2007 | |||
2998 | 2008 | if (y < 0) | ||
2999 | 2009 | { | ||
3000 | 2010 | return 0; | ||
3001 | 2011 | } | ||
3002 | 2012 | else if (y >= height) | ||
3003 | 2013 | { | ||
3004 | 2014 | return static_cast<int>(text_.length()); | ||
3005 | 2015 | } | ||
3006 | 2016 | |||
3007 | 2017 | int trailing; | ||
3008 | 2018 | int index; | ||
3009 | 2019 | pango_layout_xy_to_index(layout, x * PANGO_SCALE, y * PANGO_SCALE, | ||
3010 | 2020 | &index, &trailing); | ||
3011 | 2021 | index = static_cast<int>( | ||
3012 | 2022 | g_utf8_offset_to_pointer(text + index, trailing) - text); | ||
3013 | 2023 | |||
3014 | 2024 | index = LayoutIndexToTextIndex(index); | ||
3015 | 2025 | |||
3016 | 2026 | // Adjust the offset if preedit is not empty and if the offset is after | ||
3017 | 2027 | // current cursor. | ||
3018 | 2028 | int preedit_length = static_cast<int>(preedit_.length()); | ||
3019 | 2029 | if (preedit_length && index > cursor_) | ||
3020 | 2030 | { | ||
3021 | 2031 | if (index >= cursor_ + preedit_length) | ||
3022 | 2032 | index -= preedit_length; | ||
3023 | 2033 | else | ||
3024 | 2034 | index = cursor_; | ||
3025 | 2035 | } | ||
3026 | 2036 | return Clamp(index, 0, static_cast<int>(text_.length())); | ||
3027 | 2037 | } | ||
3028 | 2038 | |||
3029 | 2039 | bool TextEntryIM::GetSelectionBounds(int *start, int *end) | ||
3030 | 2040 | { | ||
3031 | 2041 | if (start) | ||
3032 | 2042 | *start = Min<int>(selection_bound_, cursor_); | ||
3033 | 2043 | if (end) | ||
3034 | 2044 | *end = Max<int>(selection_bound_, cursor_); | ||
3035 | 2045 | |||
3036 | 2046 | return (selection_bound_ != cursor_); | ||
3037 | 2047 | } | ||
3038 | 2048 | |||
3039 | 2049 | void TextEntryIM::SetSelectionBounds(int selection_bound, int cursor) | ||
3040 | 2050 | { | ||
3041 | 2051 | if (selection_bound_ != selection_bound || cursor_ != cursor) | ||
3042 | 2052 | { | ||
3043 | 2053 | selection_changed_ = true; | ||
3044 | 2054 | selection_bound_ = selection_bound; | ||
3045 | 2055 | if (cursor_ != cursor) | ||
3046 | 2056 | { | ||
3047 | 2057 | cursor_ = cursor; | ||
3048 | 2058 | cursor_moved_ = true; | ||
3049 | 2059 | cursor_moved.emit(cursor); | ||
3050 | 2060 | } | ||
3051 | 2061 | |||
3052 | 2062 | //ResetImContext(); | ||
3053 | 2063 | } | ||
3054 | 2064 | } | ||
3055 | 2065 | |||
3056 | 2066 | void TextEntryIM::SetFontFamily(const char *font) | ||
3057 | 2067 | { | ||
3058 | 2068 | font_family_ = font; | ||
3059 | 2069 | QueueRefresh(true, true); | ||
3060 | 2070 | } | ||
3061 | 2071 | |||
3062 | 2072 | void TextEntryIM::SetFontSize(double font_size) | ||
3063 | 2073 | { | ||
3064 | 2074 | font_size_ = font_size; | ||
3065 | 2075 | QueueRefresh(true, true); | ||
3066 | 2076 | } | ||
3067 | 2077 | |||
3068 | 2078 | void TextEntryIM::SetFontOptions(const cairo_font_options_t *options) | ||
3069 | 2079 | { | ||
3070 | 2080 | g_return_if_fail(options); | ||
3071 | 2081 | |||
3072 | 2082 | cairo_font_options_destroy(font_options_); | ||
3073 | 2083 | font_options_ = cairo_font_options_copy(options); | ||
3074 | 2084 | |||
3075 | 2085 | QueueRefresh(true, true); | ||
3076 | 2086 | } | ||
3077 | 2087 | |||
3078 | 2088 | bool TextEntryIM::InspectKeyEvent(unsigned int eventType, | ||
3079 | 2089 | unsigned int key_sym, | ||
3080 | 2090 | const char* character) | ||
3081 | 2091 | { | ||
3082 | 2092 | if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false) && (ime_active_ == false)) | ||
3083 | 2093 | { | ||
3084 | 2094 | if (key_sym == NUX_VK_ENTER || | ||
3085 | 2095 | key_sym == NUX_KP_ENTER || | ||
3086 | 2096 | key_sym == NUX_VK_UP || | ||
3087 | 2097 | key_sym == NUX_VK_DOWN || | ||
3088 | 2098 | key_sym == NUX_VK_LEFT || | ||
3089 | 2099 | key_sym == NUX_VK_RIGHT || | ||
3090 | 2100 | key_sym == NUX_VK_LEFT_TAB || | ||
3091 | 2101 | key_sym == NUX_VK_TAB || | ||
3092 | 2102 | key_sym == NUX_VK_ESCAPE) | ||
3093 | 2103 | { | ||
3094 | 2104 | return false; | ||
3095 | 2105 | } | ||
3096 | 2106 | } | ||
3097 | 2107 | |||
3098 | 2108 | if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true) && (ime_active_ == false)) | ||
3099 | 2109 | { | ||
3100 | 2110 | // Enable to exit the TextEntryIM when in write mode(hack for unity dash) | ||
3101 | 2111 | if (key_sym == NUX_VK_UP || | ||
3102 | 2112 | key_sym == NUX_VK_DOWN || | ||
3103 | 2113 | key_sym == NUX_VK_ESCAPE) | ||
3104 | 2114 | { | ||
3105 | 2115 | return false; | ||
3106 | 2116 | } | ||
3107 | 2117 | } | ||
3108 | 2118 | |||
3109 | 2119 | if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == false) && (text_input_mode_ == false)) | ||
3110 | 2120 | { | ||
3111 | 2121 | return false; | ||
3112 | 2122 | } | ||
3113 | 2123 | |||
3114 | 2124 | return true; | ||
3115 | 2125 | } | ||
3116 | 2126 | } | ||
3117 | 2127 | 0 | ||
3118 | === removed file 'Nux/TextEntryIM.h' | |||
3119 | --- Nux/TextEntryIM.h 2012-02-12 01:55:02 +0000 | |||
3120 | +++ Nux/TextEntryIM.h 1970-01-01 00:00:00 +0000 | |||
3121 | @@ -1,383 +0,0 @@ | |||
3122 | 1 | #ifndef TEXTENTRYIM_H | ||
3123 | 2 | #define TEXTENTRYIM_H | ||
3124 | 3 | |||
3125 | 4 | |||
3126 | 5 | |||
3127 | 6 | // Heavily inspired from google gadget code | ||
3128 | 7 | /* | ||
3129 | 8 | Copyright 2008 Google Inc. | ||
3130 | 9 | |||
3131 | 10 | Licensed under the Apache License, Version 2.0(the "License"); | ||
3132 | 11 | you may not use this file except in compliance with the License. | ||
3133 | 12 | You may obtain a copy of the License at | ||
3134 | 13 | |||
3135 | 14 | http://www.apache.org/licenses/LICENSE-2.0 | ||
3136 | 15 | |||
3137 | 16 | Unless required by applicable law or agreed to in writing, software | ||
3138 | 17 | distributed under the License is distributed on an "AS IS" BASIS, | ||
3139 | 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
3140 | 19 | See the License for the specific language governing permissions and | ||
3141 | 20 | limitations under the License. | ||
3142 | 21 | */ | ||
3143 | 22 | |||
3144 | 23 | |||
3145 | 24 | #include "cairo/cairo.h" | ||
3146 | 25 | #include "pango/pango.h" | ||
3147 | 26 | #include "pango/pangocairo.h" | ||
3148 | 27 | #include "NuxImage/CairoGraphics.h" | ||
3149 | 28 | #include "InputMethodIBus.h" | ||
3150 | 29 | |||
3151 | 30 | namespace nux | ||
3152 | 31 | { | ||
3153 | 32 | class CairoGraphics; | ||
3154 | 33 | class IBusIMEContext; | ||
3155 | 34 | |||
3156 | 35 | class TextEntryIM: public View | ||
3157 | 36 | { | ||
3158 | 37 | NUX_DECLARE_OBJECT_TYPE(TextEntryIM, View); | ||
3159 | 38 | public: | ||
3160 | 39 | TextEntryIM(const char* text, NUX_FILE_LINE_PROTO); | ||
3161 | 40 | ~TextEntryIM(); | ||
3162 | 41 | |||
3163 | 42 | Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type); | ||
3164 | 43 | virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw); | ||
3165 | 44 | virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw); | ||
3166 | 45 | virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw); | ||
3167 | 46 | |||
3168 | 47 | void PreLayoutManagement(); | ||
3169 | 48 | long PostLayoutManagement(long layoutResult); | ||
3170 | 49 | |||
3171 | 50 | // Receivers | ||
3172 | 51 | |||
3173 | 52 | void RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags); | ||
3174 | 53 | void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); | ||
3175 | 54 | void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); | ||
3176 | 55 | void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); | ||
3177 | 56 | void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); | ||
3178 | 57 | void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); | ||
3179 | 58 | void RecvKeyEvent( | ||
3180 | 59 | unsigned long eventType , /*event type*/ | ||
3181 | 60 | unsigned long keysym , /*event keysym*/ | ||
3182 | 61 | unsigned long state , /*event state*/ | ||
3183 | 62 | const char* character , /*character*/ | ||
3184 | 63 | unsigned short keyCount /*key repeat count*/); | ||
3185 | 64 | |||
3186 | 65 | void RecvStartKeyFocus(); | ||
3187 | 66 | void RecvEndKeyFocus(); | ||
3188 | 67 | |||
3189 | 68 | bool _size_match_text; | ||
3190 | 69 | BaseTexture *_texture2D; | ||
3191 | 70 | |||
3192 | 71 | void MainDraw(); | ||
3193 | 72 | void ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); | ||
3194 | 73 | void ProcessKeyEvent ( | ||
3195 | 74 | unsigned long eventType , /*event type*/ | ||
3196 | 75 | unsigned long keysym , /*event keysym*/ | ||
3197 | 76 | unsigned long state , /*event state*/ | ||
3198 | 77 | const char* character , /*character*/ | ||
3199 | 78 | unsigned short keyCount /*key repeat count*/); | ||
3200 | 79 | |||
3201 | 80 | void FocusInx(); | ||
3202 | 81 | void FocusOutx(); | ||
3203 | 82 | |||
3204 | 83 | //! Text changed signal | ||
3205 | 84 | /*! | ||
3206 | 85 | This signal is emitted when the text has changed. | ||
3207 | 86 | */ | ||
3208 | 87 | sigc::signal <void, TextEntryIM*> sigTextChanged; | ||
3209 | 88 | sigc::signal <void> activated; | ||
3210 | 89 | sigc::signal <void, int> cursor_moved; | ||
3211 | 90 | |||
3212 | 91 | void SetText(const char *text); | ||
3213 | 92 | std::string const& GetText() const; | ||
3214 | 93 | |||
3215 | 94 | void SetCompletion(const char *text); // Should use std::string, does not for consistancy | ||
3216 | 95 | std::string const& GetCompletion() const; | ||
3217 | 96 | |||
3218 | 97 | void SetCompletionColor(const Color &color); | ||
3219 | 98 | Color const& GetCompletionColor() const; | ||
3220 | 99 | |||
3221 | 100 | void SetTextColor(const Color &color); | ||
3222 | 101 | Color const& GetTextColor() const; | ||
3223 | 102 | void SetFontFamily(const char *font); | ||
3224 | 103 | void SetFontSize(double font_size); | ||
3225 | 104 | void SetFontOptions(const cairo_font_options_t *options); | ||
3226 | 105 | |||
3227 | 106 | /** Select text between start and end. */ | ||
3228 | 107 | void Select(int start, int end); | ||
3229 | 108 | /** Select all text */ | ||
3230 | 109 | void SelectAll(); | ||
3231 | 110 | |||
3232 | 111 | CairoGraphics::Alignment GetAlign() const; | ||
3233 | 112 | void SetAlign(CairoGraphics::Alignment align); | ||
3234 | 113 | |||
3235 | 114 | bool im_active(); | ||
3236 | 115 | |||
3237 | 116 | protected: | ||
3238 | 117 | bool _block_focus; // used to selectively ignore focus keyevents | ||
3239 | 118 | |||
3240 | 119 | virtual void GeometryChanged(); | ||
3241 | 120 | |||
3242 | 121 | /** | ||
3243 | 122 | * Enum used to specify different motion types. | ||
3244 | 123 | */ | ||
3245 | 124 | enum MovementStep { | ||
3246 | 125 | VISUALLY, | ||
3247 | 126 | WORDS, | ||
3248 | 127 | DISPLAY_LINES, | ||
3249 | 128 | DISPLAY_LINE_ENDS, | ||
3250 | 129 | PAGES, | ||
3251 | 130 | BUFFER | ||
3252 | 131 | }; | ||
3253 | 132 | |||
3254 | 133 | void QueueTextDraw(); | ||
3255 | 134 | /** Remove the cached layout. */ | ||
3256 | 135 | void ResetLayout(); | ||
3257 | 136 | /** | ||
3258 | 137 | * Create pango layout on-demand. If the layout is not changed, return the | ||
3259 | 138 | * cached one. | ||
3260 | 139 | */ | ||
3261 | 140 | PangoLayout* EnsureLayout(); | ||
3262 | 141 | /** Create a new layout containning current edit content */ | ||
3263 | 142 | PangoLayout* CreateLayout(); | ||
3264 | 143 | /** Create cairo canvas on-demand. */ | ||
3265 | 144 | CairoGraphics* EnsureCanvas(); | ||
3266 | 145 | /** Adjust the scroll information */ | ||
3267 | 146 | void AdjustScroll(); | ||
3268 | 147 | /** | ||
3269 | 148 | * Send out a request to refresh all informations of the edit control | ||
3270 | 149 | * and queue a draw request. | ||
3271 | 150 | * If @c relayout is true then the layout will be regenerated. | ||
3272 | 151 | * */ | ||
3273 | 152 | void QueueRefresh(bool relayout, bool adjust_scroll); | ||
3274 | 153 | /** Reset the input method context */ | ||
3275 | 154 | void ResetImContext(); | ||
3276 | 155 | /** Reset preedit text */ | ||
3277 | 156 | void ResetPreedit(); | ||
3278 | 157 | /** Send out a request to blink the cursor if necessary */ | ||
3279 | 158 | void QueueCursorBlink(); | ||
3280 | 159 | static bool CursorBlinkCallback(TextEntryIM *data); | ||
3281 | 160 | |||
3282 | 161 | void ShowCursor(); | ||
3283 | 162 | void HideCursor(); | ||
3284 | 163 | |||
3285 | 164 | /** Draw the Cursor to the canvas */ | ||
3286 | 165 | void DrawCursor(CairoGraphics *canvas); | ||
3287 | 166 | /** Draw the text to the canvas */ | ||
3288 | 167 | void DrawText(CairoGraphics *canvas); | ||
3289 | 168 | |||
3290 | 169 | void GetCursorRects(Rect *strong, Rect *weak); | ||
3291 | 170 | |||
3292 | 171 | void UpdateCursorRegion(); | ||
3293 | 172 | |||
3294 | 173 | void UpdateSelectionRegion(); | ||
3295 | 174 | |||
3296 | 175 | /** Move cursor */ | ||
3297 | 176 | void MoveCursor(MovementStep step, int count, bool extend_selection); | ||
3298 | 177 | /** Move cursor visually, meaning left or right */ | ||
3299 | 178 | int MoveVisually(int current_pos, int count); | ||
3300 | 179 | /** Move cursor in words */ | ||
3301 | 180 | int MoveWords(int current_pos, int count); | ||
3302 | 181 | /** Move cursor in display lines */ | ||
3303 | 182 | int MoveDisplayLines(int current_pos, int count); | ||
3304 | 183 | /** Move cursor in pages */ | ||
3305 | 184 | int MovePages(int current_pos, int count); | ||
3306 | 185 | /** Move cursor to the beginning or end of a display line */ | ||
3307 | 186 | int MoveLineEnds(int current_pos, int count); | ||
3308 | 187 | |||
3309 | 188 | /** Set the current cursor offset, in number of characters. */ | ||
3310 | 189 | void SetCursor(int cursor); | ||
3311 | 190 | /** Get the most reasonable character offset according to the pixel | ||
3312 | 191 | * coordinate in the layout */ | ||
3313 | 192 | int XYToTextIndex(int x, int y); | ||
3314 | 193 | /** Get the offset range that is currently selected,in number of characters.*/ | ||
3315 | 194 | bool GetSelectionBounds(int *start, int *end); | ||
3316 | 195 | /** Set the offest range that should be selected, in number of characters. */ | ||
3317 | 196 | void SetSelectionBounds(int selection_bound, int cursor); | ||
3318 | 197 | |||
3319 | 198 | /** Convert index in text_ into index in layout text. */ | ||
3320 | 199 | int TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor); | ||
3321 | 200 | |||
3322 | 201 | /** Convert index in layout text into index in text_. */ | ||
3323 | 202 | int LayoutIndexToTextIndex(int layout_index); | ||
3324 | 203 | |||
3325 | 204 | /** Get char length at index, in number of bytes. */ | ||
3326 | 205 | int GetCharLength(int index); | ||
3327 | 206 | |||
3328 | 207 | /** Get previous char length before index, in number of bytes. */ | ||
3329 | 208 | int GetPrevCharLength(int index); | ||
3330 | 209 | |||
3331 | 210 | /** Insert text at current caret position */ | ||
3332 | 211 | void EnterText(const char *str); | ||
3333 | 212 | /** Delete text in a specified range, in number of characters. */ | ||
3334 | 213 | void DeleteText(int start, int end); | ||
3335 | 214 | |||
3336 | 215 | /** Select the current word under cursor */ | ||
3337 | 216 | void SelectWord(); | ||
3338 | 217 | /** Select the current display line under cursor */ | ||
3339 | 218 | void SelectLine(); | ||
3340 | 219 | /** Delete the text that is currently selected */ | ||
3341 | 220 | void DeleteSelection(); | ||
3342 | 221 | |||
3343 | 222 | /** Cut the current selected text to the clipboard */ | ||
3344 | 223 | void CutClipboard(); | ||
3345 | 224 | /** Copy the current selected text to the clipboard */ | ||
3346 | 225 | void CopyClipboard(); | ||
3347 | 226 | /** Paste the text in the clipboard to current offset */ | ||
3348 | 227 | void PasteClipboard(); | ||
3349 | 228 | /** Delete a character before the offset of the cursor */ | ||
3350 | 229 | void BackSpace(MovementStep step); | ||
3351 | 230 | /** Delete a character at the offset of the cursor */ | ||
3352 | 231 | void Delete(MovementStep step); | ||
3353 | 232 | /** Switch between the overwrite mode and the insert mode*/ | ||
3354 | 233 | void ToggleOverwrite(); | ||
3355 | 234 | |||
3356 | 235 | /** Gets the color of selection background. */ | ||
3357 | 236 | Color GetSelectionBackgroundColor(); | ||
3358 | 237 | /** Gets the color of selection text. */ | ||
3359 | 238 | Color GetSelectionTextColor(); | ||
3360 | 239 | |||
3361 | 240 | /** | ||
3362 | 241 | * Gets the cursor location in pango layout. The unit is pixel. | ||
3363 | 242 | */ | ||
3364 | 243 | void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height, | ||
3365 | 244 | int *weak_x, int *weak_y, int *weak_height); | ||
3366 | 245 | |||
3367 | 246 | /** The CairoCanvas which hold cairo_t inside */ | ||
3368 | 247 | CairoGraphics* canvas_; | ||
3369 | 248 | |||
3370 | 249 | /** The cached Pango Layout */ | ||
3371 | 250 | PangoLayout* cached_layout_; | ||
3372 | 251 | |||
3373 | 252 | /** The text content of the edit control */ | ||
3374 | 253 | std::string text_; | ||
3375 | 254 | /** The preedit text of the edit control */ | ||
3376 | 255 | std::string preedit_; | ||
3377 | 256 | /** Attribute list of the preedit text */ | ||
3378 | 257 | PangoAttrList *preedit_attrs_; | ||
3379 | 258 | /** | ||
3380 | 259 | * The character that should be displayed in invisible mode. | ||
3381 | 260 | * If this is empty, then the edit control is visible | ||
3382 | 261 | */ | ||
3383 | 262 | std::string password_char_; | ||
3384 | 263 | |||
3385 | 264 | /** The completion string */ | ||
3386 | 265 | std::string completion_; | ||
3387 | 266 | |||
3388 | 267 | /** The completion colour */ | ||
3389 | 268 | Color completion_color_; | ||
3390 | 269 | |||
3391 | 270 | /** Last time of mouse double click event. */ | ||
3392 | 271 | unsigned long long last_dblclick_time_; | ||
3393 | 272 | |||
3394 | 273 | /** The current cursor position in number of bytes. */ | ||
3395 | 274 | int cursor_; | ||
3396 | 275 | /** | ||
3397 | 276 | * The preedit cursor position within the preedit string, | ||
3398 | 277 | * in number of bytes. | ||
3399 | 278 | */ | ||
3400 | 279 | int preedit_cursor_; | ||
3401 | 280 | /** | ||
3402 | 281 | * The current selection bound in number of bytes, | ||
3403 | 282 | * range between cursor_ and selection_bound_ are selected. | ||
3404 | 283 | */ | ||
3405 | 284 | int selection_bound_; | ||
3406 | 285 | |||
3407 | 286 | /** X offset of current scroll, in pixels */ | ||
3408 | 287 | int scroll_offset_x_; | ||
3409 | 288 | /** Y offset of current scroll, in pixels */ | ||
3410 | 289 | int scroll_offset_y_; | ||
3411 | 290 | /** Timer id of cursor blink callback */ | ||
3412 | 291 | int cursor_blink_timer_; | ||
3413 | 292 | /** | ||
3414 | 293 | * Indicates the status of cursor blinking, | ||
3415 | 294 | * 0 means hide cursor | ||
3416 | 295 | * otherwise means show cursor. | ||
3417 | 296 | * The maximum value would be 2, and decrased by one in each cursor blink | ||
3418 | 297 | * callback, then there would be 2/3 visible time and 1/3 invisible time. | ||
3419 | 298 | */ | ||
3420 | 299 | int cursor_blink_status_; | ||
3421 | 300 | |||
3422 | 301 | /** Whether the text is visible, decided by password_char_ */ | ||
3423 | 302 | bool visible_; | ||
3424 | 303 | /** Whether the edit control is focused */ | ||
3425 | 304 | bool focused_; | ||
3426 | 305 | /** Whether the input method should be reset */ | ||
3427 | 306 | bool need_im_reset_; | ||
3428 | 307 | /** Whether the keyboard in overwrite mode */ | ||
3429 | 308 | bool overwrite_; | ||
3430 | 309 | /** Whether the button click should select words */ | ||
3431 | 310 | bool select_words_; | ||
3432 | 311 | /** Whether the button click should select lines */ | ||
3433 | 312 | bool select_lines_; | ||
3434 | 313 | /** Whether the left button is pressed */ | ||
3435 | 314 | bool button_; | ||
3436 | 315 | /** Whether the text should be bold */ | ||
3437 | 316 | bool bold_; | ||
3438 | 317 | /** Whether the text should be underlined */ | ||
3439 | 318 | bool underline_; | ||
3440 | 319 | /** Whether the text should be struck-out */ | ||
3441 | 320 | bool strikeout_; | ||
3442 | 321 | /** Whether the text should be italic */ | ||
3443 | 322 | bool italic_; | ||
3444 | 323 | /** Whether the text could be shown in multilines */ | ||
3445 | 324 | bool multiline_; | ||
3446 | 325 | /** Whether the text should be wrapped */ | ||
3447 | 326 | bool wrap_; | ||
3448 | 327 | /** whether the cursor should be displayed */ | ||
3449 | 328 | bool cursor_visible_; | ||
3450 | 329 | /** whether the edit control is readonly */ | ||
3451 | 330 | bool readonly_; | ||
3452 | 331 | /** | ||
3453 | 332 | * Indicates if the content of the edit control has been modified | ||
3454 | 333 | * since last draw | ||
3455 | 334 | */ | ||
3456 | 335 | bool content_modified_; | ||
3457 | 336 | |||
3458 | 337 | /** Indicates if the selection region has been changed since last draw. */ | ||
3459 | 338 | bool selection_changed_; | ||
3460 | 339 | |||
3461 | 340 | /** Indicates if the cursor position has been moved since last draw. */ | ||
3462 | 341 | bool cursor_moved_; | ||
3463 | 342 | |||
3464 | 343 | /** Indicates if the canvas cache needs updating. */ | ||
3465 | 344 | bool update_canvas_; | ||
3466 | 345 | |||
3467 | 346 | /** The font family of the text */ | ||
3468 | 347 | std::string font_family_; | ||
3469 | 348 | /** The font size of the text */ | ||
3470 | 349 | double font_size_; | ||
3471 | 350 | |||
3472 | 351 | cairo_font_options_t *font_options_; | ||
3473 | 352 | double font_dpi_; | ||
3474 | 353 | |||
3475 | 354 | /** The text color of the edit control */ | ||
3476 | 355 | Color _text_color; | ||
3477 | 356 | |||
3478 | 357 | CairoGraphics::Alignment align_; | ||
3479 | 358 | |||
3480 | 359 | #if defined(NUX_OS_LINUX) | ||
3481 | 360 | Cursor caret_cursor_; | ||
3482 | 361 | #endif | ||
3483 | 362 | |||
3484 | 363 | std::list<Rect> last_selection_region_; | ||
3485 | 364 | std::list<Rect> selection_region_; | ||
3486 | 365 | std::list<Rect> last_cursor_region_; | ||
3487 | 366 | std::list<Rect> cursor_region_; | ||
3488 | 367 | |||
3489 | 368 | IBusIMEContext* ime_; | ||
3490 | 369 | bool ime_active_; | ||
3491 | 370 | |||
3492 | 371 | friend class IBusIMEContext; | ||
3493 | 372 | |||
3494 | 373 | protected: | ||
3495 | 374 | bool text_input_mode_; | ||
3496 | 375 | bool key_nav_mode_; | ||
3497 | 376 | |||
3498 | 377 | virtual bool InspectKeyEvent(unsigned int eventType, | ||
3499 | 378 | unsigned int keysym, | ||
3500 | 379 | const char* character); | ||
3501 | 380 | }; | ||
3502 | 381 | } | ||
3503 | 382 | |||
3504 | 383 | #endif // TEXTENTRY_H | ||
3505 | 384 | 0 | ||
3506 | === modified file 'configure.ac' | |||
3507 | --- configure.ac 2012-02-21 06:39:45 +0000 | |||
3508 | +++ configure.ac 2012-02-23 05:24:18 +0000 | |||
3509 | @@ -22,7 +22,7 @@ | |||
3510 | 22 | # The number format is : year/month/day | 22 | # The number format is : year/month/day |
3511 | 23 | # e.g.: december 5th, 2011 is: 20111205 | 23 | # e.g.: december 5th, 2011 is: 20111205 |
3512 | 24 | # To make more than one API change in a day, add a number to the date. Like 20111205.xx | 24 | # To make more than one API change in a day, add a number to the date. Like 20111205.xx |
3514 | 25 | m4_define([nux_abi_version], [20120221.01]) | 25 | m4_define([nux_abi_version], [20120223.01]) |
3515 | 26 | 26 | ||
3516 | 27 | m4_define([nux_version], | 27 | m4_define([nux_version], |
3517 | 28 | [nux_major_version.nux_minor_version.nux_micro_version]) | 28 | [nux_major_version.nux_minor_version.nux_micro_version]) |
3518 | @@ -186,6 +186,16 @@ | |||
3519 | 186 | AC_SUBST(UNITY_SUPPORT_TEST_CFLAGS) | 186 | AC_SUBST(UNITY_SUPPORT_TEST_CFLAGS) |
3520 | 187 | AC_SUBST(UNITY_SUPPORT_TEST_LIBS) | 187 | AC_SUBST(UNITY_SUPPORT_TEST_LIBS) |
3521 | 188 | 188 | ||
3522 | 189 | PKG_CHECK_MODULES(IBUS, | ||
3523 | 190 | glib-2.0 >= 2.25.14 | ||
3524 | 191 | gio-2.0 | ||
3525 | 192 | ibus-1.0 | ||
3526 | 193 | gobject-2.0 | ||
3527 | 194 | ) | ||
3528 | 195 | |||
3529 | 196 | AC_SUBST(IBUS_CFLAGS) | ||
3530 | 197 | AC_SUBST(IBUS_LIBS) | ||
3531 | 198 | |||
3532 | 189 | dnl ************************************ | 199 | dnl ************************************ |
3533 | 190 | dnl Enable/disable tests | 200 | dnl Enable/disable tests |
3534 | 191 | dnl ************************************ | 201 | dnl ************************************ |
3535 | 192 | 202 | ||
3536 | === modified file 'examples/Makefile.am' | |||
3537 | --- examples/Makefile.am 2012-02-12 22:25:19 +0000 | |||
3538 | +++ examples/Makefile.am 2012-02-23 05:24:18 +0000 | |||
3539 | @@ -46,6 +46,7 @@ | |||
3540 | 46 | $(NUX_CORE_CFLAGS) \ | 46 | $(NUX_CORE_CFLAGS) \ |
3541 | 47 | $(NUX_EXAMPLES_CFLAGS) \ | 47 | $(NUX_EXAMPLES_CFLAGS) \ |
3542 | 48 | $(NUX_CFLAGS) \ | 48 | $(NUX_CFLAGS) \ |
3543 | 49 | $(IBUS_CFLAGS) \ | ||
3544 | 49 | $(MAINTAINER_CFLAGS) | 50 | $(MAINTAINER_CFLAGS) |
3545 | 50 | 51 | ||
3546 | 51 | ALL_LIBS = \ | 52 | ALL_LIBS = \ |
3547 | @@ -54,7 +55,8 @@ | |||
3548 | 54 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ | 55 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ |
3549 | 55 | $(top_builddir)/Nux/libnux-@NUX_API_VERSION@.la \ | 56 | $(top_builddir)/Nux/libnux-@NUX_API_VERSION@.la \ |
3550 | 56 | $(NUX_EXAMPLES_LIBS) \ | 57 | $(NUX_EXAMPLES_LIBS) \ |
3552 | 57 | $(NUX_LIBS) | 58 | $(NUX_LIBS) \ |
3553 | 59 | $(IBUS_LIBS) | ||
3554 | 58 | 60 | ||
3555 | 59 | # This is the individual executable build. For every $exe in noinst_PROGRAMS | 61 | # This is the individual executable build. For every $exe in noinst_PROGRAMS |
3556 | 60 | # you need a $exe_SOURCES and $exe_LDADD so it builds | 62 | # you need a $exe_SOURCES and $exe_LDADD so it builds |
3557 | 61 | 63 | ||
3558 | === modified file 'tests/Makefile.am' | |||
3559 | --- tests/Makefile.am 2012-02-19 07:04:11 +0000 | |||
3560 | +++ tests/Makefile.am 2012-02-23 05:24:18 +0000 | |||
3561 | @@ -13,8 +13,9 @@ | |||
3562 | 13 | xtest-vlayout-key-navigation \ | 13 | xtest-vlayout-key-navigation \ |
3563 | 14 | xtest-scrollbar \ | 14 | xtest-scrollbar \ |
3564 | 15 | xtest-focus-on-mouse-down \ | 15 | xtest-focus-on-mouse-down \ |
3567 | 16 | xtest-focus-on-mouse-enter \ | 16 | xtest-keynav-directions \ |
3568 | 17 | xtest-keynav-directions | 17 | xtest-text-entry \ |
3569 | 18 | xtest-focus-on-mouse-enter | ||
3570 | 18 | 19 | ||
3571 | 19 | # Please keep alphabetical | 20 | # Please keep alphabetical |
3572 | 20 | test_nux_SOURCES = \ | 21 | test_nux_SOURCES = \ |
3573 | @@ -100,7 +101,8 @@ | |||
3574 | 100 | $(NUX_CORE_CFLAGS) \ | 101 | $(NUX_CORE_CFLAGS) \ |
3575 | 101 | $(NUX_EXAMPLES_CFLAGS) \ | 102 | $(NUX_EXAMPLES_CFLAGS) \ |
3576 | 102 | $(NUX_CFLAGS) \ | 103 | $(NUX_CFLAGS) \ |
3578 | 103 | $(MAINTAINER_CFLAGS) | 104 | $(MAINTAINER_CFLAGS) \ |
3579 | 105 | $(IBUS_CFLAGS) | ||
3580 | 104 | 106 | ||
3581 | 105 | gtest_nux_LDADD = \ | 107 | gtest_nux_LDADD = \ |
3582 | 106 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ | 108 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
3583 | @@ -124,7 +126,8 @@ | |||
3584 | 124 | $(NUX_CORE_CFLAGS) \ | 126 | $(NUX_CORE_CFLAGS) \ |
3585 | 125 | $(NUX_EXAMPLES_CFLAGS) \ | 127 | $(NUX_EXAMPLES_CFLAGS) \ |
3586 | 126 | $(NUX_CFLAGS) \ | 128 | $(NUX_CFLAGS) \ |
3588 | 127 | $(MAINTAINER_CFLAGS) | 129 | $(MAINTAINER_CFLAGS) \ |
3589 | 130 | $(IBUS_CFLAGS) | ||
3590 | 128 | 131 | ||
3591 | 129 | TestLibs = $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ | 132 | TestLibs = $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
3592 | 130 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ | 133 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ |
3593 | @@ -227,6 +230,14 @@ | |||
3594 | 227 | xtest_keynav_directions_LDADD = $(TestLibs) | 230 | xtest_keynav_directions_LDADD = $(TestLibs) |
3595 | 228 | xtest_keynav_directions_LDFLAGS = -lpthread -lXtst | 231 | xtest_keynav_directions_LDFLAGS = -lpthread -lXtst |
3596 | 229 | 232 | ||
3597 | 233 | xtest_text_entry_SOURCES = xtest-text-entry.cpp \ | ||
3598 | 234 | nux_automated_test_framework.cpp \ | ||
3599 | 235 | nux_automated_test_framework.h | ||
3600 | 236 | |||
3601 | 237 | xtest_text_entry_CPPFLAGS = $(TestFlags) | ||
3602 | 238 | xtest_text_entry_LDADD = $(TestLibs) | ||
3603 | 239 | xtest_text_entry_LDFLAGS = -lpthread -lXtst | ||
3604 | 240 | |||
3605 | 230 | 241 | ||
3606 | 231 | #run make test as part of make check | 242 | #run make test as part of make check |
3607 | 232 | check-local: test gtest test-apps | 243 | check-local: test gtest test-apps |
3608 | 233 | 244 | ||
3609 | === modified file 'tests/Readme.txt' | |||
3610 | --- tests/Readme.txt 2012-02-17 13:43:55 +0000 | |||
3611 | +++ tests/Readme.txt 2012-02-23 05:24:18 +0000 | |||
3612 | @@ -53,3 +53,6 @@ | |||
3613 | 53 | 53 | ||
3614 | 54 | xtest-keynav-direction | 54 | xtest-keynav-direction |
3615 | 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. |
3616 | 56 | |||
3617 | 57 | xtest-text-entry | ||
3618 | 58 | Simulate various operations on the text entry | ||
3619 | 56 | 59 | ||
3620 | === modified file 'tests/nux_automated_test_framework.cpp' | |||
3621 | --- tests/nux_automated_test_framework.cpp 2011-12-06 22:44:57 +0000 | |||
3622 | +++ tests/nux_automated_test_framework.cpp 2012-02-23 05:24:18 +0000 | |||
3623 | @@ -259,7 +259,16 @@ | |||
3624 | 259 | } | 259 | } |
3625 | 260 | 260 | ||
3626 | 261 | std::string s(1, c); | 261 | std::string s(1, c); |
3628 | 262 | SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); | 262 | |
3629 | 263 | if (c == ' ') | ||
3630 | 264 | { | ||
3631 | 265 | SendFakeKeyEvent(XK_space, modifier); | ||
3632 | 266 | } | ||
3633 | 267 | else | ||
3634 | 268 | { | ||
3635 | 269 | SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); | ||
3636 | 270 | } | ||
3637 | 271 | |||
3638 | 263 | nux::SleepForMilliseconds(300); | 272 | nux::SleepForMilliseconds(300); |
3639 | 264 | } | 273 | } |
3640 | 265 | 274 | ||
3641 | @@ -282,7 +291,15 @@ | |||
3642 | 282 | } | 291 | } |
3643 | 283 | 292 | ||
3644 | 284 | std::string s(1, c); | 293 | std::string s(1, c); |
3646 | 285 | SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); | 294 | |
3647 | 295 | if (c == ' ') | ||
3648 | 296 | { | ||
3649 | 297 | SendFakeKeyEvent(XK_space, modifier); | ||
3650 | 298 | } | ||
3651 | 299 | else | ||
3652 | 300 | { | ||
3653 | 301 | SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); | ||
3654 | 302 | } | ||
3655 | 286 | nux::SleepForMilliseconds(300); | 303 | nux::SleepForMilliseconds(300); |
3656 | 287 | } | 304 | } |
3657 | 288 | } | 305 | } |
3658 | @@ -367,6 +384,44 @@ | |||
3659 | 367 | SendFakeKeyEvent(XK_Return, 0); | 384 | SendFakeKeyEvent(XK_Return, 0); |
3660 | 368 | } | 385 | } |
3661 | 369 | 386 | ||
3662 | 387 | void NuxAutomatedTestFramework::ViewSendRight() | ||
3663 | 388 | { | ||
3664 | 389 | SendFakeKeyEvent(XK_Right, 0); | ||
3665 | 390 | } | ||
3666 | 391 | |||
3667 | 392 | void NuxAutomatedTestFramework::ViewSendLeft() | ||
3668 | 393 | { | ||
3669 | 394 | SendFakeKeyEvent(XK_Left, 0); | ||
3670 | 395 | } | ||
3671 | 396 | |||
3672 | 397 | void NuxAutomatedTestFramework::ViewSendUp() | ||
3673 | 398 | { | ||
3674 | 399 | SendFakeKeyEvent(XK_Up, 0); | ||
3675 | 400 | } | ||
3676 | 401 | |||
3677 | 402 | void NuxAutomatedTestFramework::ViewSendDown() | ||
3678 | 403 | { | ||
3679 | 404 | SendFakeKeyEvent(XK_Down, 0); | ||
3680 | 405 | } | ||
3681 | 406 | |||
3682 | 407 | void NuxAutomatedTestFramework::ViewSendIBusToggle() | ||
3683 | 408 | { | ||
3684 | 409 | KeyCode modcode0 = 0; | ||
3685 | 410 | KeyCode modcode1 = 0; | ||
3686 | 411 | |||
3687 | 412 | modcode0 = XKeysymToKeycode(display_, XK_Control_L); | ||
3688 | 413 | XTestFakeKeyEvent(display_, modcode0, True, 0); | ||
3689 | 414 | |||
3690 | 415 | modcode1 = XKeysymToKeycode(display_, XK_space); | ||
3691 | 416 | XTestFakeKeyEvent(display_, modcode1, True, 0); | ||
3692 | 417 | |||
3693 | 418 | // release | ||
3694 | 419 | /* Generate modkey release */ | ||
3695 | 420 | XTestFakeKeyEvent(display_, modcode1, False, 0); | ||
3696 | 421 | XTestFakeKeyEvent(display_, modcode0, False, 0); | ||
3697 | 422 | |||
3698 | 423 | } | ||
3699 | 424 | |||
3700 | 370 | void NuxAutomatedTestFramework::PutMouseAt(int x, int y) | 425 | void NuxAutomatedTestFramework::PutMouseAt(int x, int y) |
3701 | 371 | { | 426 | { |
3702 | 372 | XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); | 427 | XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); |
3703 | @@ -458,4 +513,4 @@ | |||
3704 | 458 | { | 513 | { |
3705 | 459 | nuxAlertMsg("%s: %s", msg, "Failed"); | 514 | nuxAlertMsg("%s: %s", msg, "Failed"); |
3706 | 460 | } | 515 | } |
3707 | 461 | } | ||
3708 | 462 | \ No newline at end of file | 516 | \ No newline at end of file |
3709 | 517 | } | ||
3710 | 463 | 518 | ||
3711 | === modified file 'tests/nux_automated_test_framework.h' | |||
3712 | --- tests/nux_automated_test_framework.h 2011-11-23 04:42:44 +0000 | |||
3713 | +++ tests/nux_automated_test_framework.h 2012-02-23 05:24:18 +0000 | |||
3714 | @@ -80,6 +80,16 @@ | |||
3715 | 80 | void ViewSendTab(); | 80 | void ViewSendTab(); |
3716 | 81 | //! Simulate Return key. | 81 | //! Simulate Return key. |
3717 | 82 | void ViewSendReturn(); | 82 | void ViewSendReturn(); |
3718 | 83 | //! Simulate Left arrow key. | ||
3719 | 84 | void ViewSendLeft(); | ||
3720 | 85 | //! Simulate Right arrow key. | ||
3721 | 86 | void ViewSendRight(); | ||
3722 | 87 | //! Simulate Up arrow key. | ||
3723 | 88 | void ViewSendUp(); | ||
3724 | 89 | //! Simulate Down arrow key. | ||
3725 | 90 | void ViewSendDown(); | ||
3726 | 91 | |||
3727 | 92 | void ViewSendIBusToggle(); | ||
3728 | 83 | 93 | ||
3729 | 84 | //! Put the mouse pointer anywhere on the display. | 94 | //! Put the mouse pointer anywhere on the display. |
3730 | 85 | void PutMouseAt(int x, int y); | 95 | void PutMouseAt(int x, int y); |
3731 | 86 | 96 | ||
3732 | === added file 'tests/xtest-text-entry.cpp' | |||
3733 | --- tests/xtest-text-entry.cpp 1970-01-01 00:00:00 +0000 | |||
3734 | +++ tests/xtest-text-entry.cpp 2012-02-23 05:24:18 +0000 | |||
3735 | @@ -0,0 +1,391 @@ | |||
3736 | 1 | /* | ||
3737 | 2 | * Copyright 2010 Inalogic Inc. | ||
3738 | 3 | * | ||
3739 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3740 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
3741 | 6 | * by the Free Software Foundation. | ||
3742 | 7 | * | ||
3743 | 8 | * This program is distributed in the hope that it will be useful, but | ||
3744 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3745 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR | ||
3746 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
3747 | 12 | * | ||
3748 | 13 | * You should have received a copy of the GNU General Public License | ||
3749 | 14 | * version 3 along with this program. If not, see | ||
3750 | 15 | * <http://www.gnu.org/licenses/> | ||
3751 | 16 | * | ||
3752 | 17 | * Authored by: Jay Taoko <jaytaoko@inalogic.com> | ||
3753 | 18 | * | ||
3754 | 19 | */ | ||
3755 | 20 | |||
3756 | 21 | #include "Nux/Nux.h" | ||
3757 | 22 | #include "Nux/WindowThread.h" | ||
3758 | 23 | #include "Nux/VLayout.h" | ||
3759 | 24 | #include "Nux/TextEntry.h" | ||
3760 | 25 | #include "Nux/ProgramFramework/ProgramTemplate.h" | ||
3761 | 26 | #include "Nux/ProgramFramework/TestView.h" | ||
3762 | 27 | #include <X11/extensions/XTest.h> | ||
3763 | 28 | #include <X11/keysym.h> | ||
3764 | 29 | #include "nux_automated_test_framework.h" | ||
3765 | 30 | |||
3766 | 31 | class TextTextEntry: public ProgramTemplate | ||
3767 | 32 | { | ||
3768 | 33 | public: | ||
3769 | 34 | TextTextEntry(const char* program_name, int window_width, int window_height, int program_life_span); | ||
3770 | 35 | ~TextTextEntry(); | ||
3771 | 36 | |||
3772 | 37 | virtual void UserInterfaceSetup(); | ||
3773 | 38 | |||
3774 | 39 | void TextEntryClick(nux::TextEntry* text_entry); | ||
3775 | 40 | void ResetEvents(); | ||
3776 | 41 | nux::TextEntry* text_entry_; | ||
3777 | 42 | |||
3778 | 43 | bool clicked_; | ||
3779 | 44 | }; | ||
3780 | 45 | |||
3781 | 46 | TextTextEntry::TextTextEntry(const char* program_name, | ||
3782 | 47 | int window_width, | ||
3783 | 48 | int window_height, | ||
3784 | 49 | int program_life_span) | ||
3785 | 50 | : ProgramTemplate(program_name, window_width, window_height, program_life_span) | ||
3786 | 51 | { | ||
3787 | 52 | ResetEvents(); | ||
3788 | 53 | text_entry_ = NULL; | ||
3789 | 54 | } | ||
3790 | 55 | |||
3791 | 56 | TextTextEntry::~TextTextEntry() | ||
3792 | 57 | { | ||
3793 | 58 | |||
3794 | 59 | } | ||
3795 | 60 | |||
3796 | 61 | void TextTextEntry::ResetEvents() | ||
3797 | 62 | { | ||
3798 | 63 | clicked_ = false; | ||
3799 | 64 | } | ||
3800 | 65 | |||
3801 | 66 | void TextTextEntry::TextEntryClick(nux::TextEntry* text_entry) | ||
3802 | 67 | { | ||
3803 | 68 | if (text_entry_ == text_entry) | ||
3804 | 69 | { | ||
3805 | 70 | clicked_ = true; | ||
3806 | 71 | } | ||
3807 | 72 | } | ||
3808 | 73 | |||
3809 | 74 | void TextTextEntry::UserInterfaceSetup() | ||
3810 | 75 | { | ||
3811 | 76 | nux::VLayout* main_layout = new nux::VLayout(NUX_TRACKER_LOCATION); | ||
3812 | 77 | text_entry_ = new nux::TextEntry("", NUX_TRACKER_LOCATION); | ||
3813 | 78 | text_entry_->SetFontSize(76); | ||
3814 | 79 | |||
3815 | 80 | main_layout->AddView(text_entry_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); | ||
3816 | 81 | main_layout->SetSpaceBetweenChildren(10); | ||
3817 | 82 | main_layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER); | ||
3818 | 83 | |||
3819 | 84 | static_cast<nux::WindowThread*>(window_thread_)->SetLayout(main_layout); | ||
3820 | 85 | |||
3821 | 86 | nux::ColorLayer background(nux::Color(0xFF4D4D4D)); | ||
3822 | 87 | static_cast<nux::WindowThread*>(window_thread_)->SetWindowBackgroundPaintLayer(&background); | ||
3823 | 88 | } | ||
3824 | 89 | |||
3825 | 90 | bool SetEngineActive (IBusBus* bus_, std::string engine) | ||
3826 | 91 | { | ||
3827 | 92 | GList* engines = ibus_bus_list_active_engines(bus_); | ||
3828 | 93 | GList* start = engines; | ||
3829 | 94 | |||
3830 | 95 | bool found = false; | ||
3831 | 96 | gboolean global_flag = ibus_bus_get_use_global_engine(bus_); | ||
3832 | 97 | |||
3833 | 98 | // Iterates through the active engines | ||
3834 | 99 | do | ||
3835 | 100 | { | ||
3836 | 101 | IBusEngineDesc *engine_desc = IBUS_ENGINE_DESC (engines->data); | ||
3837 | 102 | |||
3838 | 103 | // Found Engine, make it active! | ||
3839 | 104 | if (g_strcmp0(ibus_engine_desc_get_name(engine_desc), engine.c_str()) == 0) | ||
3840 | 105 | { | ||
3841 | 106 | found = true; | ||
3842 | 107 | |||
3843 | 108 | // Set ibus to use global engines | ||
3844 | 109 | if (!global_flag) | ||
3845 | 110 | ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(true)); | ||
3846 | 111 | |||
3847 | 112 | // Set and activate the engine | ||
3848 | 113 | ibus_bus_set_global_engine(bus_,engine.c_str()); | ||
3849 | 114 | } | ||
3850 | 115 | } while ((engines = g_list_next(engines)) != NULL); | ||
3851 | 116 | |||
3852 | 117 | // Restores the global setting back to what it was | ||
3853 | 118 | ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(global_flag)); | ||
3854 | 119 | |||
3855 | 120 | g_list_free(start); | ||
3856 | 121 | return found; | ||
3857 | 122 | } | ||
3858 | 123 | |||
3859 | 124 | TextTextEntry* test_textentry = NULL; | ||
3860 | 125 | |||
3861 | 126 | void TestingThread(nux::NThread* thread, void* user_data) | ||
3862 | 127 | { | ||
3863 | 128 | while (test_textentry->ReadyToGo() == false) | ||
3864 | 129 | { | ||
3865 | 130 | nuxDebugMsg("Waiting to start"); | ||
3866 | 131 | nux::SleepForMilliseconds(300); | ||
3867 | 132 | } | ||
3868 | 133 | |||
3869 | 134 | nux::SleepForMilliseconds(1300); | ||
3870 | 135 | |||
3871 | 136 | nux::WindowThread* wnd_thread = static_cast<nux::WindowThread*>(user_data); | ||
3872 | 137 | |||
3873 | 138 | NuxAutomatedTestFramework test(wnd_thread); | ||
3874 | 139 | |||
3875 | 140 | test.Startup(); | ||
3876 | 141 | |||
3877 | 142 | test.TestReportMsg(test_textentry->text_entry_, "TextEntry created"); | ||
3878 | 143 | |||
3879 | 144 | test_textentry->ResetEvents(); | ||
3880 | 145 | test.ViewSendMouseMotionToCenter(test_textentry->text_entry_); | ||
3881 | 146 | |||
3882 | 147 | test.ViewSendMouseClick(0, 1); | ||
3883 | 148 | |||
3884 | 149 | // Type "Nux" | ||
3885 | 150 | // The cursor is at the end of the line | ||
3886 | 151 | // Simulate CTRL+A to select the entire text | ||
3887 | 152 | // Simulate DELETE key to delete the text | ||
3888 | 153 | { | ||
3889 | 154 | test.ViewSendString("Nux"); | ||
3890 | 155 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Nux", "Typed \"Nux\""); | ||
3891 | 156 | |||
3892 | 157 | test.ViewSendCtrlA(); | ||
3893 | 158 | nux::SleepForMilliseconds(500); | ||
3894 | 159 | test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "Nux", "Selection is \"Nux\""); | ||
3895 | 160 | |||
3896 | 161 | |||
3897 | 162 | test.ViewSendDelete(); | ||
3898 | 163 | nux::SleepForMilliseconds(500); | ||
3899 | 164 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); | ||
3900 | 165 | } | ||
3901 | 166 | |||
3902 | 167 | // Type "0123456789" | ||
3903 | 168 | // The cursor is at the end of the line | ||
3904 | 169 | // Simulate BACKSPACE key until the text is enpty | ||
3905 | 170 | { | ||
3906 | 171 | test.ViewSendString("Ubuntu"); | ||
3907 | 172 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubuntu", "Typed \"Ubuntu\""); | ||
3908 | 173 | |||
3909 | 174 | test.ViewSendBackspace(); | ||
3910 | 175 | nux::SleepForMilliseconds(500); | ||
3911 | 176 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubunt", "Text is \"Ubunt\""); | ||
3912 | 177 | |||
3913 | 178 | test.ViewSendBackspace(); | ||
3914 | 179 | nux::SleepForMilliseconds(500); | ||
3915 | 180 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubun", "Text is \"Ubun\""); | ||
3916 | 181 | |||
3917 | 182 | test.ViewSendBackspace(); | ||
3918 | 183 | nux::SleepForMilliseconds(500); | ||
3919 | 184 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubu", "Text is \"Ubu\""); | ||
3920 | 185 | |||
3921 | 186 | test.ViewSendBackspace(); | ||
3922 | 187 | nux::SleepForMilliseconds(500); | ||
3923 | 188 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ub", "Text is \"Ub\""); | ||
3924 | 189 | |||
3925 | 190 | test.ViewSendBackspace(); | ||
3926 | 191 | nux::SleepForMilliseconds(500); | ||
3927 | 192 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "U", "Text is \"U\""); | ||
3928 | 193 | |||
3929 | 194 | test.ViewSendBackspace(); | ||
3930 | 195 | nux::SleepForMilliseconds(500); | ||
3931 | 196 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); | ||
3932 | 197 | } | ||
3933 | 198 | |||
3934 | 199 | // Type "0123456789" | ||
3935 | 200 | // Move cursor to start of line | ||
3936 | 201 | // Simulate DELETE key until the text is 6789 | ||
3937 | 202 | // Simulate CTRL+A to select the entire text | ||
3938 | 203 | // Simulate DELETE key to delete the text | ||
3939 | 204 | { | ||
3940 | 205 | test.ViewSendString("0123456789"); | ||
3941 | 206 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "0123456789", "Typed \"0123456789\""); | ||
3942 | 207 | |||
3943 | 208 | test_textentry->text_entry_->MoveCursorToLineStart(); | ||
3944 | 209 | |||
3945 | 210 | test.ViewSendDelete(); | ||
3946 | 211 | nux::SleepForMilliseconds(500); | ||
3947 | 212 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "123456789", "Text is \"123456789\""); | ||
3948 | 213 | |||
3949 | 214 | test.ViewSendDelete(); | ||
3950 | 215 | nux::SleepForMilliseconds(500); | ||
3951 | 216 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "23456789", "Text is \"23456789\""); | ||
3952 | 217 | |||
3953 | 218 | test.ViewSendDelete(); | ||
3954 | 219 | nux::SleepForMilliseconds(500); | ||
3955 | 220 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "3456789", "Text is \"3456789\""); | ||
3956 | 221 | |||
3957 | 222 | test.ViewSendDelete(); | ||
3958 | 223 | nux::SleepForMilliseconds(500); | ||
3959 | 224 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "456789", "Text is \"456789\""); | ||
3960 | 225 | |||
3961 | 226 | test.ViewSendDelete(); | ||
3962 | 227 | nux::SleepForMilliseconds(500); | ||
3963 | 228 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "56789", "Text is \"56789\""); | ||
3964 | 229 | |||
3965 | 230 | test.ViewSendDelete(); | ||
3966 | 231 | nux::SleepForMilliseconds(500); | ||
3967 | 232 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "6789", "Text \"6789\""); | ||
3968 | 233 | |||
3969 | 234 | |||
3970 | 235 | test.ViewSendCtrlA(); | ||
3971 | 236 | nux::SleepForMilliseconds(500); | ||
3972 | 237 | test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "6789", "Selection is \"6789\""); | ||
3973 | 238 | |||
3974 | 239 | |||
3975 | 240 | test.ViewSendDelete(); | ||
3976 | 241 | nux::SleepForMilliseconds(500); | ||
3977 | 242 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); | ||
3978 | 243 | } | ||
3979 | 244 | |||
3980 | 245 | // Toggle IBus | ||
3981 | 246 | // Type "qwerty" | ||
3982 | 247 | // Simulate key '1' to select the first IM option | ||
3983 | 248 | { | ||
3984 | 249 | // CTRL+Space to initiate iBus | ||
3985 | 250 | //test.ViewSendIBusToggle(); | ||
3986 | 251 | |||
3987 | 252 | IBusBus* bus_; | ||
3988 | 253 | ibus_init(); | ||
3989 | 254 | bus_ = ibus_bus_new(); | ||
3990 | 255 | bool active = false; | ||
3991 | 256 | |||
3992 | 257 | // Test for ibus-pinyin | ||
3993 | 258 | if (SetEngineActive(bus_,"pinyin")) | ||
3994 | 259 | { | ||
3995 | 260 | // Type random stuff | ||
3996 | 261 | { | ||
3997 | 262 | test.ViewSendString("qwerty"); | ||
3998 | 263 | nux::SleepForMilliseconds(500); | ||
3999 | 264 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); | ||
4000 | 265 | |||
4001 | 266 | test.ViewSendChar('1'); | ||
4002 | 267 | nux::SleepForMilliseconds(500); | ||
4003 | 268 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "请问儿童有", "TextEntry is \"请问儿童有\""); | ||
4004 | 269 | |||
4005 | 270 | test.ViewSendCtrlA(); | ||
4006 | 271 | nux::SleepForMilliseconds(500); | ||
4007 | 272 | |||
4008 | 273 | test.ViewSendDelete(); | ||
4009 | 274 | nux::SleepForMilliseconds(500); | ||
4010 | 275 | } | ||
4011 | 276 | |||
4012 | 277 | // Testing Cursor in the preedit window | ||
4013 | 278 | { | ||
4014 | 279 | test.ViewSendString("ming"); | ||
4015 | 280 | nux::SleepForMilliseconds(500); | ||
4016 | 281 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); | ||
4017 | 282 | |||
4018 | 283 | test.ViewSendLeft(); | ||
4019 | 284 | nux::SleepForMilliseconds(500); | ||
4020 | 285 | |||
4021 | 286 | test.ViewSendDelete(); | ||
4022 | 287 | nux::SleepForMilliseconds(500); | ||
4023 | 288 | |||
4024 | 289 | test.ViewSendChar('1'); | ||
4025 | 290 | nux::SleepForMilliseconds(500); | ||
4026 | 291 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "民", "TextEntry is \"民\""); | ||
4027 | 292 | |||
4028 | 293 | test.ViewSendCtrlA(); | ||
4029 | 294 | nux::SleepForMilliseconds(500); | ||
4030 | 295 | |||
4031 | 296 | test.ViewSendDelete(); | ||
4032 | 297 | nux::SleepForMilliseconds(500); | ||
4033 | 298 | } | ||
4034 | 299 | |||
4035 | 300 | active = true; | ||
4036 | 301 | } | ||
4037 | 302 | else | ||
4038 | 303 | { | ||
4039 | 304 | test.TestReportMsg(false,"Pinyin is NOT an active input method" ); | ||
4040 | 305 | } | ||
4041 | 306 | |||
4042 | 307 | // Test for ibus-hangul | ||
4043 | 308 | if (SetEngineActive(bus_,"hangul")) | ||
4044 | 309 | { | ||
4045 | 310 | // Test for the the space in ibus-hangul working correctlly | ||
4046 | 311 | { | ||
4047 | 312 | test.ViewSendString("asd abc "); | ||
4048 | 313 | nux::SleepForMilliseconds(500); | ||
4049 | 314 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "ㅁㄴㅇ 뮻 ", "TextEntry is \"ㅁㄴㅇ 뮻 \""); | ||
4050 | 315 | |||
4051 | 316 | test.ViewSendCtrlA(); | ||
4052 | 317 | nux::SleepForMilliseconds(500); | ||
4053 | 318 | |||
4054 | 319 | test.ViewSendDelete(); | ||
4055 | 320 | nux::SleepForMilliseconds(500); | ||
4056 | 321 | } | ||
4057 | 322 | |||
4058 | 323 | active = true; | ||
4059 | 324 | } | ||
4060 | 325 | else | ||
4061 | 326 | { | ||
4062 | 327 | test.TestReportMsg(false,"Hangul is NOT an active input method" ); | ||
4063 | 328 | } | ||
4064 | 329 | |||
4065 | 330 | // Checking for ibus-anthy - Japanese | ||
4066 | 331 | if (SetEngineActive(bus_,"anthy")) | ||
4067 | 332 | { | ||
4068 | 333 | { | ||
4069 | 334 | test.ViewSendString("shisutemu "); | ||
4070 | 335 | nux::SleepForMilliseconds(500); | ||
4071 | 336 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); | ||
4072 | 337 | |||
4073 | 338 | // Ctrl + J commits for ibus-anthy | ||
4074 | 339 | test.ViewSendKeyCombo(XK_Control_L, 0, 0, 'j'); | ||
4075 | 340 | nux::SleepForMilliseconds(500); | ||
4076 | 341 | test.TestReportMsg(test_textentry->text_entry_->GetText() == "システム", "TextEntry is \"システム\""); | ||
4077 | 342 | |||
4078 | 343 | test.ViewSendCtrlA(); | ||
4079 | 344 | nux::SleepForMilliseconds(500); | ||
4080 | 345 | |||
4081 | 346 | test.ViewSendDelete(); | ||
4082 | 347 | nux::SleepForMilliseconds(500); | ||
4083 | 348 | } | ||
4084 | 349 | |||
4085 | 350 | active = true; | ||
4086 | 351 | } | ||
4087 | 352 | else | ||
4088 | 353 | { | ||
4089 | 354 | test.TestReportMsg(false,"Anthy is NOT an active input method" ); | ||
4090 | 355 | } | ||
4091 | 356 | |||
4092 | 357 | g_object_unref (bus_); | ||
4093 | 358 | |||
4094 | 359 | // CTRL+Space to deactivate iBus | ||
4095 | 360 | if (active) | ||
4096 | 361 | test.ViewSendIBusToggle(); | ||
4097 | 362 | } | ||
4098 | 363 | |||
4099 | 364 | if (test.WhenDoneTerminateProgram()) | ||
4100 | 365 | { | ||
4101 | 366 | wnd_thread->ExitMainLoop(); | ||
4102 | 367 | } | ||
4103 | 368 | nuxDebugMsg("Exit testing thread"); | ||
4104 | 369 | } | ||
4105 | 370 | |||
4106 | 371 | int main(int argc, char** argv) | ||
4107 | 372 | { | ||
4108 | 373 | int xstatus = XInitThreads(); | ||
4109 | 374 | nuxAssertMsg(xstatus > 0, "XInitThreads has failed"); | ||
4110 | 375 | |||
4111 | 376 | test_textentry = new TextTextEntry("Text Entry", 600, 200, 40000); | ||
4112 | 377 | test_textentry->Startup(); | ||
4113 | 378 | test_textentry->UserInterfaceSetup(); | ||
4114 | 379 | |||
4115 | 380 | nux::SystemThread* test_thread = nux::CreateSystemThread(NULL, &TestingThread, test_textentry->GetWindowThread()); | ||
4116 | 381 | |||
4117 | 382 | test_thread->Start(test_textentry); | ||
4118 | 383 | |||
4119 | 384 | test_textentry->Run(); | ||
4120 | 385 | |||
4121 | 386 | delete test_thread; | ||
4122 | 387 | delete test_textentry; | ||
4123 | 388 | |||
4124 | 389 | return 0; | ||
4125 | 390 | } | ||
4126 | 391 |
Excellent work!