DALi Version 1.9.0
[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/public-api/events/key-event.h>
23 #include <dali/public-api/object/type-registry.h>
24 #include <dali/integration-api/debug.h>
25
26 // INTERNAL INCLUDES
27 #include <dali/public-api/adaptor-framework/key.h>
28 #include <dali/integration-api/adaptor-framework/adaptor.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/internal/input/common/key-impl.h>
31 #include <dali/internal/input/common/virtual-keyboard-impl.h>
32 #include <dali/internal/input/linux/dali-ecore-imf.h>
33 #include <dali/internal/input/tizen-wayland/ecore-virtual-keyboard.h>
34 #include <dali/internal/input/ubuntu-x11/dali-ecore-input.h>
35 #include <dali/internal/system/common/locale-utils.h>
36 #include <dali/internal/system/common/singleton-service-impl.h>
37 #include <dali/internal/system/linux/dali-ecore.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   mPreeditAttrs.Clear();
317
318   // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
319   // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
320   ecore_imf_context_preedit_string_with_attributes_get( context, &preEditString, &attrs, &cursorPosition );
321
322   if ( attrs )
323   {
324     // iterate through the list of attributes getting the type, start and end position.
325     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) ) ))
326     {
327       Dali::InputMethodContext::PreeditAttributeData data;
328       data.startIndex = 0;
329       data.endIndex = 0;
330
331       size_t visualCharacterIndex = 0;
332       size_t byteIndex = 0;
333
334       // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
335       const char leadByte = preEditString[byteIndex];
336       while( leadByte != '\0' )
337       {
338         // attr->end_index is provided as a byte position not character and we need to know the character position.
339         const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
340         if( byteIndex <= attr->start_index )
341         {
342            data.startIndex = visualCharacterIndex;
343         }
344         if ( byteIndex >= attr->end_index )
345         {
346           data.endIndex = visualCharacterIndex;
347           break;
348           // end loop as found cursor position that matches byte position
349         }
350         else
351         {
352           byteIndex += currentSequenceLength; // jump to next character
353           visualCharacterIndex++;  // increment character count so we know our position for when we get a match
354         }
355       }
356
357       switch( attr->preedit_type )
358       {
359         case ECORE_IMF_PREEDIT_TYPE_NONE:
360         {
361           data.preeditType = Dali::InputMethodContext::PreeditStyle::NONE;
362           break;
363         }
364         case ECORE_IMF_PREEDIT_TYPE_SUB1:
365         {
366           data.preeditType = Dali::InputMethodContext::PreeditStyle::UNDERLINE;
367           break;
368         }
369         case ECORE_IMF_PREEDIT_TYPE_SUB2:
370         {
371           data.preeditType = Dali::InputMethodContext::PreeditStyle::REVERSE;
372           break;
373         }
374         case ECORE_IMF_PREEDIT_TYPE_SUB3:
375         {
376           data.preeditType = Dali::InputMethodContext::PreeditStyle::HIGHLIGHT;
377           break;
378         }
379         case ECORE_IMF_PREEDIT_TYPE_SUB4:
380         {
381           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1;
382           break;
383         }
384         case ECORE_IMF_PREEDIT_TYPE_SUB5:
385         {
386           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2;
387           break;
388         }
389         case ECORE_IMF_PREEDIT_TYPE_SUB6:
390         {
391           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3;
392           break;
393         }
394         case ECORE_IMF_PREEDIT_TYPE_SUB7:
395         {
396           data.preeditType = Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4;
397           break;
398         }
399         default:
400         {
401           data.preeditType = Dali::InputMethodContext::PreeditStyle::NONE;
402           break;
403         }
404       }
405       mPreeditAttrs.PushBack( data );
406     }
407   }
408
409   if ( Dali::Adaptor::IsAvailable() )
410   {
411     Dali::InputMethodContext handle( this );
412     Dali::InputMethodContext::EventData eventData( Dali::InputMethodContext::PRE_EDIT, preEditString, cursorPosition, 0 );
413     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit( handle, eventData );
414
415     if( callbackData.update )
416     {
417       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
418
419       NotifyCursorPosition();
420     }
421
422     if( callbackData.preeditResetRequired )
423     {
424       Reset();
425     }
426   }
427   free( preEditString );
428 }
429
430 void InputMethodContextX::CommitReceived( void*, ImfContext* imfContext, void* eventInfo )
431 {
432   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::CommitReceived\n" );
433
434   if ( Dali::Adaptor::IsAvailable() )
435   {
436     const std::string keyString( static_cast<char*>( eventInfo ) );
437
438     Dali::InputMethodContext handle( this );
439     Dali::InputMethodContext::EventData eventData( Dali::InputMethodContext::COMMIT, keyString, 0, 0 );
440     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit( handle, eventData );
441
442     if( callbackData.update )
443     {
444       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
445
446       NotifyCursorPosition();
447     }
448   }
449 }
450
451 /**
452  * Called when an InputMethodContext retrieve surround event is received.
453  * Here the InputMethodContext module wishes to know the string we are working with and where within the string the cursor is
454  * We need to signal the application to tell us this information.
455  */
456 bool InputMethodContextX::RetrieveSurrounding( void* data, ImfContext* imfContext, char** text, int* cursorPosition )
457 {
458   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::RetrieveSurrounding\n" );
459
460   Dali::InputMethodContext::EventData imfData( Dali::InputMethodContext::GET_SURROUNDING, std::string(), 0, 0 );
461   Dali::InputMethodContext handle( this );
462   Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit( handle, imfData );
463
464   if( callbackData.update )
465   {
466     if( text )
467     {
468       *text = strdup( callbackData.currentText.c_str() );
469     }
470
471     if( cursorPosition )
472     {
473       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
474       *cursorPosition = mIMFCursorPosition;
475     }
476   }
477
478   return EINA_TRUE;
479 }
480
481 /**
482  * Called when an InputMethodContext delete surrounding event is received.
483  * Here we tell the application that it should delete a certain range.
484  */
485 void InputMethodContextX::DeleteSurrounding( void* data, ImfContext* imfContext, void* eventInfo )
486 {
487   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::DeleteSurrounding\n" );
488
489   if( Dali::Adaptor::IsAvailable() )
490   {
491     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( eventInfo );
492
493     Dali::InputMethodContext::EventData imfData( Dali::InputMethodContext::DELETE_SURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
494     Dali::InputMethodContext handle( this );
495     Dali::InputMethodContext::CallbackData callbackData = mEventSignal.Emit( handle, imfData );
496
497     if( callbackData.update )
498     {
499       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
500
501       NotifyCursorPosition();
502     }
503   }
504 }
505
506 void InputMethodContextX::NotifyCursorPosition()
507 {
508   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::NotifyCursorPosition\n" );
509
510   if( mIMFContext )
511   {
512     ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
513   }
514 }
515
516 void InputMethodContextX::SetCursorPosition( unsigned int cursorPosition )
517 {
518   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetCursorPosition\n" );
519
520   mIMFCursorPosition = static_cast<int>( cursorPosition );
521 }
522
523 unsigned int InputMethodContextX::GetCursorPosition() const
524 {
525   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetCursorPosition\n" );
526
527   return static_cast<unsigned int>( mIMFCursorPosition );
528 }
529
530 void InputMethodContextX::SetSurroundingText( const std::string& text )
531 {
532   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetSurroundingText\n" );
533
534   mSurroundingText = text;
535 }
536
537 const std::string& InputMethodContextX::GetSurroundingText() const
538 {
539   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetSurroundingText\n" );
540
541   return mSurroundingText;
542 }
543
544 void InputMethodContextX::NotifyTextInputMultiLine( bool multiLine )
545 {
546 }
547
548 Dali::InputMethodContext::TextDirection InputMethodContextX::GetTextDirection()
549 {
550   Dali::InputMethodContext::TextDirection direction ( Dali::InputMethodContext::LeftToRight );
551
552     if ( mIMFContext )
553     {
554       char* locale( NULL );
555       ecore_imf_context_input_panel_language_locale_get( mIMFContext, &locale );
556
557       if ( locale )
558       {
559         direction = static_cast< Dali::InputMethodContext::TextDirection >( Locale::GetDirection( std::string( locale ) ) );
560         free( locale );
561       }
562     }
563
564   return direction;
565 }
566
567 Rect<int> InputMethodContextX::GetInputMethodArea()
568 {
569   int xPos, yPos, width, height;
570
571   width = height = xPos = yPos = 0;
572
573   if( mIMFContext )
574   {
575     ecore_imf_context_input_panel_geometry_get( mIMFContext, &xPos, &yPos, &width, &height );
576   }
577   else
578   {
579     DALI_LOG_WARNING("VKB Unable to get InputMethodContext Context so GetSize unavailable\n");
580   }
581
582   return Rect<int>(xPos,yPos,width,height);
583 }
584
585 void InputMethodContextX::ApplyOptions( const InputMethodOptions& options )
586 {
587   using namespace Dali::InputMethod::Category;
588
589   int index;
590
591   if (mIMFContext == NULL)
592   {
593     DALI_LOG_WARNING("VKB Unable to excute ApplyOptions with Null ImfContext\n");
594     return;
595   }
596
597   if ( mOptions.CompareAndSet(PANEL_LAYOUT, options, index) )
598   {
599   }
600   if ( mOptions.CompareAndSet(BUTTON_ACTION, options, index) )
601   {
602   }
603   if ( mOptions.CompareAndSet(AUTO_CAPITALIZE, options, index) )
604   {
605   }
606   if ( mOptions.CompareAndSet(VARIATION, options, index) )
607   {
608   }
609 }
610
611 void InputMethodContextX::SetInputPanelData( const std::string& data )
612 {
613   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelData\n" );
614
615   if( mIMFContext )
616   {
617     int length = data.length();
618     ecore_imf_context_input_panel_imdata_set( mIMFContext, data.c_str(), length );
619   }
620 }
621
622 void InputMethodContextX::GetInputPanelData( std::string& data )
623 {
624   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelData\n" );
625
626   if( mIMFContext )
627   {
628     int length = 4096; // The max length is 4096 bytes
629     Dali::Vector< char > buffer;
630     buffer.Resize( length );
631     ecore_imf_context_input_panel_imdata_get( mIMFContext, &buffer[0], &length );
632     data = std::string( buffer.Begin(), buffer.End() );
633   }
634 }
635
636 Dali::InputMethodContext::State InputMethodContextX::GetInputPanelState()
637 {
638   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelState\n" );
639
640   if( mIMFContext )
641   {
642     int value;
643     value = ecore_imf_context_input_panel_state_get( mIMFContext );
644
645     switch (value)
646     {
647       case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
648       {
649         return Dali::InputMethodContext::SHOW;
650         break;
651       }
652
653       case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
654       {
655         return Dali::InputMethodContext::HIDE;
656         break;
657       }
658
659       case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
660       {
661         return Dali::InputMethodContext::WILL_SHOW;
662         break;
663       }
664
665       default:
666       {
667         return Dali::InputMethodContext::DEFAULT;
668       }
669     }
670   }
671   return Dali::InputMethodContext::DEFAULT;
672 }
673
674 void InputMethodContextX::SetReturnKeyState( bool visible )
675 {
676   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetReturnKeyState\n" );
677
678   if( mIMFContext )
679   {
680     ecore_imf_context_input_panel_return_key_disabled_set( mIMFContext, !visible );
681   }
682 }
683
684 void InputMethodContextX::AutoEnableInputPanel( bool enabled )
685 {
686   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::AutoEnableInputPanel\n" );
687
688   if( mIMFContext )
689   {
690     ecore_imf_context_input_panel_enabled_set( mIMFContext, enabled );
691   }
692 }
693
694 void InputMethodContextX::ShowInputPanel()
695 {
696   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::ShowInputPanel\n" );
697
698   if( mIMFContext )
699   {
700     ecore_imf_context_input_panel_show( mIMFContext );
701   }
702 }
703
704 void InputMethodContextX::HideInputPanel()
705 {
706   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::HideInputPanel\n" );
707
708   if( mIMFContext )
709   {
710     ecore_imf_context_input_panel_hide( mIMFContext );
711   }
712 }
713
714 Dali::InputMethodContext::KeyboardType InputMethodContextX::GetKeyboardType()
715 {
716   return Dali::InputMethodContext::KeyboardType::SOFTWARE_KEYBOARD;
717 }
718
719 std::string InputMethodContextX::GetInputPanelLocale()
720 {
721   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelLocale\n" );
722
723   std::string locale = "";
724
725   if( mIMFContext )
726   {
727     char* value = NULL;
728     ecore_imf_context_input_panel_language_locale_get( mIMFContext, &value );
729
730     if( value )
731     {
732       std::string valueCopy( value );
733       locale = valueCopy;
734
735       // The locale string retrieved must be freed with free().
736       free( value );
737     }
738   }
739   return locale;
740 }
741
742 void InputMethodContextX::SetContentMIMETypes( const std::string& mimeTypes )
743 {
744   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetContentMIMETypes\n" );
745   // ecore_imf_context_mime_type_accept_set() is supported from ecore-imf 1.20.0 version.
746 }
747
748 bool InputMethodContextX::FilterEventKey( const Dali::KeyEvent& keyEvent )
749 {
750   bool eventHandled( false );
751
752   // If a device key then skip ecore_imf_context_filter_event.
753   if ( ! KeyLookup::IsDeviceButton( keyEvent.keyPressedName.c_str() ))
754   {
755     //check whether it's key down or key up event
756     if ( keyEvent.state == KeyEvent::Down )
757     {
758       eventHandled = ProcessEventKeyDown( keyEvent );
759     }
760     else if ( keyEvent.state == KeyEvent::Up )
761     {
762       eventHandled = ProcessEventKeyUp( keyEvent );
763     }
764   }
765
766   return eventHandled;
767 }
768
769 void InputMethodContextX::AllowTextPrediction( bool prediction )
770 {
771   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::AllowTextPrediction\n" );
772
773   if( mIMFContext )
774   {
775     ecore_imf_context_prediction_allow_set( mIMFContext, prediction );
776   }
777 }
778
779 bool InputMethodContextX::IsTextPredictionAllowed() const
780 {
781   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::IsTextPredictionAllowed\n" );
782   bool prediction = false;
783   if( mIMFContext )
784   {
785     prediction = ecore_imf_context_prediction_allow_get( mIMFContext );
786   }
787   return prediction;
788 }
789
790 void InputMethodContextX::SetInputPanelLanguage( Dali::InputMethodContext::InputPanelLanguage language )
791 {
792   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelLanguage\n" );
793   if( mIMFContext )
794   {
795     switch (language)
796     {
797       case Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC:
798       {
799         ecore_imf_context_input_panel_language_set( mIMFContext, ECORE_IMF_INPUT_PANEL_LANG_AUTOMATIC );
800         break;
801       }
802       case Dali::InputMethodContext::InputPanelLanguage::ALPHABET:
803       {
804         ecore_imf_context_input_panel_language_set( mIMFContext, ECORE_IMF_INPUT_PANEL_LANG_ALPHABET );
805         break;
806       }
807     }
808   }
809 }
810
811 Dali::InputMethodContext::InputPanelLanguage InputMethodContextX::GetInputPanelLanguage() const
812 {
813   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetInputPanelLanguage\n" );
814   if( mIMFContext )
815   {
816     int value;
817     value =  ecore_imf_context_input_panel_language_get( mIMFContext );
818
819     switch (value)
820     {
821       case ECORE_IMF_INPUT_PANEL_LANG_AUTOMATIC:
822       {
823         return Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC;
824         break;
825       }
826       case ECORE_IMF_INPUT_PANEL_LANG_ALPHABET:
827       {
828         return Dali::InputMethodContext::InputPanelLanguage::ALPHABET;
829         break;
830       }
831     }
832   }
833   return Dali::InputMethodContext::InputPanelLanguage::AUTOMATIC;
834 }
835
836 void InputMethodContextX::SetInputPanelPosition( unsigned int x, unsigned int y )
837 {
838   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::SetInputPanelPosition\n" );
839
840   // ecore_imf_context_input_panel_position_set() is supported from ecore-imf 1.21.0 version.
841 }
842
843 void InputMethodContextX::GetPreeditStyle( Vector< Dali::InputMethodContext::PreeditAttributeData >& attrs ) const
844 {
845   DALI_LOG_INFO( gLogFilter, Debug::General, "InputMethodContextX::GetPreeditStyle\n" );
846   attrs = mPreeditAttrs;
847 }
848
849 bool InputMethodContextX::ProcessEventKeyDown( const KeyEvent& keyEvent )
850 {
851   bool eventHandled( false );
852   if ( mIMFContext )
853   {
854     Integration::KeyEvent integKeyEvent( keyEvent );
855     std::string key = integKeyEvent.logicalKey;
856
857     std::string compose = keyEvent.GetCompose();
858
859     // We're consuming key down event so we have to pass to InputMethodContext so that it can parse it as well.
860     Ecore_IMF_Event_Key_Down ecoreKeyDownEvent;
861     ecoreKeyDownEvent.keyname = keyEvent.keyPressedName.c_str();
862     ecoreKeyDownEvent.key = key.c_str();
863     ecoreKeyDownEvent.string = keyEvent.keyPressed.c_str();
864     ecoreKeyDownEvent.compose = compose.c_str();
865     ecoreKeyDownEvent.timestamp = keyEvent.time;
866     ecoreKeyDownEvent.modifiers = EcoreInputModifierToEcoreIMFModifier( keyEvent.keyModifier );
867     ecoreKeyDownEvent.locks = EcoreInputModifierToEcoreIMFLock( keyEvent.keyModifier );
868
869 #if defined(ECORE_VERSION_MAJOR) && (ECORE_VERSION_MAJOR >= 1) && defined(ECORE_VERSION_MINOR)
870 #if (ECORE_VERSION_MINOR >= 14)
871     ecoreKeyDownEvent.dev_name  = "";
872     ecoreKeyDownEvent.dev_class = ECORE_IMF_DEVICE_CLASS_KEYBOARD;
873     ecoreKeyDownEvent.dev_subclass = ECORE_IMF_DEVICE_SUBCLASS_NONE;
874 #endif // Since ecore_imf 1.14 version
875 #if (ECORE_VERSION_MINOR >= 22)
876     ecoreKeyDownEvent.keycode = keyEvent.keyCode;
877 #endif // Since ecore_imf 1.22 version
878 #endif // Since ecore_imf Version 1
879
880     // 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.
881     if ((keyEvent.GetDeviceName() == "ime") && ((!strncmp(keyEvent.keyPressedName.c_str(), "Left", 4)) ||
882                                    (!strncmp(keyEvent.keyPressedName.c_str(), "Right", 5)) ||
883                                    (!strncmp(keyEvent.keyPressedName.c_str(), "Up", 2)) ||
884                                    (!strncmp(keyEvent.keyPressedName.c_str(), "Down", 4))))
885     {
886       eventHandled = 0;
887     }
888     else
889     {
890       eventHandled = ecore_imf_context_filter_event(mIMFContext,
891                                                     ECORE_IMF_EVENT_KEY_DOWN,
892                                                     reinterpret_cast<Ecore_IMF_Event *>( &ecoreKeyDownEvent ));
893     }
894
895     // If the event has not been handled by InputMethodContext then check if we should reset our IMFcontext
896     if (!eventHandled)
897     {
898       if (!strcmp(keyEvent.keyPressedName.c_str(), "Escape") ||
899           !strcmp(keyEvent.keyPressedName.c_str(), "Return") ||
900           !strcmp(keyEvent.keyPressedName.c_str(), "KP_Enter"))
901       {
902         ecore_imf_context_reset(mIMFContext);
903       }
904     }
905   }
906   return eventHandled;
907 }
908
909 bool InputMethodContextX::ProcessEventKeyUp( const KeyEvent& keyEvent )
910 {
911   bool eventHandled( false );
912   if( mIMFContext )
913   {
914     Integration::KeyEvent integKeyEvent( keyEvent );
915     std::string key = integKeyEvent.logicalKey;
916
917     std::string compose = keyEvent.GetCompose();
918
919     // We're consuming key up event so we have to pass to InputMethodContext so that it can parse it as well.
920     Ecore_IMF_Event_Key_Up ecoreKeyUpEvent;
921     ecoreKeyUpEvent.keyname = keyEvent.keyPressedName.c_str();
922     ecoreKeyUpEvent.key = key.c_str();
923     ecoreKeyUpEvent.string = keyEvent.keyPressed.c_str();
924     ecoreKeyUpEvent.compose = compose.c_str();
925     ecoreKeyUpEvent.timestamp = keyEvent.time;
926     ecoreKeyUpEvent.modifiers = EcoreInputModifierToEcoreIMFModifier( keyEvent.keyModifier );
927     ecoreKeyUpEvent.locks = EcoreInputModifierToEcoreIMFLock( keyEvent.keyModifier );
928 #if defined(ECORE_VERSION_MAJOR) && (ECORE_VERSION_MAJOR >= 1) && defined(ECORE_VERSION_MINOR)
929 #if (ECORE_VERSION_MINOR >= 14)
930     ecoreKeyUpEvent.dev_name  = "";
931 #endif // Since ecore_imf 1.14 version
932 #if (ECORE_VERSION_MINOR >= 22)
933     ecoreKeyUpEvent.keycode = keyEvent.keyCode;
934 #endif // Since ecore_imf 1.22 version
935 #endif // Since ecore_imf Version 1
936
937     eventHandled = ecore_imf_context_filter_event(mIMFContext,
938                                                   ECORE_IMF_EVENT_KEY_UP,
939                                                   reinterpret_cast<Ecore_IMF_Event *>( &ecoreKeyUpEvent ));
940   }
941   return eventHandled;
942 }
943
944 Ecore_IMF_Keyboard_Modifiers InputMethodContextX::EcoreInputModifierToEcoreIMFModifier( unsigned int ecoreModifier )
945 {
946   unsigned int modifier( ECORE_IMF_KEYBOARD_MODIFIER_NONE );  // If no other matches returns NONE.
947
948   if ( ecoreModifier & ECORE_EVENT_MODIFIER_SHIFT )  // enums from ecore_input/Ecore_Input.h
949   {
950     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_SHIFT;  // enums from ecore_imf/ecore_imf.h
951   }
952
953   if ( ecoreModifier & ECORE_EVENT_MODIFIER_ALT )
954   {
955     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_ALT;
956   }
957
958   if ( ecoreModifier & ECORE_EVENT_MODIFIER_CTRL )
959   {
960     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_CTRL;
961   }
962
963   if ( ecoreModifier & ECORE_EVENT_MODIFIER_WIN )
964   {
965     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_WIN;
966   }
967
968   if ( ecoreModifier & ECORE_EVENT_MODIFIER_ALTGR )
969   {
970     modifier |= ECORE_IMF_KEYBOARD_MODIFIER_ALTGR;
971   }
972
973   return static_cast<Ecore_IMF_Keyboard_Modifiers>( modifier );
974 }
975
976 Ecore_IMF_Keyboard_Locks InputMethodContextX::EcoreInputModifierToEcoreIMFLock( unsigned int modifier )
977 {
978     unsigned int lock( ECORE_IMF_KEYBOARD_LOCK_NONE ); // If no other matches, returns NONE.
979
980     if( modifier & ECORE_EVENT_LOCK_NUM )
981     {
982       lock |= ECORE_IMF_KEYBOARD_LOCK_NUM; // Num lock is active.
983     }
984
985     if( modifier & ECORE_EVENT_LOCK_CAPS )
986     {
987       lock |= ECORE_IMF_KEYBOARD_LOCK_CAPS; // Caps lock is active.
988     }
989
990     if( modifier & ECORE_EVENT_LOCK_SCROLL )
991     {
992       lock |= ECORE_IMF_KEYBOARD_LOCK_SCROLL; // Scroll lock is active.
993     }
994
995     return static_cast<Ecore_IMF_Keyboard_Locks>( lock );
996 }
997
998 void InputMethodContextX::OnStaged( Dali::Actor actor )
999 {
1000   Ecore_X_Window ecoreXwin( AnyCast< Ecore_X_Window >( Dali::Integration::SceneHolder::Get( actor ).GetNativeHandle() ) );
1001
1002   if( mEcoreXwin != ecoreXwin )
1003   {
1004     mEcoreXwin = ecoreXwin;
1005
1006     // Reset
1007     Finalize();
1008     Initialize();
1009   }
1010 }
1011
1012 } // Adaptor
1013
1014 } // Internal
1015
1016 } // Dali