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