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