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