Removed singletons from the type registry
[platform/core/uifw/dali-adaptor.git] / adaptors / x11 / imf-manager-impl-x.cpp
1 /*
2  * Copyright (c) 2014 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 <singleton-service-impl.h>
31 #include <virtual-keyboard-impl.h>
32 #include "ecore-virtual-keyboard.h"
33
34 namespace Dali
35 {
36
37 namespace Internal
38 {
39
40 namespace Adaptor
41 {
42
43 namespace
44 {
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMF_MANAGER");
47 #endif
48
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)
51 {
52   size_t length = 0;
53
54   if ((leadByte & 0x80) == 0 )          //ASCII character (lead bit zero)
55   {
56     length = 1;
57   }
58   else if (( leadByte & 0xe0 ) == 0xc0 ) //110x xxxx
59   {
60     length = 2;
61   }
62   else if (( leadByte & 0xf0 ) == 0xe0 ) //1110 xxxx
63   {
64     length = 3;
65   }
66   else if (( leadByte & 0xf8 ) == 0xf0 ) //1111 0xxx
67   {
68     length = 4;
69   }
70
71   return length;
72 }
73
74 // Static function calls used by ecore 'c' style callback registration
75 void Commit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
76 {
77   if ( data )
78   {
79     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
80     imfManager->CommitReceived( data, imfContext, event_info );
81   }
82 }
83
84 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
85 {
86   if ( data )
87   {
88     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
89     imfManager->PreEditChanged( data, imfContext, event_info );
90   }
91 }
92
93 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
94 {
95   if ( data )
96   {
97     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
98     return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
99   }
100   else
101   {
102     return false;
103   }
104 }
105
106 /**
107  * Called when an IMF delete surrounding event is received.
108  * Here we tell the application that it should delete a certain range.
109  */
110 void ImfDeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
111 {
112   if ( data )
113   {
114     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
115     imfManager->DeleteSurrounding( data, imfContext, event_info );
116   }
117 }
118
119 } // unnamed namespace
120
121 bool ImfManager::IsAvailable()
122 {
123   bool available( false );
124
125   Dali::SingletonService service( SingletonService::Get() );
126   if ( service )
127   {
128     available = service.GetSingleton( typeid( Dali::ImfManager ) );
129   }
130
131   return available;
132 }
133
134 Dali::ImfManager ImfManager::Get()
135 {
136   Dali::ImfManager manager;
137
138   Dali::SingletonService service( SingletonService::Get() );
139   if ( service )
140   {
141     // Check whether the singleton is already created
142     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
143     if( handle )
144     {
145       // If so, downcast the handle
146       manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
147     }
148     else if ( Adaptor::IsAvailable() )
149     {
150       // Create instance and register singleton only if the adaptor is available
151
152       Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
153       Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
154
155       // The Ecore_X_Window needs to use the ImfManager.
156       // Only when the render surface is window, we can get the Ecore_X_Window.
157       Ecore_X_Window ecoreXwin( AnyCast<Ecore_X_Window>(nativeWindow) );
158       if (ecoreXwin)
159       {
160         // If we fail to get Ecore_X_Window, we can't use the ImfManager correctly.
161         // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
162         // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
163
164         manager = Dali::ImfManager( new ImfManager( ecoreXwin ) );
165         service.Register( typeid( manager ), manager );
166       }
167       else
168       {
169         DALI_LOG_ERROR("Failed to get native window handle");
170       }
171     }
172   }
173
174   return manager;
175 }
176
177 ImfManager::ImfManager( Ecore_X_Window ecoreXwin )
178 : mIMFContext(),
179   mIMFCursorPosition( 0 ),
180   mSurroundingText(),
181   mRestoreAfterFocusLost( false ),
182   mIdleCallbackConnected( false )
183 {
184   ecore_imf_init();
185   CreateContext( ecoreXwin );
186
187   ConnectCallbacks();
188   VirtualKeyboard::ConnectCallbacks( mIMFContext );
189 }
190
191 ImfManager::~ImfManager()
192 {
193   VirtualKeyboard::DisconnectCallbacks( mIMFContext );
194   DisconnectCallbacks();
195
196   DeleteContext();
197   ecore_imf_shutdown();
198 }
199
200 void ImfManager::CreateContext( Ecore_X_Window ecoreXwin )
201 {
202   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
203
204   const char *contextId = ecore_imf_context_default_id_get();
205   if( contextId )
206   {
207     mIMFContext = ecore_imf_context_add( contextId );
208
209     if( mIMFContext )
210     {
211       if( ecoreXwin )
212       {
213         ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
214       }
215     }
216     else
217     {
218       DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
219     }
220   }
221   else
222   {
223     DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
224   }
225 }
226
227 void ImfManager::DeleteContext()
228 {
229   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
230
231   if ( mIMFContext )
232   {
233     mIMFContext = NULL;
234   }
235 }
236
237 // Callbacks for predicitive text support.
238 void ImfManager::ConnectCallbacks()
239 {
240   if ( mIMFContext )
241   {
242     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
243
244     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit,    this );
245     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit,     this );
246     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
247
248     ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
249   }
250 }
251
252 void ImfManager::DisconnectCallbacks()
253 {
254   if ( mIMFContext )
255   {
256     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
257
258     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit );
259     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit );
260     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
261
262     // We do not need to unset the retrieve surrounding callback.
263   }
264 }
265
266 void ImfManager::Activate()
267 {
268   // Reset mIdleCallbackConnected
269   mIdleCallbackConnected = false;
270
271   if ( mIMFContext )
272   {
273     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
274
275     ecore_imf_context_focus_in( mIMFContext );
276
277     // emit keyboard activated signal
278     Dali::ImfManager handle( this );
279     mActivatedSignal.Emit( handle );
280   }
281 }
282
283 void ImfManager::Deactivate()
284 {
285   if( mIMFContext )
286   {
287     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
288
289     Reset();
290     ecore_imf_context_focus_out( mIMFContext );
291   }
292
293   // Reset mIdleCallbackConnected
294   mIdleCallbackConnected = false;
295 }
296
297 void ImfManager::Reset()
298 {
299   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
300
301   if ( mIMFContext )
302   {
303     ecore_imf_context_reset( mIMFContext );
304   }
305 }
306
307 Ecore_IMF_Context* ImfManager::GetContext()
308 {
309   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
310
311   return mIMFContext;
312 }
313
314 bool ImfManager::RestoreAfterFocusLost() const
315 {
316   return mRestoreAfterFocusLost;
317 }
318
319 void ImfManager::SetRestoreAfterFocusLost( bool toggle )
320 {
321   mRestoreAfterFocusLost = toggle;
322 }
323
324 /**
325  * Called when an IMF Pre-Edit changed event is received.
326  * We are still predicting what the user is typing.  The latest string is what the IMF module thinks
327  * the user wants to type.
328  */
329 void ImfManager::PreEditChanged( void*, Ecore_IMF_Context* imfContext, void* event_info )
330 {
331   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
332
333   char* preEditString( NULL );
334   int cursorPosition( 0 );
335   Eina_List* attrs = NULL;
336   Eina_List* l = NULL;
337
338   Ecore_IMF_Preedit_Attr* attr;
339
340   // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
341   // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
342   ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
343
344   if ( attrs )
345   {
346     // iterate through the list of attributes getting the type, start and end position.
347     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) ) ))
348     {
349 #ifdef DALI_PROFILE_UBUNTU
350       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
351 #else // DALI_PROFILE_UBUNTU
352       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
353 #endif // DALI_PROFILE_UBUNTU
354       {
355         // 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.
356
357         size_t visualCharacterIndex = 0;
358         size_t byteIndex = 0;
359
360         // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
361         const char leadByte = preEditString[byteIndex];
362         while( leadByte != '\0' )
363         {
364           // attr->end_index is provided as a byte position not character and we need to know the character position.
365           const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
366           if ( byteIndex == attr->end_index )
367           {
368             cursorPosition = visualCharacterIndex;
369             break;
370             // end loop as found cursor position that matches byte position
371           }
372           else
373           {
374             byteIndex += currentSequenceLength; // jump to next character
375             visualCharacterIndex++;  // increment character count so we know our position for when we get a match
376           }
377
378           DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
379         }
380       }
381     }
382   }
383
384   if ( Dali::Adaptor::IsAvailable() )
385   {
386     Dali::ImfManager handle( this );
387     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
388     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
389
390     if ( callbackData.update )
391     {
392       SetCursorPosition( callbackData.cursorPosition );
393       SetSurroundingText( callbackData.currentText );
394
395       NotifyCursorPosition();
396     }
397
398     if ( callbackData.preeditResetRequired )
399     {
400       Reset();
401     }
402   }
403   free( preEditString );
404 }
405
406 void ImfManager::CommitReceived( void*, Ecore_IMF_Context* imfContext, void* event_info )
407 {
408   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
409
410   if ( Dali::Adaptor::IsAvailable() )
411   {
412     const std::string keyString( static_cast<char*>( event_info ) );
413
414     Dali::ImfManager handle( this );
415     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
416     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
417
418     if( callbackData.update )
419     {
420       SetCursorPosition( callbackData.cursorPosition );
421       SetSurroundingText( callbackData.currentText );
422
423       NotifyCursorPosition();
424     }
425   }
426 }
427
428 /**
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.
432  */
433 Eina_Bool ImfManager::RetrieveSurrounding( void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition )
434 {
435   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
436
437   Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
438   Dali::ImfManager handle( this );
439   mEventSignal.Emit( handle, imfData );
440
441   if( text )
442   {
443     *text = strdup( mSurroundingText.c_str() );
444   }
445
446   if( cursorPosition )
447   {
448     *cursorPosition = mIMFCursorPosition;
449   }
450
451   return EINA_TRUE;
452 }
453
454 /**
455  * Called when an IMF delete surrounding event is received.
456  * Here we tell the application that it should delete a certain range.
457  */
458 void ImfManager::DeleteSurrounding( void* data, Ecore_IMF_Context* imfContext, void* event_info )
459 {
460   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
461
462   if( Dali::Adaptor::IsAvailable() )
463   {
464     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
465
466     Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
467     Dali::ImfManager handle( this );
468     mEventSignal.Emit( handle, imfData );
469   }
470 }
471
472 void ImfManager::NotifyCursorPosition()
473 {
474   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
475
476   if( mIMFContext )
477   {
478     ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
479   }
480 }
481
482 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
483 {
484   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
485
486   mIMFCursorPosition = static_cast<int>( cursorPosition );
487 }
488
489 unsigned int ImfManager::GetCursorPosition() const
490 {
491   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
492
493   return static_cast<unsigned int>( mIMFCursorPosition );
494 }
495
496 void ImfManager::SetSurroundingText( const std::string& text )
497 {
498   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
499
500   mSurroundingText = text;
501 }
502
503 const std::string& ImfManager::GetSurroundingText() const
504 {
505   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
506
507   return mSurroundingText;
508 }
509
510 } // Adaptor
511
512 } // Internal
513
514 } // Dali