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