2 * Copyright (c) 2014 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 <singleton-service-impl.h>
31 #include <virtual-keyboard-impl.h>
32 #include "ecore-virtual-keyboard.h"
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMF_MANAGER");
49 // Currently this code is internal to dali/dali/internal/event/text/utf8.h but should be made Public and used from there instead.
50 size_t Utf8SequenceLength(const unsigned char leadByte)
54 if ((leadByte & 0x80) == 0 ) //ASCII character (lead bit zero)
58 else if (( leadByte & 0xe0 ) == 0xc0 ) //110x xxxx
62 else if (( leadByte & 0xf0 ) == 0xe0 ) //1110 xxxx
66 else if (( leadByte & 0xf8 ) == 0xf0 ) //1111 0xxx
74 // Static function calls used by ecore 'c' style callback registration
75 void Commit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
79 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
80 imfManager->CommitReceived( data, imfContext, event_info );
84 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
88 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
89 imfManager->PreEditChanged( data, imfContext, event_info );
93 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
97 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
98 return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
107 * Called when an IMF delete surrounding event is received.
108 * Here we tell the application that it should delete a certain range.
110 void ImfDeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
114 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
115 imfManager->DeleteSurrounding( data, imfContext, event_info );
121 return ImfManager::Get();
124 TypeRegistration IMF_MANAGER_TYPE( typeid(Dali::ImfManager), typeid(Dali::BaseHandle), Create );
126 } // unnamed namespace
128 bool ImfManager::IsAvailable()
130 bool available( false );
132 Dali::SingletonService service( SingletonService::Get() );
135 available = service.GetSingleton( typeid( Dali::ImfManager ) );
141 Dali::ImfManager ImfManager::Get()
143 Dali::ImfManager manager;
145 Dali::SingletonService service( SingletonService::Get() );
148 // Check whether the singleton is already created
149 Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
152 // If so, downcast the handle
153 manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
155 else if ( Adaptor::IsAvailable() )
157 // Create instance and register singleton only if the adaptor is available
159 Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
160 Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
162 // The Ecore_Wl_Window needs to use the ImfManager.
163 // Only when the render surface is window, we can get the Ecore_Wl_Window.
164 Ecore_Wl_Window *ecoreWwin( AnyCast< Ecore_Wl_Window* >( nativeWindow ) );
167 // If we fail to get Ecore_Wl_Window, we can't use the ImfManager correctly.
168 // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
169 // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
171 manager = Dali::ImfManager( new ImfManager( ecoreWwin ) );
172 service.Register( typeid( manager ), manager );
176 DALI_LOG_ERROR("Failed to get native window handle\n");
184 ImfManager::ImfManager( Ecore_Wl_Window *ecoreWlwin )
186 mIMFCursorPosition( 0 ),
188 mRestoreAfterFocusLost( false ),
189 mIdleCallbackConnected( false )
192 CreateContext( ecoreWlwin );
195 VirtualKeyboard::ConnectCallbacks( mIMFContext );
198 ImfManager::~ImfManager()
200 VirtualKeyboard::DisconnectCallbacks( mIMFContext );
201 DisconnectCallbacks();
204 ecore_imf_shutdown();
208 void ImfManager::CreateContext( Ecore_Wl_Window *ecoreWlwin )
210 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
212 const char *contextId = ecore_imf_context_default_id_get();
215 mIMFContext = ecore_imf_context_add( contextId );
221 ecore_imf_context_client_window_set( mIMFContext,
222 reinterpret_cast<void*>( ecore_wl_window_id_get(ecoreWlwin)) );
227 DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
232 DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
236 void ImfManager::DeleteContext()
238 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
246 // Callbacks for predicitive text support.
247 void ImfManager::ConnectCallbacks()
251 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
253 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit, this );
254 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit, this );
255 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
257 ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
261 void ImfManager::DisconnectCallbacks()
265 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
267 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit );
268 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit );
269 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
271 // We do not need to unset the retrieve surrounding callback.
275 void ImfManager::Activate()
277 // Reset mIdleCallbackConnected
278 mIdleCallbackConnected = false;
282 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
284 ecore_imf_context_focus_in( mIMFContext );
286 // emit keyboard activated signal
287 Dali::ImfManager handle( this );
288 mActivatedSignal.Emit( handle );
292 void ImfManager::Deactivate()
296 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
299 ecore_imf_context_focus_out( mIMFContext );
302 // Reset mIdleCallbackConnected
303 mIdleCallbackConnected = false;
306 void ImfManager::Reset()
308 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
312 ecore_imf_context_reset( mIMFContext );
316 Ecore_IMF_Context* ImfManager::GetContext()
318 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
323 bool ImfManager::RestoreAfterFocusLost() const
325 return mRestoreAfterFocusLost;
328 void ImfManager::SetRestoreAfterFocusLost( bool toggle )
330 mRestoreAfterFocusLost = toggle;
334 * Called when an IMF Pre-Edit changed event is received.
335 * We are still predicting what the user is typing. The latest string is what the IMF module thinks
336 * the user wants to type.
338 void ImfManager::PreEditChanged( void*, Ecore_IMF_Context* imfContext, void* event_info )
340 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
342 char* preEditString( NULL );
343 int cursorPosition( 0 );
344 Eina_List* attrs = NULL;
347 Ecore_IMF_Preedit_Attr* attr;
349 // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
350 // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
351 ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
355 // iterate through the list of attributes getting the type, start and end position.
356 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) ) ))
358 #ifdef DALI_PROFILE_UBUNTU
359 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
360 #else // DALI_PROFILE_UBUNTU
361 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
362 #endif // DALI_PROFILE_UBUNTU
364 // 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.
366 size_t visualCharacterIndex = 0;
367 size_t byteIndex = 0;
369 // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
370 const char leadByte = preEditString[byteIndex];
371 while( leadByte != '\0' )
373 // attr->end_index is provided as a byte position not character and we need to know the character position.
374 const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
375 if ( byteIndex == attr->end_index )
377 cursorPosition = visualCharacterIndex;
379 // end loop as found cursor position that matches byte position
383 byteIndex += currentSequenceLength; // jump to next character
384 visualCharacterIndex++; // increment character count so we know our position for when we get a match
387 DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
393 if ( Dali::Adaptor::IsAvailable() )
395 Dali::ImfManager handle( this );
396 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
397 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
399 if ( callbackData.update )
401 SetCursorPosition( callbackData.cursorPosition );
402 SetSurroundingText( callbackData.currentText );
404 NotifyCursorPosition();
407 if ( callbackData.preeditResetRequired )
412 free( preEditString );
415 void ImfManager::CommitReceived( void*, Ecore_IMF_Context* imfContext, void* event_info )
417 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
419 if ( Dali::Adaptor::IsAvailable() )
421 const std::string keyString( static_cast<char*>( event_info ) );
423 Dali::ImfManager handle( this );
424 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
425 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
427 if( callbackData.update )
429 SetCursorPosition( callbackData.cursorPosition );
430 SetSurroundingText( callbackData.currentText );
432 NotifyCursorPosition();
438 * Called when an IMF retrieve surround event is received.
439 * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
440 * We need to signal the application to tell us this information.
442 Eina_Bool ImfManager::RetrieveSurrounding( void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition )
444 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
446 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
447 Dali::ImfManager handle( this );
448 mEventSignal.Emit( handle, imfData );
452 *text = strdup( mSurroundingText.c_str() );
457 *cursorPosition = mIMFCursorPosition;
464 * Called when an IMF delete surrounding event is received.
465 * Here we tell the application that it should delete a certain range.
467 void ImfManager::DeleteSurrounding( void* data, Ecore_IMF_Context* imfContext, void* event_info )
469 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
471 if( Dali::Adaptor::IsAvailable() )
473 Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
475 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
476 Dali::ImfManager handle( this );
477 mEventSignal.Emit( handle, imfData );
481 void ImfManager::NotifyCursorPosition()
483 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
487 ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
491 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
493 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
495 mIMFCursorPosition = static_cast<int>( cursorPosition );
498 unsigned int ImfManager::GetCursorPosition() const
500 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
502 return static_cast<unsigned int>( mIMFCursorPosition );
505 void ImfManager::SetSurroundingText( const std::string& text )
507 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
509 mSurroundingText = text;
512 const std::string& ImfManager::GetSurroundingText() const
514 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
516 return mSurroundingText;
519 void ImfManager::NotifyTextInputMultiLine( bool multiLine )
521 Ecore_IMF_Input_Hints currentHint = ecore_imf_context_input_hint_get(mIMFContext);
522 ecore_imf_context_input_hint_set(mIMFContext, (Ecore_IMF_Input_Hints)(multiLine ?
523 (currentHint | ECORE_IMF_INPUT_HINT_MULTILINE) :
524 (currentHint & ~ECORE_IMF_INPUT_HINT_MULTILINE)));