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 <boost/bind.hpp>
23 #include <dali/public-api/events/key-event.h>
24 #include <dali/public-api/object/type-registry.h>
26 #include <dali/integration-api/debug.h>
29 #include <window-render-surface.h>
30 #include <adaptor-impl.h>
31 #include <singleton-service-impl.h>
32 #include <virtual-keyboard-impl.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 );
122 BaseHandle handle( ImfManager::Get() );
124 Dali::SingletonService service( SingletonService::Get() );
125 if ( !handle && Adaptor::IsAvailable() && service )
127 Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
129 // The Ecore_X_Window needs to use the ImfManager.
130 // Only when the render surface is window, we can get the Ecore_X_Window.
131 Ecore_X_Window ecoreXwin( 0 );
132 Dali::RenderSurface& surface( adaptorImpl.GetSurface() );
133 if( surface.GetType() == Dali::RenderSurface::WINDOW )
135 ecoreXwin = AnyCast< Ecore_X_Window >( adaptorImpl.GetSurface().GetSurface() );
138 // If we fail to get Ecore_X_Window, we can't use the ImfManager correctly.
139 // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
140 // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
142 Dali::ImfManager manager = Dali::ImfManager( new ImfManager( ecoreXwin ) );
143 service.Register( typeid( manager ), manager );
150 TypeRegistration IMF_MANAGER_TYPE( typeid(Dali::ImfManager), typeid(Dali::BaseHandle), Create, true /* Create Instance At Startup */ );
152 } // unnamed namespace
154 Dali::ImfManager ImfManager::Get()
156 Dali::ImfManager manager;
158 Dali::SingletonService service( SingletonService::Get() );
161 // Check whether the singleton is already created
162 Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
165 // If so, downcast the handle
166 manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
173 ImfManager::ImfManager( Ecore_X_Window ecoreXwin )
175 mIMFCursorPosition( 0 ),
176 mSurroundingText(""),
177 mRestoreAfterFocusLost( false ),
178 mIdleCallbackConnected( false ),
182 CreateContext( ecoreXwin );
185 VirtualKeyboard::ConnectCallbacks( mIMFContext );
188 ImfManager::~ImfManager()
190 VirtualKeyboard::DisconnectCallbacks( mIMFContext );
191 DisconnectCallbacks();
194 ecore_imf_shutdown();
197 void ImfManager::CreateContext( Ecore_X_Window ecoreXwin )
199 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
201 const char *contextId = ecore_imf_context_default_id_get();
204 mIMFContext = ecore_imf_context_add( contextId );
210 ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
215 DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
220 DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
224 void ImfManager::DeleteContext()
226 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
234 // Callbacks for predicitive text support.
235 void ImfManager::ConnectCallbacks()
239 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
241 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit, this );
242 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit, this );
243 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
245 ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
249 void ImfManager::DisconnectCallbacks()
253 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
255 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit );
256 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit );
257 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
259 // We do not need to unset the retrieve surrounding callback.
263 void ImfManager::Activate()
265 // Reset mIdleCallbackConnected
266 mIdleCallbackConnected = false;
270 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
272 ecore_imf_context_focus_in( mIMFContext );
274 // emit keyboard activated signal
275 Dali::ImfManager handle( this );
276 mActivatedSignalV2.Emit( handle );
280 void ImfManager::Deactivate()
284 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
287 ecore_imf_context_focus_out( mIMFContext );
290 // Reset mIdleCallbackConnected
291 mIdleCallbackConnected = false;
294 void ImfManager::Reset()
296 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
300 ecore_imf_context_reset( mIMFContext );
304 Ecore_IMF_Context* ImfManager::GetContext()
306 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
311 bool ImfManager::RestoreAfterFocusLost() const
313 return mRestoreAfterFocusLost;
316 void ImfManager::SetRestoreAferFocusLost( bool toggle )
318 mRestoreAfterFocusLost = toggle;
322 * Called when an IMF Pre-Edit changed event is received.
323 * We are still predicting what the user is typing. The latest string is what the IMF module thinks
324 * the user wants to type.
326 void ImfManager::PreEditChanged( void *, Ecore_IMF_Context *imfContext, void *event_info )
328 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
330 char *preEditString( NULL );
331 int cursorPosition( 0 );
332 Eina_List *attrs = NULL;
335 Ecore_IMF_Preedit_Attr *attr;
337 // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
338 // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
339 ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
343 // iterate through the list of attributes getting the type, start and end position.
344 for ( l = attrs, (attr = (Ecore_IMF_Preedit_Attr*)eina_list_data_get(l) ); l; l = eina_list_next(l), ( attr = (Ecore_IMF_Preedit_Attr*)eina_list_data_get(l) ))
346 #ifdef DALI_PROFILE_UBUNTU
347 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
348 #else // DALI_PROFILE_UBUNTU
349 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
350 #endif // DALI_PROFILE_UBUNTU
352 // 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.
354 size_t visualCharacterIndex = 0;
355 size_t byteIndex = 0;
357 // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
358 while ( preEditString[byteIndex] != '\0' )
360 // attr->end_index is provided as a byte position not character and we need to know the character position.
361 size_t currentSequenceLength = Utf8SequenceLength(preEditString[byteIndex]); // returns number of bytes used to represent character.
362 if ( byteIndex == attr->end_index )
364 cursorPosition = visualCharacterIndex;
366 // end loop as found cursor position that matches byte position
370 byteIndex += currentSequenceLength; // jump to next character
371 visualCharacterIndex++; // increment character count so we know our position for when we get a match
374 DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
380 if ( Dali::Adaptor::IsAvailable() )
382 std::string keyString ( preEditString );
383 int numberOfChars( 0 );
385 Dali::ImfManager handle( this );
386 Dali::ImfManager::ImfEventData imfEventData ( Dali::ImfManager::PREEDIT, keyString, cursorPosition, numberOfChars );
387 Dali::ImfManager::ImfCallbackData callbackData = mEventSignalV2.Emit( handle, imfEventData );
389 if ( callbackData.update )
391 SetCursorPosition( callbackData.cursorPosition );
392 SetSurroundingText( callbackData.currentText );
394 NotifyCursorPosition();
397 if ( callbackData.preeditResetRequired )
402 free( preEditString );
405 void ImfManager::CommitReceived( void *, Ecore_IMF_Context *imfContext, void *event_info )
407 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
409 if ( Dali::Adaptor::IsAvailable() )
411 const std::string keyString( (char *)event_info );
412 const int cursorOffset( 0 );
413 const int numberOfChars( 0 );
415 Dali::ImfManager handle( this );
416 Dali::ImfManager::ImfEventData imfEventData ( Dali::ImfManager::COMMIT, keyString, cursorOffset, numberOfChars );
417 Dali::ImfManager::ImfCallbackData callbackData = mEventSignalV2.Emit( handle, imfEventData );
419 if ( callbackData.update )
421 SetCursorPosition( callbackData.cursorPosition );
422 SetSurroundingText( callbackData.currentText );
424 NotifyCursorPosition();
430 * Called when an IMF retrieve surround event is received.
431 * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
432 * We need to signal the application to tell us this information.
434 Eina_Bool ImfManager::RetrieveSurrounding( void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
436 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
438 std::string keyString ( "" );
439 int cursorOffset( 0 );
440 int numberOfChars( 0 );
442 Dali::ImfManager::ImfEventData imfData ( Dali::ImfManager::GETSURROUNDING , keyString, cursorOffset, numberOfChars );
443 Dali::ImfManager handle( this );
444 mEventSignalV2.Emit( handle, imfData );
448 std::string surroundingText( GetSurroundingText() );
450 if ( !surroundingText.empty() )
452 *text = strdup( surroundingText.c_str() );
456 *text = strdup( "" );
460 if ( cursorPosition )
462 *cursorPosition = GetCursorPosition();
470 * Called when an IMF delete surrounding event is received.
471 * Here we tell the application that it should delete a certain range.
473 void ImfManager::DeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
475 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
477 if ( Dali::Adaptor::IsAvailable() )
479 Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = (Ecore_IMF_Event_Delete_Surrounding*) event_info;
481 const std::string keyString( "" );
482 const int cursorOffset( deleteSurroundingEvent->offset );
483 const int numberOfChars( deleteSurroundingEvent->n_chars );
485 Dali::ImfManager::ImfEventData imfData ( Dali::ImfManager::DELETESURROUNDING , keyString, cursorOffset, numberOfChars );
486 Dali::ImfManager handle( this );
487 mEventSignalV2.Emit( handle, imfData );
491 void ImfManager::NotifyCursorPosition()
493 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
497 ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
501 int ImfManager::GetCursorPosition()
503 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
505 return mIMFCursorPosition;
508 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
510 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
512 mIMFCursorPosition = ( int )cursorPosition;
515 void ImfManager::SetSurroundingText( std::string text )
517 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
519 mSurroundingText = text;
522 std::string ImfManager::GetSurroundingText()
524 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
526 return mSurroundingText;