1e598c223653c774d33d33b9848f428ac56e809f
[platform/core/uifw/dali-adaptor.git] / adaptors / x11 / imf-manager-impl-x.cpp
1 /*
2  * Copyright (c) 2017 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 <imf-manager-impl.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 <adaptor.h>
28 #include <window-render-surface.h>
29 #include <adaptor-impl.h>
30 #include <locale-utils.h>
31 #include <singleton-service-impl.h>
32 #include <virtual-keyboard-impl.h>
33 // Ecore is littered with C style cast
34 #pragma GCC diagnostic push
35 #pragma GCC diagnostic ignored "-Wold-style-cast"
36 #include "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_IMF_MANAGER");
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 *event_info )
80 {
81   if ( data )
82   {
83     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
84     imfManager->CommitReceived( data, imfContext, event_info );
85   }
86 }
87
88 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
89 {
90   if ( data )
91   {
92     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
93     imfManager->PreEditChanged( data, imfContext, event_info );
94   }
95 }
96
97 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
98 {
99   if ( data )
100   {
101     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
102     return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
103   }
104   else
105   {
106     return false;
107   }
108 }
109
110 /**
111  * Called when an IMF 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 *event_info )
115 {
116   if ( data )
117   {
118     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
119     imfManager->DeleteSurrounding( data, imfContext, event_info );
120   }
121 }
122
123 } // unnamed namespace
124
125 bool ImfManager::IsAvailable()
126 {
127   bool available( false );
128
129   Dali::SingletonService service( SingletonService::Get() );
130   if ( service )
131   {
132     available = service.GetSingleton( typeid( Dali::ImfManager ) );
133   }
134
135   return available;
136 }
137
138 Dali::ImfManager ImfManager::Get()
139 {
140   Dali::ImfManager manager;
141
142   Dali::SingletonService service( SingletonService::Get() );
143   if ( service )
144   {
145     // Check whether the singleton is already created
146     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
147     if( handle )
148     {
149       // If so, downcast the handle
150       manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
151     }
152     else if ( Adaptor::IsAvailable() )
153     {
154       // Create instance and register singleton only if the adaptor is available
155
156       Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
157       Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
158
159       // The Ecore_X_Window needs to use the ImfManager.
160       // Only when the render surface is window, we can get the Ecore_X_Window.
161       Ecore_X_Window ecoreXwin( AnyCast<Ecore_X_Window>(nativeWindow) );
162       if (ecoreXwin)
163       {
164         // If we fail to get Ecore_X_Window, we can't use the ImfManager correctly.
165         // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
166         // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
167
168         manager = Dali::ImfManager( new ImfManager( ecoreXwin ) );
169         service.Register( typeid( manager ), manager );
170       }
171       else
172       {
173         DALI_LOG_ERROR("Failed to get native window handle\n");
174       }
175     }
176   }
177
178   return manager;
179 }
180
181 ImfManager::ImfManager( Ecore_X_Window ecoreXwin )
182 : mIMFContext(),
183   mIMFCursorPosition( 0 ),
184   mSurroundingText(),
185   mRestoreAfterFocusLost( false ),
186   mIdleCallbackConnected( false )
187 {
188   ecore_imf_init();
189   CreateContext( ecoreXwin );
190
191   ConnectCallbacks();
192   VirtualKeyboard::ConnectCallbacks( mIMFContext );
193 }
194
195 ImfManager::~ImfManager()
196 {
197   VirtualKeyboard::DisconnectCallbacks( mIMFContext );
198   DisconnectCallbacks();
199
200   DeleteContext();
201   ecore_imf_shutdown();
202 }
203
204 void ImfManager::CreateContext( Ecore_X_Window ecoreXwin )
205 {
206   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
207
208   const char *contextId = ecore_imf_context_default_id_get();
209   if( contextId )
210   {
211     mIMFContext = ecore_imf_context_add( contextId );
212
213     if( mIMFContext )
214     {
215       if( ecoreXwin )
216       {
217         ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
218       }
219     }
220     else
221     {
222       DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
223     }
224   }
225   else
226   {
227     DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
228   }
229 }
230
231 void ImfManager::DeleteContext()
232 {
233   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
234
235   if ( mIMFContext )
236   {
237     ecore_imf_context_del( mIMFContext );
238     mIMFContext = NULL;
239   }
240 }
241
242 // Callbacks for predicitive text support.
243 void ImfManager::ConnectCallbacks()
244 {
245   if ( mIMFContext )
246   {
247     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
248
249     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit,    this );
250     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit,     this );
251     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
252
253     ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
254   }
255 }
256
257 void ImfManager::DisconnectCallbacks()
258 {
259   if ( mIMFContext )
260   {
261     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
262
263     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit );
264     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit );
265     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
266
267     // We do not need to unset the retrieve surrounding callback.
268   }
269 }
270
271 void ImfManager::Activate()
272 {
273   // Reset mIdleCallbackConnected
274   mIdleCallbackConnected = false;
275
276   if ( mIMFContext )
277   {
278     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
279
280     ecore_imf_context_focus_in( mIMFContext );
281
282     // emit keyboard activated signal
283     Dali::ImfManager handle( this );
284     mActivatedSignal.Emit( handle );
285   }
286 }
287
288 void ImfManager::Deactivate()
289 {
290   if( mIMFContext )
291   {
292     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
293
294     Reset();
295     ecore_imf_context_focus_out( mIMFContext );
296   }
297
298   // Reset mIdleCallbackConnected
299   mIdleCallbackConnected = false;
300 }
301
302 void ImfManager::Reset()
303 {
304   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
305
306   if ( mIMFContext )
307   {
308     ecore_imf_context_reset( mIMFContext );
309   }
310 }
311
312 Ecore_IMF_Context* ImfManager::GetContext()
313 {
314   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
315
316   return mIMFContext;
317 }
318
319 bool ImfManager::RestoreAfterFocusLost() const
320 {
321   return mRestoreAfterFocusLost;
322 }
323
324 void ImfManager::SetRestoreAfterFocusLost( bool toggle )
325 {
326   mRestoreAfterFocusLost = toggle;
327 }
328
329 /**
330  * Called when an IMF Pre-Edit changed event is received.
331  * We are still predicting what the user is typing.  The latest string is what the IMF module thinks
332  * the user wants to type.
333  */
334 void ImfManager::PreEditChanged( void*, Ecore_IMF_Context* imfContext, void* event_info )
335 {
336   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
337
338   char* preEditString( NULL );
339   int cursorPosition( 0 );
340   Eina_List* attrs = NULL;
341   Eina_List* l = NULL;
342
343   Ecore_IMF_Preedit_Attr* attr;
344
345   // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
346   // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
347   ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
348
349   if ( attrs )
350   {
351     // iterate through the list of attributes getting the type, start and end position.
352     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) ) ))
353     {
354 #ifdef DALI_PROFILE_UBUNTU
355       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
356 #else // DALI_PROFILE_UBUNTU
357       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
358 #endif // DALI_PROFILE_UBUNTU
359       {
360         // 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.
361
362         size_t visualCharacterIndex = 0;
363         size_t byteIndex = 0;
364
365         // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
366         const char leadByte = preEditString[byteIndex];
367         while( leadByte != '\0' )
368         {
369           // attr->end_index is provided as a byte position not character and we need to know the character position.
370           const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
371           if ( byteIndex == attr->end_index )
372           {
373             cursorPosition = visualCharacterIndex;
374             break;
375             // end loop as found cursor position that matches byte position
376           }
377           else
378           {
379             byteIndex += currentSequenceLength; // jump to next character
380             visualCharacterIndex++;  // increment character count so we know our position for when we get a match
381           }
382
383           DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
384         }
385       }
386     }
387   }
388
389   if ( Dali::Adaptor::IsAvailable() )
390   {
391     Dali::ImfManager handle( this );
392     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
393     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
394
395     if( callbackData.update )
396     {
397       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
398
399       NotifyCursorPosition();
400     }
401
402     if( callbackData.preeditResetRequired )
403     {
404       Reset();
405     }
406   }
407   free( preEditString );
408 }
409
410 void ImfManager::CommitReceived( void*, Ecore_IMF_Context* imfContext, void* event_info )
411 {
412   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
413
414   if ( Dali::Adaptor::IsAvailable() )
415   {
416     const std::string keyString( static_cast<char*>( event_info ) );
417
418     Dali::ImfManager handle( this );
419     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
420     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
421
422     if( callbackData.update )
423     {
424       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
425
426       NotifyCursorPosition();
427     }
428   }
429 }
430
431 /**
432  * Called when an IMF retrieve surround event is received.
433  * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
434  * We need to signal the application to tell us this information.
435  */
436 Eina_Bool ImfManager::RetrieveSurrounding( void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition )
437 {
438   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
439
440   Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
441   Dali::ImfManager handle( this );
442   Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
443
444   if( callbackData.update )
445   {
446     if( text )
447     {
448       *text = strdup( callbackData.currentText.c_str() );
449     }
450
451     if( cursorPosition )
452     {
453       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
454       *cursorPosition = mIMFCursorPosition;
455     }
456   }
457
458   return EINA_TRUE;
459 }
460
461 /**
462  * Called when an IMF delete surrounding event is received.
463  * Here we tell the application that it should delete a certain range.
464  */
465 void ImfManager::DeleteSurrounding( void* data, Ecore_IMF_Context* imfContext, void* event_info )
466 {
467   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
468
469   if( Dali::Adaptor::IsAvailable() )
470   {
471     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
472
473     Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
474     Dali::ImfManager handle( this );
475     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
476
477     if( callbackData.update )
478     {
479       mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
480
481       NotifyCursorPosition();
482     }
483   }
484 }
485
486 void ImfManager::NotifyCursorPosition()
487 {
488   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
489
490   if( mIMFContext )
491   {
492     ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
493   }
494 }
495
496 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
497 {
498   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
499
500   mIMFCursorPosition = static_cast<int>( cursorPosition );
501 }
502
503 unsigned int ImfManager::GetCursorPosition() const
504 {
505   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
506
507   return static_cast<unsigned int>( mIMFCursorPosition );
508 }
509
510 void ImfManager::SetSurroundingText( const std::string& text )
511 {
512   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
513
514   mSurroundingText = text;
515 }
516
517 const std::string& ImfManager::GetSurroundingText() const
518 {
519   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
520
521   return mSurroundingText;
522 }
523
524 void ImfManager::NotifyTextInputMultiLine( bool multiLine )
525 {
526 }
527
528 Dali::ImfManager::TextDirection ImfManager::GetTextDirection()
529 {
530   Dali::ImfManager::TextDirection direction ( Dali::ImfManager::LeftToRight );
531
532   if ( ImfManager::IsAvailable() /* We do not want to create an instance of ImfManager */ )
533   {
534     if ( mIMFContext )
535     {
536       char* locale( NULL );
537       ecore_imf_context_input_panel_language_locale_get( mIMFContext, &locale );
538
539       if ( locale )
540       {
541         direction = Locale::GetTextDirection( std::string( locale ) );
542         free( locale );
543       }
544     }
545   }
546   return direction;
547 }
548
549 Rect<int> ImfManager::GetInputMethodArea()
550 {
551   int xPos, yPos, width, height;
552
553   width = height = xPos = yPos = 0;
554
555   if( mIMFContext )
556   {
557     ecore_imf_context_input_panel_geometry_get( mIMFContext, &xPos, &yPos, &width, &height );
558   }
559   else
560   {
561     DALI_LOG_WARNING("VKB Unable to get IMF Context so GetSize unavailable\n");
562   }
563
564   return Rect<int>(xPos,yPos,width,height);
565 }
566
567 void ImfManager::ApplyOptions( const InputMethodOptions& options )
568 {
569   using namespace Dali::InputMethod::Category;
570
571   int index;
572
573   if (mIMFContext == NULL)
574   {
575     DALI_LOG_WARNING("VKB Unable to excute ApplyOptions with Null ImfContext\n");
576     return;
577   }
578
579   if ( mOptions.CompareAndSet(PANEL_LAYOUT, options, index) )
580   {
581   }
582   if ( mOptions.CompareAndSet(AUTO_CAPITALISE, options, index) )
583   {
584   }
585   if ( mOptions.CompareAndSet(ACTION_BUTTON_TITLE, options, index) )
586   {
587   }
588   if ( mOptions.CompareAndSet(VARIATION, options, index) )
589   {
590   }
591 }
592
593 void ImfManager::SetInputPanelUserData( const std::string& data )
594 {
595   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetInputPanelUserData\n" );
596
597   if( mIMFContext )
598   {
599     int length = data.length();
600     ecore_imf_context_input_panel_imdata_set( mIMFContext, &data, length );
601   }
602 }
603
604 void ImfManager::GetInputPanelUserData( std::string& data )
605 {
606   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelUserData\n" );
607
608   if( mIMFContext )
609   {
610     int* length = NULL;
611     ecore_imf_context_input_panel_imdata_get( mIMFContext, &data, length );
612   }
613 }
614
615 Dali::ImfManager::State ImfManager::GetInputPanelState()
616 {
617   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelState\n" );
618
619   if( mIMFContext )
620   {
621     int value;
622     value = ecore_imf_context_input_panel_state_get( mIMFContext );
623
624     switch (value)
625     {
626       case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
627       {
628         return Dali::ImfManager::SHOW;
629         break;
630       }
631
632       case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
633       {
634         return Dali::ImfManager::HIDE;
635         break;
636       }
637
638       case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
639       {
640         return Dali::ImfManager::WILL_SHOW;
641         break;
642       }
643
644       default:
645       {
646         return Dali::ImfManager::DEFAULT;
647       }
648     }
649   }
650   return Dali::ImfManager::DEFAULT;
651 }
652
653 void ImfManager::SetReturnKeyState( bool visible )
654 {
655   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetReturnKeyState\n" );
656
657   if( mIMFContext )
658   {
659     ecore_imf_context_input_panel_return_key_disabled_set( mIMFContext, !visible );
660   }
661 }
662
663 void ImfManager::AutoEnableInputPanel( bool enabled )
664 {
665   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::AutoEnableInputPanel\n" );
666
667   if( mIMFContext )
668   {
669     ecore_imf_context_input_panel_enabled_set( mIMFContext, enabled );
670   }
671 }
672
673 void ImfManager::ShowInputPanel()
674 {
675   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ShowInputPanel\n" );
676
677   if( mIMFContext )
678   {
679     ecore_imf_context_input_panel_show( mIMFContext );
680   }
681 }
682
683 void ImfManager::HideInputPanel()
684 {
685   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::HideInputPanel\n" );
686
687   if( mIMFContext )
688   {
689     ecore_imf_context_input_panel_hide( mIMFContext );
690   }
691 }
692
693 } // Adaptor
694
695 } // Internal
696
697 } // Dali
698
699 #pragma GCC diagnostic pop