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