Nux

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

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

Description of the change

TextEntry with InputMethod support.

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

Excellent work!

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: