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