2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <imf-manager-impl.h>
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>
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 #include "ecore-virtual-keyboard.h"
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMF_MANAGER");
50 // Currently this code is internal to dali/dali/internal/event/text/utf8.h but should be made Public and used from there instead.
51 size_t Utf8SequenceLength(const unsigned char leadByte)
55 if ((leadByte & 0x80) == 0 ) //ASCII character (lead bit zero)
59 else if (( leadByte & 0xe0 ) == 0xc0 ) //110x xxxx
63 else if (( leadByte & 0xf0 ) == 0xe0 ) //1110 xxxx
67 else if (( leadByte & 0xf8 ) == 0xf0 ) //1111 0xxx
75 // Static function calls used by ecore 'c' style callback registration
76 void Commit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
80 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
81 imfManager->CommitReceived( data, imfContext, event_info );
85 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
89 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
90 imfManager->PreEditChanged( data, imfContext, event_info );
94 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
98 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
99 return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
108 * Called when an IMF delete surrounding event is received.
109 * Here we tell the application that it should delete a certain range.
111 void ImfDeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
115 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
116 imfManager->DeleteSurrounding( data, imfContext, event_info );
120 } // unnamed namespace
122 bool ImfManager::IsAvailable()
124 bool available( false );
126 Dali::SingletonService service( SingletonService::Get() );
129 available = service.GetSingleton( typeid( Dali::ImfManager ) );
135 Dali::ImfManager ImfManager::Get()
137 Dali::ImfManager manager;
139 Dali::SingletonService service( SingletonService::Get() );
142 // Check whether the singleton is already created
143 Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
146 // If so, downcast the handle
147 manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
149 else if ( Adaptor::IsAvailable() )
151 // Create instance and register singleton only if the adaptor is available
153 Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
154 Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
156 // The Ecore_X_Window needs to use the ImfManager.
157 // Only when the render surface is window, we can get the Ecore_X_Window.
158 Ecore_X_Window ecoreXwin( AnyCast<Ecore_X_Window>(nativeWindow) );
161 // If we fail to get Ecore_X_Window, we can't use the ImfManager correctly.
162 // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
163 // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
165 manager = Dali::ImfManager( new ImfManager( ecoreXwin ) );
166 service.Register( typeid( manager ), manager );
170 DALI_LOG_ERROR("Failed to get native window handle\n");
178 ImfManager::ImfManager( Ecore_X_Window ecoreXwin )
180 mIMFCursorPosition( 0 ),
182 mRestoreAfterFocusLost( false ),
183 mIdleCallbackConnected( false )
186 CreateContext( ecoreXwin );
189 VirtualKeyboard::ConnectCallbacks( mIMFContext );
192 ImfManager::~ImfManager()
194 VirtualKeyboard::DisconnectCallbacks( mIMFContext );
195 DisconnectCallbacks();
198 ecore_imf_shutdown();
201 void ImfManager::CreateContext( Ecore_X_Window ecoreXwin )
203 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
205 const char *contextId = ecore_imf_context_default_id_get();
208 mIMFContext = ecore_imf_context_add( contextId );
214 ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
219 DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
224 DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
228 void ImfManager::DeleteContext()
230 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
234 ecore_imf_context_del( mIMFContext );
239 // Callbacks for predicitive text support.
240 void ImfManager::ConnectCallbacks()
244 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
246 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit, this );
247 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit, this );
248 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
250 ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
254 void ImfManager::DisconnectCallbacks()
258 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
260 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit );
261 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit );
262 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
264 // We do not need to unset the retrieve surrounding callback.
268 void ImfManager::Activate()
270 // Reset mIdleCallbackConnected
271 mIdleCallbackConnected = false;
275 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
277 ecore_imf_context_focus_in( mIMFContext );
279 // emit keyboard activated signal
280 Dali::ImfManager handle( this );
281 mActivatedSignal.Emit( handle );
285 void ImfManager::Deactivate()
289 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
292 ecore_imf_context_focus_out( mIMFContext );
295 // Reset mIdleCallbackConnected
296 mIdleCallbackConnected = false;
299 void ImfManager::Reset()
301 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
305 ecore_imf_context_reset( mIMFContext );
309 Ecore_IMF_Context* ImfManager::GetContext()
311 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
316 bool ImfManager::RestoreAfterFocusLost() const
318 return mRestoreAfterFocusLost;
321 void ImfManager::SetRestoreAfterFocusLost( bool toggle )
323 mRestoreAfterFocusLost = toggle;
327 * Called when an IMF Pre-Edit changed event is received.
328 * We are still predicting what the user is typing. The latest string is what the IMF module thinks
329 * the user wants to type.
331 void ImfManager::PreEditChanged( void*, Ecore_IMF_Context* imfContext, void* event_info )
333 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
335 char* preEditString( NULL );
336 int cursorPosition( 0 );
337 Eina_List* attrs = NULL;
340 Ecore_IMF_Preedit_Attr* attr;
342 // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
343 // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
344 ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
348 // iterate through the list of attributes getting the type, start and end position.
349 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) ) ))
351 #ifdef DALI_PROFILE_UBUNTU
352 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
353 #else // DALI_PROFILE_UBUNTU
354 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
355 #endif // DALI_PROFILE_UBUNTU
357 // 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.
359 size_t visualCharacterIndex = 0;
360 size_t byteIndex = 0;
362 // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
363 const char leadByte = preEditString[byteIndex];
364 while( leadByte != '\0' )
366 // attr->end_index is provided as a byte position not character and we need to know the character position.
367 const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
368 if ( byteIndex == attr->end_index )
370 cursorPosition = visualCharacterIndex;
372 // end loop as found cursor position that matches byte position
376 byteIndex += currentSequenceLength; // jump to next character
377 visualCharacterIndex++; // increment character count so we know our position for when we get a match
380 DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
386 if ( Dali::Adaptor::IsAvailable() )
388 Dali::ImfManager handle( this );
389 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
390 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
392 if( callbackData.update )
394 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
396 NotifyCursorPosition();
399 if( callbackData.preeditResetRequired )
404 free( preEditString );
407 void ImfManager::CommitReceived( void*, Ecore_IMF_Context* imfContext, void* event_info )
409 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
411 if ( Dali::Adaptor::IsAvailable() )
413 const std::string keyString( static_cast<char*>( event_info ) );
415 Dali::ImfManager handle( this );
416 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
417 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
419 if( callbackData.update )
421 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
423 NotifyCursorPosition();
429 * Called when an IMF retrieve surround event is received.
430 * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
431 * We need to signal the application to tell us this information.
433 Eina_Bool ImfManager::RetrieveSurrounding( void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition )
435 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
437 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
438 Dali::ImfManager handle( this );
439 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
441 if( callbackData.update )
445 *text = strdup( callbackData.currentText.c_str() );
450 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
451 *cursorPosition = mIMFCursorPosition;
459 * Called when an IMF delete surrounding event is received.
460 * Here we tell the application that it should delete a certain range.
462 void ImfManager::DeleteSurrounding( void* data, Ecore_IMF_Context* imfContext, void* event_info )
464 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
466 if( Dali::Adaptor::IsAvailable() )
468 Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
470 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
471 Dali::ImfManager handle( this );
472 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
474 if( callbackData.update )
476 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
478 NotifyCursorPosition();
483 void ImfManager::NotifyCursorPosition()
485 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
489 ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
493 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
495 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
497 mIMFCursorPosition = static_cast<int>( cursorPosition );
500 unsigned int ImfManager::GetCursorPosition() const
502 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
504 return static_cast<unsigned int>( mIMFCursorPosition );
507 void ImfManager::SetSurroundingText( const std::string& text )
509 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
511 mSurroundingText = text;
514 const std::string& ImfManager::GetSurroundingText() const
516 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
518 return mSurroundingText;
521 void ImfManager::NotifyTextInputMultiLine( bool multiLine )
525 Dali::ImfManager::TextDirection ImfManager::GetTextDirection()
527 Dali::ImfManager::TextDirection direction ( Dali::ImfManager::LeftToRight );
529 if ( ImfManager::IsAvailable() /* We do not want to create an instance of ImfManager */ )
533 char* locale( NULL );
534 ecore_imf_context_input_panel_language_locale_get( mIMFContext, &locale );
538 direction = Locale::GetTextDirection( std::string( locale ) );
546 Rect<int> ImfManager::GetInputMethodArea()
548 int xPos, yPos, width, height;
550 width = height = xPos = yPos = 0;
554 ecore_imf_context_input_panel_geometry_get( mIMFContext, &xPos, &yPos, &width, &height );
558 DALI_LOG_WARNING("VKB Unable to get IMF Context so GetSize unavailable\n");
561 return Rect<int>(xPos,yPos,width,height);
564 void ImfManager::ApplyOptions( const InputMethodOptions& options )
566 using namespace Dali::InputMethod::Category;
570 if (mIMFContext == NULL)
572 DALI_LOG_WARNING("VKB Unable to excute ApplyOptions with Null ImfContext\n");
576 if ( mOptions.CompareAndSet(PANEL_LAYOUT, options, index) )
579 if ( mOptions.CompareAndSet(AUTO_CAPITALISE, options, index) )
582 if ( mOptions.CompareAndSet(ACTION_BUTTON_TITLE, options, index) )
585 if ( mOptions.CompareAndSet(VARIATION, options, index) )
590 void ImfManager::SetInputPanelUserData( const std::string& data )
592 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetInputPanelUserData\n" );
596 int length = data.length();
597 ecore_imf_context_input_panel_imdata_set( mIMFContext, &data, length );
601 void ImfManager::GetInputPanelUserData( std::string& data )
603 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelUserData\n" );
608 ecore_imf_context_input_panel_imdata_get( mIMFContext, &data, length );
612 Dali::ImfManager::State ImfManager::GetInputPanelState()
614 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelState\n" );
619 value = ecore_imf_context_input_panel_state_get( mIMFContext );
623 case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
625 return Dali::ImfManager::SHOW;
629 case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
631 return Dali::ImfManager::HIDE;
635 case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
637 return Dali::ImfManager::WILL_SHOW;
643 return Dali::ImfManager::DEFAULT;
647 return Dali::ImfManager::DEFAULT;
650 void ImfManager::SetReturnKeyState( bool visible )
652 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetReturnKeyState\n" );
656 ecore_imf_context_input_panel_return_key_disabled_set( mIMFContext, !visible );
660 void ImfManager::AutoEnableInputPanel( bool enabled )
662 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::AutoEnableInputPanel\n" );
666 ecore_imf_context_input_panel_enabled_set( mIMFContext, enabled );
670 void ImfManager::ShowInputPanel()
672 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ShowInputPanel\n" );
676 ecore_imf_context_input_panel_show( mIMFContext );
680 void ImfManager::HideInputPanel()
682 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::HideInputPanel\n" );
686 ecore_imf_context_input_panel_hide( mIMFContext );