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