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