[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / input / ubuntu-x11 / input-method-context-impl-x.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/input/ubuntu-x11/input-method-context-impl-x.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/events/key-event.h>
25 #include <dali/public-api/object/type-registry.h>
26
27 // INTERNAL INCLUDES
28 #include <dali/integration-api/adaptor-framework/adaptor.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/internal/input/common/key-impl.h>
31 #include <dali/internal/input/common/virtual-keyboard-impl.h>
32 #include <dali/internal/input/linux/dali-ecore-imf.h>
33 #include <dali/internal/input/ubuntu-x11/dali-ecore-input.h>
34 #include <dali/internal/system/common/locale-utils.h>
35 #include <dali/internal/system/linux/dali-ecore.h>
36 #include <dali/public-api/adaptor-framework/key.h>
37
38 namespace Dali
39 {
40 namespace Internal
41 {
42 namespace Adaptor
43 {
44 namespace
45 {
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_INPUT_METHOD_CONTEXT");
48 #endif
49
50 // Currently this code is internal to dali/dali/internal/event/text/utf8.h but should be made Public and used from there instead.
51 size_t Utf8SequenceLength(const unsigned char leadByte)
52 {
53   size_t length = 0;
54
55   if((leadByte & 0x80) == 0) //ASCII character (lead bit zero)
56   {
57     length = 1;
58   }
59   else if((leadByte & 0xe0) == 0xc0) //110x xxxx
60   {
61     length = 2;
62   }
63   else if((leadByte & 0xf0) == 0xe0) //1110 xxxx
64   {
65     length = 3;
66   }
67   else if((leadByte & 0xf8) == 0xf0) //1111 0xxx
68   {
69     length = 4;
70   }
71   else if((leadByte & 0xfc) == 0xf8) //1111 10xx
72   {
73     length = 5;
74   }
75   else if((leadByte & 0xfe) == 0xfc) //1111 110x
76   {
77     length = 6;
78   }
79
80   return length;
81 }
82
83 // Static function calls used by ecore 'c' style callback registration
84 void Commit(void* data, Ecore_IMF_Context* imfContext, void* eventInfo)
85 {
86   if(data)
87   {
88     InputMethodContextX* inputMethodContext = static_cast<InputMethodContextX*>(data);
89     inputMethodContext->CommitReceived(data, imfContext, eventInfo);
90   }
91 }
92
93 void PreEdit(void* data, Ecore_IMF_Context* imfContext, void* eventInfo)
94 {
95   if(data)
96   {
97     InputMethodContextX* inputMethodContext = static_cast<InputMethodContextX*>(data);
98     inputMethodContext->PreEditChanged(data, imfContext, eventInfo);
99   }
100 }
101
102 Eina_Bool ImfRetrieveSurrounding(void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition)
103 {
104   if(data)
105   {
106     InputMethodContextX* inputMethodContext = static_cast<InputMethodContextX*>(data);
107     return inputMethodContext->RetrieveSurrounding(data, imfContext, text, cursorPosition);
108   }
109   else
110   {
111     return false;
112   }
113 }
114
115 /**
116  * Called when an InputMethodContext delete surrounding event is received.
117  * Here we tell the application that it should delete a certain range.
118  */
119 void ImfDeleteSurrounding(void* data, Ecore_IMF_Context* imfContext, void* eventInfo)
120 {
121   if(data)
122   {
123     InputMethodContextX* inputMethodContext = static_cast<InputMethodContextX*>(data);
124     inputMethodContext->DeleteSurrounding(data, imfContext, eventInfo);
125   }
126 }
127
128 } // unnamed namespace
129
130 InputMethodContextPtr InputMethodContextX::New(Dali::Actor actor)
131 {
132   InputMethodContextPtr manager;
133
134   if(actor && Dali::Adaptor::IsAvailable())
135   {
136     manager = new InputMethodContextX(actor);
137   }
138
139   return manager;
140 }
141
142 void InputMethodContextX::Finalize()
143 {
144   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::Finalize\n");
145   DisconnectCallbacks();
146   DeleteContext();
147 }
148
149 InputMethodContextX::InputMethodContextX(Dali::Actor actor)
150 : mIMFContext(),
151   mEcoreXwin(0),
152   mIMFCursorPosition(0),
153   mSurroundingText(),
154   mRestoreAfterFocusLost(false),
155   mIdleCallbackConnected(false)
156 {
157   ecore_imf_init();
158
159   actor.OnSceneSignal().Connect(this, &InputMethodContextX::OnStaged);
160 }
161
162 InputMethodContextX::~InputMethodContextX()
163 {
164   Finalize();
165   ecore_imf_shutdown();
166 }
167
168 void InputMethodContextX::Initialize()
169 {
170   CreateContext();
171   ConnectCallbacks();
172 }
173
174 void InputMethodContextX::CreateContext()
175 {
176   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::CreateContext\n");
177
178   if(!mEcoreXwin)
179   {
180     return;
181   }
182
183   const char* contextId = ecore_imf_context_default_id_get();
184   if(contextId)
185   {
186     mIMFContext = ecore_imf_context_add(contextId);
187
188     if(mIMFContext)
189     {
190       ecore_imf_context_client_window_set(mIMFContext, reinterpret_cast<void*>(mEcoreXwin));
191     }
192     else
193     {
194       DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContext Unable to get IMFContext\n");
195     }
196   }
197   else
198   {
199     DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContext Unable to get IMFContext\n");
200   }
201 }
202
203 void InputMethodContextX::DeleteContext()
204 {
205   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::DeleteContext\n");
206
207   if(mIMFContext)
208   {
209     ecore_imf_context_del(mIMFContext);
210     mIMFContext = NULL;
211   }
212 }
213
214 // Callbacks for predicitive text support.
215 void InputMethodContextX::ConnectCallbacks()
216 {
217   if(mIMFContext)
218   {
219     DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::ConnectCallbacks\n");
220
221     ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit, this);
222     ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit, this);
223     ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this);
224
225     ecore_imf_context_retrieve_surrounding_callback_set(mIMFContext, ImfRetrieveSurrounding, this);
226   }
227 }
228
229 void InputMethodContextX::DisconnectCallbacks()
230 {
231   if(mIMFContext)
232   {
233     DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::DisconnectCallbacks\n");
234
235     ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit);
236     ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit);
237     ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding);
238
239     // We do not need to unset the retrieve surrounding callback.
240   }
241 }
242
243 void InputMethodContextX::Activate()
244 {
245   // Reset mIdleCallbackConnected
246   mIdleCallbackConnected = false;
247
248   if(mIMFContext)
249   {
250     DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::Activate\n");
251
252     ecore_imf_context_focus_in(mIMFContext);
253
254     // emit keyboard activated signal
255     Dali::InputMethodContext handle(this);
256     mActivatedSignal.Emit(handle);
257   }
258 }
259
260 void InputMethodContextX::Deactivate()
261 {
262   if(mIMFContext)
263   {
264     DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::Deactivate\n");
265
266     Reset();
267     ecore_imf_context_focus_out(mIMFContext);
268   }
269
270   // Reset mIdleCallbackConnected
271   mIdleCallbackConnected = false;
272 }
273
274 void InputMethodContextX::Reset()
275 {
276   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::Reset\n");
277
278   if(mIMFContext)
279   {
280     ecore_imf_context_reset(mIMFContext);
281   }
282 }
283
284 ImfContext* InputMethodContextX::GetContext()
285 {
286   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetContext\n");
287
288   return mIMFContext;
289 }
290
291 bool InputMethodContextX::RestoreAfterFocusLost() const
292 {
293   return mRestoreAfterFocusLost;
294 }
295
296 void InputMethodContextX::SetRestoreAfterFocusLost(bool toggle)
297 {
298   mRestoreAfterFocusLost = toggle;
299 }
300
301 /**
302  * Called when an InputMethodContext Pre-Edit changed event is received.
303  * We are still predicting what the user is typing.  The latest string is what the InputMethodContext module thinks
304  * the user wants to type.
305  */
306 void InputMethodContextX::PreEditChanged(void*, ImfContext* imfContext, void* eventInfo)
307 {
308   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::PreEditChanged\n");
309   auto context = static_cast<Ecore_IMF_Context*>(imfContext);
310
311   char*      preEditString(NULL);
312   int        cursorPosition(0);
313   Eina_List* attrs = NULL;
314   Eina_List* l     = NULL;
315
316   Ecore_IMF_Preedit_Attr* attr;
317
318   mPreeditAttrs.Clear();
319
320   // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
321   // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
322   ecore_imf_context_preedit_string_with_attributes_get(context, &preEditString, &attrs, &cursorPosition);
323
324   if(attrs)
325   {
326     // iterate through the list of attributes getting the type, start and end position.
327     for(l = attrs, (attr = static_cast<Ecore_IMF_Preedit_Attr*>(eina_list_data_get(l))); l; l = eina_list_next(l), (attr = static_cast<Ecore_IMF_Preedit_Attr*>(eina_list_data_get(l))))
328     {
329       Dali::InputMethodContext::PreeditAttributeData data;
330       data.startIndex = 0;
331       data.endIndex   = 0;
332
333       size_t visualCharacterIndex = 0;
334       size_t byteIndex            = 0;
335
336       // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
337       char leadByte = preEditString[byteIndex];
338
339       while(leadByte != '\0')
340       {
341         leadByte = preEditString[byteIndex]; // Update the character to get the number of its byte
342
343         // attr->end_index is provided as a byte position not character and we need to know the character position.
344         const size_t currentSequenceLength = Utf8SequenceLength(leadByte); // returns number of bytes used to represent character.
345         if(byteIndex <= attr->start_index)
346         {
347           data.startIndex = visualCharacterIndex;
348         }
349         if(byteIndex >= attr->end_index)
350         {
351           data.endIndex = visualCharacterIndex;
352           break;
353           // end loop as found cursor position that matches byte position
354         }
355         else
356         {
357           byteIndex += currentSequenceLength; // jump to next character
358           visualCharacterIndex++;             // increment character count so we know our position for when we get a match
359         }
360       }
361
362       switch(attr->preedit_type)
363       {
364         case ECORE_IMF_PREEDIT_TYPE_NONE:
365         {
366           data.preeditType = Dali::InputMethodContext::PreeditStyle::NONE;
367           break;
368         }
369         case ECORE_IMF_PREEDIT_TYPE_SUB1:
370         {
371           data.preeditType = Dali::InputMethodContext::PreeditStyle::UNDERLINE;
372           break;
373         }
374         case ECORE_IMF_PREEDIT_TYPE_SUB2:
375         {
376           data.preeditType = Dali::InputMethodContext::PreeditStyle::REVERSE;
377           break;
378         }
379         case ECORE_IMF_PREEDIT_TYPE_SUB3:
380         {
381           data.preeditType = Dali::InputMethodContext::PreeditStyle::HIGHLIGHT;
382           break;
383         }
384         case ECORE_IMF_PREEDIT_TYPE_SUB4:
385         {
386           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1;
387           break;
388         }
389         case ECORE_IMF_PREEDIT_TYPE_SUB5:
390         {
391           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2;
392           break;
393         }
394         case ECORE_IMF_PREEDIT_TYPE_SUB6:
395         {
396           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3;
397           break;
398         }
399         case ECORE_IMF_PREEDIT_TYPE_SUB7:
400         {
401           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4;
402           break;
403         }
404         default:
405         {
406           data.preeditType = Dali::InputMethodContext::PreeditStyle::NONE;
407           break;
408         }
409       }
410       mPreeditAttrs.PushBack(data);
411     }
412   }
413
414   if(Dali::Adaptor::IsAvailable())
415   {
416     Dali::InputMethodContext               handle(this);
417     Dali::InputMethodContext::EventData    eventData(Dali::InputMethodContext::PRE_EDIT, preEditString, cursorPosition, 0);
418     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit(handle, eventData);
419
420     if(callbackData.update)
421     {
422       mIMFCursorPosition = static_cast<int>(callbackData.cursorPosition);
423
424       NotifyCursorPosition();
425     }
426
427     if(callbackData.preeditResetRequired)
428     {
429       Reset();
430     }
431   }
432   free(preEditString);
433 }
434
435 void InputMethodContextX::CommitReceived(void*, ImfContext* imfContext, void* eventInfo)
436 {
437   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::CommitReceived\n");
438
439   if(Dali::Adaptor::IsAvailable())
440   {
441     const std::string keyString(static_cast<char*>(eventInfo));
442
443     Dali::InputMethodContext               handle(this);
444     Dali::InputMethodContext::EventData    eventData(Dali::InputMethodContext::COMMIT, keyString, 0, 0);
445     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit(handle, eventData);
446
447     if(callbackData.update)
448     {
449       mIMFCursorPosition = static_cast<int>(callbackData.cursorPosition);
450
451       NotifyCursorPosition();
452     }
453   }
454 }
455
456 /**
457  * Called when an InputMethodContext retrieve surround event is received.
458  * Here the InputMethodContext module wishes to know the string we are working with and where within the string the cursor is
459  * We need to signal the application to tell us this information.
460  */
461 bool InputMethodContextX::RetrieveSurrounding(void* data, ImfContext* imfContext, char** text, int* cursorPosition)
462 {
463   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::RetrieveSurrounding\n");
464
465   Dali::InputMethodContext::EventData    imfData(Dali::InputMethodContext::GET_SURROUNDING, std::string(), 0, 0);
466   Dali::InputMethodContext               handle(this);
467   Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit(handle, imfData);
468
469   if(callbackData.update)
470   {
471     if(text)
472     {
473       *text = strdup(callbackData.currentText.c_str());
474     }
475
476     if(cursorPosition)
477     {
478       mIMFCursorPosition = static_cast<int>(callbackData.cursorPosition);
479       *cursorPosition    = mIMFCursorPosition;
480     }
481   }
482
483   return EINA_TRUE;
484 }
485
486 /**
487  * Called when an InputMethodContext delete surrounding event is received.
488  * Here we tell the application that it should delete a certain range.
489  */
490 void InputMethodContextX::DeleteSurrounding(void* data, ImfContext* imfContext, void* eventInfo)
491 {
492   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::DeleteSurrounding\n");
493
494   if(Dali::Adaptor::IsAvailable())
495   {
496     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>(eventInfo);
497
498     Dali::InputMethodContext::EventData    imfData(Dali::InputMethodContext::DELETE_SURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars);
499     Dali::InputMethodContext               handle(this);
500     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit(handle, imfData);
501
502     if(callbackData.update)
503     {
504       mIMFCursorPosition = static_cast<int>(callbackData.cursorPosition);
505
506       NotifyCursorPosition();
507     }
508   }
509 }
510
511 void InputMethodContextX::NotifyCursorPosition()
512 {
513   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::NotifyCursorPosition\n");
514
515   if(mIMFContext)
516   {
517     ecore_imf_context_cursor_position_set(mIMFContext, mIMFCursorPosition);
518   }
519 }
520
521 void InputMethodContextX::SetCursorPosition(unsigned int cursorPosition)
522 {
523   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetCursorPosition\n");
524
525   mIMFCursorPosition = static_cast<int>(cursorPosition);
526 }
527
528 unsigned int InputMethodContextX::GetCursorPosition() const
529 {
530   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetCursorPosition\n");
531
532   return static_cast<unsigned int>(mIMFCursorPosition);
533 }
534
535 void InputMethodContextX::SetSurroundingText(const std::string& text)
536 {
537   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetSurroundingText\n");
538
539   mSurroundingText = text;
540 }
541
542 const std::string& InputMethodContextX::GetSurroundingText() const
543 {
544   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetSurroundingText\n");
545
546   return mSurroundingText;
547 }
548
549 void InputMethodContextX::NotifyTextInputMultiLine(bool multiLine)
550 {
551 }
552
553 Dali::InputMethodContext::TextDirection InputMethodContextX::GetTextDirection()
554 {
555   Dali::InputMethodContext::TextDirection direction(Dali::InputMethodContext::LEFT_TO_RIGHT);
556
557   if(mIMFContext)
558   {
559     char* locale(NULL);
560     ecore_imf_context_input_panel_language_locale_get(mIMFContext, &locale);
561
562     if(locale)
563     {
564       direction = static_cast<Dali::InputMethodContext::TextDirection>(Locale::GetDirection(std::string(locale)));
565       free(locale);
566     }
567   }
568
569   return direction;
570 }
571
572 Rect<int> InputMethodContextX::GetInputMethodArea()
573 {
574   int xPos, yPos, width, height;
575
576   width = height = xPos = yPos = 0;
577
578   if(mIMFContext)
579   {
580     ecore_imf_context_input_panel_geometry_get(mIMFContext, &xPos, &yPos, &width, &height);
581   }
582   else
583   {
584     DALI_LOG_WARNING("VKB Unable to get InputMethodContext Context so GetSize unavailable\n");
585   }
586
587   return Rect<int>(xPos, yPos, width, height);
588 }
589
590 void InputMethodContextX::ApplyOptions(const InputMethodOptions& options)
591 {
592   using namespace Dali::InputMethod::Category;
593
594   int index;
595
596   if(mIMFContext == NULL)
597   {
598     DALI_LOG_WARNING("VKB Unable to excute ApplyOptions with Null ImfContext\n");
599     return;
600   }
601
602   if(mOptions.CompareAndSet(PANEL_LAYOUT, options, index))
603   {
604   }
605   if(mOptions.CompareAndSet(BUTTON_ACTION, options, index))
606   {
607   }
608   if(mOptions.CompareAndSet(AUTO_CAPITALIZE, options, index))
609   {
610   }
611   if(mOptions.CompareAndSet(VARIATION, options, index))
612   {
613   }
614 }
615
616 void InputMethodContextX::SetInputPanelData(const std::string& data)
617 {
618   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelData\n");
619
620   if(mIMFContext)
621   {
622     int length = data.length();
623     ecore_imf_context_input_panel_imdata_set(mIMFContext, data.c_str(), length);
624   }
625 }
626
627 void InputMethodContextX::GetInputPanelData(std::string& data)
628 {
629   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelData\n");
630
631   if(mIMFContext)
632   {
633     int                length = 4096; // The max length is 4096 bytes
634     Dali::Vector<char> buffer;
635     buffer.Resize(length);
636     ecore_imf_context_input_panel_imdata_get(mIMFContext, &buffer[0], &length);
637     data = std::string(buffer.Begin(), buffer.End());
638   }
639 }
640
641 Dali::InputMethodContext::State InputMethodContextX::GetInputPanelState()
642 {
643   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelState\n");
644
645   if(mIMFContext)
646   {
647     int value;
648     value = ecore_imf_context_input_panel_state_get(mIMFContext);
649
650     switch(value)
651     {
652       case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
653       {
654         return Dali::InputMethodContext::SHOW;
655         break;
656       }
657
658       case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
659       {
660         return Dali::InputMethodContext::HIDE;
661         break;
662       }
663
664       case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
665       {
666         return Dali::InputMethodContext::WILL_SHOW;
667         break;
668       }
669
670       default:
671       {
672         return Dali::InputMethodContext::DEFAULT;
673       }
674     }
675   }
676   return Dali::InputMethodContext::DEFAULT;
677 }
678
679 void InputMethodContextX::SetReturnKeyState(bool visible)
680 {
681   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetReturnKeyState\n");
682
683   if(mIMFContext)
684   {
685     ecore_imf_context_input_panel_return_key_disabled_set(mIMFContext, !visible);
686   }
687 }
688
689 void InputMethodContextX::AutoEnableInputPanel(bool enabled)
690 {
691   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::AutoEnableInputPanel\n");
692
693   if(mIMFContext)
694   {
695     ecore_imf_context_input_panel_enabled_set(mIMFContext, enabled);
696   }
697 }
698
699 void InputMethodContextX::ShowInputPanel()
700 {
701   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::ShowInputPanel\n");
702
703   if(mIMFContext)
704   {
705     ecore_imf_context_input_panel_show(mIMFContext);
706   }
707 }
708
709 void InputMethodContextX::HideInputPanel()
710 {
711   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::HideInputPanel\n");
712
713   if(mIMFContext)
714   {
715     ecore_imf_context_input_panel_hide(mIMFContext);
716   }
717 }
718
719 Dali::InputMethodContext::KeyboardType InputMethodContextX::GetKeyboardType()
720 {
721   return Dali::InputMethodContext::KeyboardType::SOFTWARE_KEYBOARD;
722 }
723
724 std::string InputMethodContextX::GetInputPanelLocale()
725 {
726   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelLocale\n");
727
728   std::string locale = "";
729
730   if(mIMFContext)
731   {
732     char* value = NULL;
733     ecore_imf_context_input_panel_language_locale_get(mIMFContext, &value);
734
735     if(value)
736     {
737       std::string valueCopy(value);
738       locale = valueCopy;
739
740       // The locale string retrieved must be freed with free().
741       free(value);
742     }
743   }
744   return locale;
745 }
746
747 void InputMethodContextX::SetContentMIMETypes(const std::string& mimeTypes)
748 {
749   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetContentMIMETypes\n");
750   // ecore_imf_context_mime_type_accept_set() is supported from ecore-imf 1.20.0 version.
751 }
752
753 bool InputMethodContextX::FilterEventKey(const Dali::KeyEvent& keyEvent)
754 {
755   bool eventHandled(false);
756
757   // If a device key then skip ecore_imf_context_filter_event.
758   if(!KeyLookup::IsDeviceButton(keyEvent.GetKeyName().c_str()))
759   {
760     //check whether it's key down or key up event
761     if(keyEvent.GetState() == Dali::KeyEvent::DOWN)
762     {
763       eventHandled = ProcessEventKeyDown(keyEvent);
764     }
765     else if(keyEvent.GetState() == Dali::KeyEvent::UP)
766     {
767       eventHandled = ProcessEventKeyUp(keyEvent);
768     }
769   }
770
771   return eventHandled;
772 }
773
774 void InputMethodContextX::AllowTextPrediction(bool prediction)
775 {
776   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::AllowTextPrediction\n");
777
778   if(mIMFContext)
779   {
780     ecore_imf_context_prediction_allow_set(mIMFContext, prediction);
781   }
782 }
783
784 bool InputMethodContextX::IsTextPredictionAllowed() const
785 {
786   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::IsTextPredictionAllowed\n");
787   bool prediction = false;
788   if(mIMFContext)
789   {
790     prediction = ecore_imf_context_prediction_allow_get(mIMFContext);
791   }
792   return prediction;
793 }
794
795 void InputMethodContextX::SetInputPanelLanguage(Dali::InputMethodContext::InputPanelLanguage language)
796 {
797   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelLanguage\n");
798   if(mIMFContext)
799   {
800     switch(language)
801     {
802       case Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC:
803       {
804         ecore_imf_context_input_panel_language_set(mIMFContext, ECORE_IMF_INPUT_PANEL_LANG_AUTOMATIC);
805         break;
806       }
807       case Dali::InputMethodContext::InputPanelLanguage::ALPHABET:
808       {
809         ecore_imf_context_input_panel_language_set(mIMFContext, ECORE_IMF_INPUT_PANEL_LANG_ALPHABET);
810         break;
811       }
812     }
813   }
814 }
815
816 Dali::InputMethodContext::InputPanelLanguage InputMethodContextX::GetInputPanelLanguage() const
817 {
818   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelLanguage\n");
819   if(mIMFContext)
820   {
821     int value;
822     value = ecore_imf_context_input_panel_language_get(mIMFContext);
823
824     switch(value)
825     {
826       case ECORE_IMF_INPUT_PANEL_LANG_AUTOMATIC:
827       {
828         return Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC;
829         break;
830       }
831       case ECORE_IMF_INPUT_PANEL_LANG_ALPHABET:
832       {
833         return Dali::InputMethodContext::InputPanelLanguage::ALPHABET;
834         break;
835       }
836     }
837   }
838   return Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC;
839 }
840
841 void InputMethodContextX::SetInputPanelPosition(unsigned int x, unsigned int y)
842 {
843   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelPosition\n");
844
845   // ecore_imf_context_input_panel_position_set() is supported from ecore-imf 1.21.0 version.
846 }
847
848 void InputMethodContextX::GetPreeditStyle(Dali::InputMethodContext::PreEditAttributeDataContainer& attrs) const
849 {
850   DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextX::GetPreeditStyle\n");
851   attrs = mPreeditAttrs;
852 }
853
854 bool InputMethodContextX::ProcessEventKeyDown(const Dali::KeyEvent& keyEvent)
855 {
856   bool eventHandled(false);
857   if(mIMFContext)
858   {
859     Integration::KeyEvent integKeyEvent(keyEvent.GetKeyName(), keyEvent.GetLogicalKey(), keyEvent.GetKeyString(), keyEvent.GetKeyCode(), keyEvent.GetKeyModifier(), keyEvent.GetTime(), static_cast<Integration::KeyEvent::State>(keyEvent.GetState()), keyEvent.GetCompose(), keyEvent.GetDeviceName(), keyEvent.GetDeviceClass(), keyEvent.GetDeviceSubclass());
860     std::string           key = integKeyEvent.logicalKey;
861
862     std::string compose = integKeyEvent.compose;
863
864     // We're consuming key down event so we have to pass to InputMethodContext so that it can parse it as well.
865     Ecore_IMF_Event_Key_Down ecoreKeyDownEvent;
866     ecoreKeyDownEvent.keyname   = integKeyEvent.keyName.c_str();
867     ecoreKeyDownEvent.key       = key.c_str();
868     ecoreKeyDownEvent.string    = integKeyEvent.keyString.c_str();
869     ecoreKeyDownEvent.compose   = compose.c_str();
870     ecoreKeyDownEvent.timestamp = integKeyEvent.time;
871     ecoreKeyDownEvent.modifiers = EcoreInputModifierToEcoreIMFModifier(integKeyEvent.keyModifier);
872     ecoreKeyDownEvent.locks     = EcoreInputModifierToEcoreIMFLock(integKeyEvent.keyModifier);
873
874 #if defined(ECORE_VERSION_MAJOR) && (ECORE_VERSION_MAJOR >= 1) && defined(ECORE_VERSION_MINOR)
875 #if(ECORE_VERSION_MINOR >= 14)
876     ecoreKeyDownEvent.dev_name     = "";
877     ecoreKeyDownEvent.dev_class    = ECORE_IMF_DEVICE_CLASS_KEYBOARD;
878     ecoreKeyDownEvent.dev_subclass = ECORE_IMF_DEVICE_SUBCLASS_NONE;
879 #endif // Since ecore_imf 1.14 version
880 #if(ECORE_VERSION_MINOR >= 22)
881     ecoreKeyDownEvent.keycode = integKeyEvent.keyCode;
882 #endif // Since ecore_imf 1.22 version
883 #endif // Since ecore_imf Version 1
884
885     // If the device is IME and the focused key is the direction keys, then we should send a key event to move a key cursor.
886     if((integKeyEvent.deviceName == "ime") && ((!strncmp(integKeyEvent.keyName.c_str(), "Left", 4)) ||
887                                                (!strncmp(integKeyEvent.keyName.c_str(), "Right", 5)) ||
888                                                (!strncmp(integKeyEvent.keyName.c_str(), "Up", 2)) ||
889                                                (!strncmp(integKeyEvent.keyName.c_str(), "Down", 4))))
890     {
891       eventHandled = 0;
892     }
893     else
894     {
895       eventHandled = ecore_imf_context_filter_event(mIMFContext,
896                                                     ECORE_IMF_EVENT_KEY_DOWN,
897                                                     reinterpret_cast<Ecore_IMF_Event*>(&ecoreKeyDownEvent));
898     }
899
900     // If the event has not been handled by InputMethodContext then check if we should reset our IMFcontext
901     if(!eventHandled)
902     {
903       if(!strcmp(integKeyEvent.keyName.c_str(), "Escape") ||
904          !strcmp(integKeyEvent.keyName.c_str(), "Return") ||
905          !strcmp(integKeyEvent.keyName.c_str(), "KP_Enter"))
906       {
907         ecore_imf_context_reset(mIMFContext);
908       }
909     }
910   }
911   return eventHandled;
912 }
913
914 bool InputMethodContextX::ProcessEventKeyUp(const Dali::KeyEvent& keyEvent)
915 {
916   bool eventHandled(false);
917   if(mIMFContext)
918   {
919     Integration::KeyEvent integKeyEvent(keyEvent.GetKeyName(), keyEvent.GetLogicalKey(), keyEvent.GetKeyString(), keyEvent.GetKeyCode(), keyEvent.GetKeyModifier(), keyEvent.GetTime(), static_cast<Integration::KeyEvent::State>(keyEvent.GetState()), keyEvent.GetCompose(), keyEvent.GetDeviceName(), keyEvent.GetDeviceClass(), keyEvent.GetDeviceSubclass());
920
921     std::string key = integKeyEvent.logicalKey;
922
923     std::string compose = integKeyEvent.compose;
924
925     // We're consuming key up event so we have to pass to InputMethodContext so that it can parse it as well.
926     Ecore_IMF_Event_Key_Up ecoreKeyUpEvent;
927     ecoreKeyUpEvent.keyname   = integKeyEvent.keyName.c_str();
928     ecoreKeyUpEvent.key       = key.c_str();
929     ecoreKeyUpEvent.string    = integKeyEvent.keyString.c_str();
930     ecoreKeyUpEvent.compose   = compose.c_str();
931     ecoreKeyUpEvent.timestamp = integKeyEvent.time;
932     ecoreKeyUpEvent.modifiers = EcoreInputModifierToEcoreIMFModifier(integKeyEvent.keyModifier);
933     ecoreKeyUpEvent.locks     = EcoreInputModifierToEcoreIMFLock(integKeyEvent.keyModifier);
934 #if defined(ECORE_VERSION_MAJOR) && (ECORE_VERSION_MAJOR >= 1) && defined(ECORE_VERSION_MINOR)
935 #if(ECORE_VERSION_MINOR >= 14)
936     ecoreKeyUpEvent.dev_name = "";
937 #endif // Since ecore_imf 1.14 version
938 #if(ECORE_VERSION_MINOR >= 22)
939     ecoreKeyUpEvent.keycode = integKeyEvent.keyCode;
940 #endif // Since ecore_imf 1.22 version
941 #endif // Since ecore_imf Version 1
942
943     eventHandled = ecore_imf_context_filter_event(mIMFContext,
944                                                   ECORE_IMF_EVENT_KEY_UP,
945                                                   reinterpret_cast<Ecore_IMF_Event*>(&ecoreKeyUpEvent));
946   }
947   return eventHandled;
948 }
949
950 Ecore_IMF_Keyboard_Modifiers InputMethodContextX::EcoreInputModifierToEcoreIMFModifier(unsigned int ecoreModifier)
951 {
952   unsigned int modifier(ECORE_IMF_KEYBOARD_MODIFIER_NONE); // If no other matches returns NONE.
953
954   if(ecoreModifier & ECORE_EVENT_MODIFIER_SHIFT) // enums from ecore_input/Ecore_Input.h
955   {
956     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_SHIFT; // enums from ecore_imf/ecore_imf.h
957   }
958
959   if(ecoreModifier & ECORE_EVENT_MODIFIER_ALT)
960   {
961     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_ALT;
962   }
963
964   if(ecoreModifier & ECORE_EVENT_MODIFIER_CTRL)
965   {
966     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_CTRL;
967   }
968
969   if(ecoreModifier & ECORE_EVENT_MODIFIER_WIN)
970   {
971     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_WIN;
972   }
973
974   if(ecoreModifier & ECORE_EVENT_MODIFIER_ALTGR)
975   {
976     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_ALTGR;
977   }
978
979   return static_cast<Ecore_IMF_Keyboard_Modifiers>(modifier);
980 }
981
982 Ecore_IMF_Keyboard_Locks InputMethodContextX::EcoreInputModifierToEcoreIMFLock(unsigned int modifier)
983 {
984   unsigned int lock(ECORE_IMF_KEYBOARD_LOCK_NONE); // If no other matches, returns NONE.
985
986   if(modifier & ECORE_EVENT_LOCK_NUM)
987   {
988     lock |= ECORE_IMF_KEYBOARD_LOCK_NUM; // Num lock is active.
989   }
990
991   if(modifier & ECORE_EVENT_LOCK_CAPS)
992   {
993     lock |= ECORE_IMF_KEYBOARD_LOCK_CAPS; // Caps lock is active.
994   }
995
996   if(modifier & ECORE_EVENT_LOCK_SCROLL)
997   {
998     lock |= ECORE_IMF_KEYBOARD_LOCK_SCROLL; // Scroll lock is active.
999   }
1000
1001   return static_cast<Ecore_IMF_Keyboard_Locks>(lock);
1002 }
1003
1004 void InputMethodContextX::OnStaged(Dali::Actor actor)
1005 {
1006   Ecore_X_Window ecoreXwin(AnyCast<Ecore_X_Window>(Dali::Integration::SceneHolder::Get(actor).GetNativeHandle()));
1007
1008   if(mEcoreXwin != ecoreXwin)
1009   {
1010     mEcoreXwin = ecoreXwin;
1011
1012     // Reset
1013     Finalize();
1014     Initialize();
1015   }
1016 }
1017
1018 } // namespace Adaptor
1019
1020 } // namespace Internal
1021
1022 } // namespace Dali