[dali_1.0.8] Merge branch 'tizen'
[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 <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   BaseHandle handle( ImfManager::Get() );
123
124   Dali::SingletonService service( SingletonService::Get() );
125   if ( !handle && Adaptor::IsAvailable() && service )
126   {
127     Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
128
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 )
134     {
135       ecoreXwin = AnyCast< Ecore_X_Window >( adaptorImpl.GetSurface().GetSurface() );
136     }
137
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().
141
142     Dali::ImfManager manager = Dali::ImfManager( new ImfManager( ecoreXwin ) );
143     service.Register( typeid( manager ), manager );
144     handle = manager;
145   }
146
147   return handle;
148 }
149
150 TypeRegistration IMF_MANAGER_TYPE( typeid(Dali::ImfManager), typeid(Dali::BaseHandle), Create, true /* Create Instance At Startup */ );
151
152 } // unnamed namespace
153
154 Dali::ImfManager ImfManager::Get()
155 {
156   Dali::ImfManager manager;
157
158   Dali::SingletonService service( SingletonService::Get() );
159   if ( service )
160   {
161     // Check whether the singleton is already created
162     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
163     if(handle)
164     {
165       // If so, downcast the handle
166       manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
167     }
168   }
169
170   return manager;
171 }
172
173 ImfManager::ImfManager( Ecore_X_Window ecoreXwin )
174 : mIMFContext(),
175   mIMFCursorPosition( 0 ),
176   mSurroundingText(""),
177   mRestoreAfterFocusLost( false ),
178   mIdleCallbackConnected( false ),
179   mKeyEvents()
180 {
181   ecore_imf_init();
182   CreateContext( ecoreXwin );
183
184   ConnectCallbacks();
185   VirtualKeyboard::ConnectCallbacks( mIMFContext );
186 }
187
188 ImfManager::~ImfManager()
189 {
190   VirtualKeyboard::DisconnectCallbacks( mIMFContext );
191   DisconnectCallbacks();
192
193   DeleteContext();
194   ecore_imf_shutdown();
195 }
196
197 void ImfManager::CreateContext( Ecore_X_Window ecoreXwin )
198 {
199   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
200
201   const char *contextId = ecore_imf_context_default_id_get();
202   if( contextId )
203   {
204     mIMFContext = ecore_imf_context_add( contextId );
205
206     if( mIMFContext )
207     {
208       if( ecoreXwin )
209       {
210         ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
211       }
212     }
213     else
214     {
215       DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
216     }
217   }
218   else
219   {
220     DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
221   }
222 }
223
224 void ImfManager::DeleteContext()
225 {
226   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
227
228   if ( mIMFContext )
229   {
230     mIMFContext = NULL;
231   }
232 }
233
234 // Callbacks for predicitive text support.
235 void ImfManager::ConnectCallbacks()
236 {
237   if ( mIMFContext )
238   {
239     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
240
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 );
244
245     ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
246   }
247 }
248
249 void ImfManager::DisconnectCallbacks()
250 {
251   if ( mIMFContext )
252   {
253     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
254
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 );
258
259     // We do not need to unset the retrieve surrounding callback.
260   }
261 }
262
263 void ImfManager::Activate()
264 {
265   // Reset mIdleCallbackConnected
266   mIdleCallbackConnected = false;
267
268   if ( mIMFContext )
269   {
270     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
271
272     ecore_imf_context_focus_in( mIMFContext );
273
274     // emit keyboard activated signal
275     Dali::ImfManager handle( this );
276     mActivatedSignalV2.Emit( handle );
277   }
278 }
279
280 void ImfManager::Deactivate()
281 {
282   if( mIMFContext )
283   {
284     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
285
286     Reset();
287     ecore_imf_context_focus_out( mIMFContext );
288   }
289
290   // Reset mIdleCallbackConnected
291   mIdleCallbackConnected = false;
292 }
293
294 void ImfManager::Reset()
295 {
296   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
297
298   if ( mIMFContext )
299   {
300     ecore_imf_context_reset( mIMFContext );
301   }
302 }
303
304 Ecore_IMF_Context* ImfManager::GetContext()
305 {
306   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
307
308   return mIMFContext;
309 }
310
311 bool ImfManager::RestoreAfterFocusLost() const
312 {
313   return mRestoreAfterFocusLost;
314 }
315
316 void ImfManager::SetRestoreAferFocusLost( bool toggle )
317 {
318   mRestoreAfterFocusLost = toggle;
319 }
320
321 /**
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.
325  */
326 void ImfManager::PreEditChanged( void *, Ecore_IMF_Context *imfContext, void *event_info )
327 {
328   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
329
330   char *preEditString( NULL );
331   int cursorPosition( 0 );
332   Eina_List *attrs = NULL;
333   Eina_List *l = NULL;
334
335   Ecore_IMF_Preedit_Attr *attr;
336
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 );
340
341   if ( attrs )
342   {
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) ))
345     {
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
351       {
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.
353
354         size_t visualCharacterIndex = 0;
355         size_t byteIndex = 0;
356
357         // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
358         while ( preEditString[byteIndex] != '\0' )
359         {
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 )
363           {
364             cursorPosition = visualCharacterIndex;
365             break;
366             // end loop as found cursor position that matches byte position
367           }
368           else
369           {
370             byteIndex += currentSequenceLength; // jump to next character
371             visualCharacterIndex++;  // increment character count so we know our position for when we get a match
372           }
373
374           DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
375         }
376       }
377     }
378   }
379
380   if ( Dali::Adaptor::IsAvailable() )
381   {
382     std::string keyString ( preEditString );
383     int numberOfChars( 0 );
384
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 );
388
389     if ( callbackData.update )
390     {
391       SetCursorPosition( callbackData.cursorPosition );
392       SetSurroundingText( callbackData.currentText );
393
394       NotifyCursorPosition();
395     }
396
397     if ( callbackData.preeditResetRequired )
398     {
399       Reset();
400     }
401   }
402   free( preEditString );
403 }
404
405 void ImfManager::CommitReceived( void *, Ecore_IMF_Context *imfContext, void *event_info )
406 {
407   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
408
409   if ( Dali::Adaptor::IsAvailable() )
410   {
411     const std::string keyString( (char *)event_info );
412     const int cursorOffset( 0 );
413     const int numberOfChars( 0 );
414
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 );
418
419     if ( callbackData.update )
420     {
421       SetCursorPosition( callbackData.cursorPosition );
422       SetSurroundingText( callbackData.currentText );
423
424       NotifyCursorPosition();
425     }
426   }
427 }
428
429 /**
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.
433  */
434 Eina_Bool ImfManager::RetrieveSurrounding( void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
435 {
436   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
437
438   std::string keyString ( "" );
439   int cursorOffset( 0 );
440   int numberOfChars( 0 );
441
442   Dali::ImfManager::ImfEventData imfData ( Dali::ImfManager::GETSURROUNDING , keyString, cursorOffset, numberOfChars );
443   Dali::ImfManager handle( this );
444   mEventSignalV2.Emit( handle, imfData );
445
446   if ( text )
447   {
448     std::string surroundingText( GetSurroundingText() );
449
450     if ( !surroundingText.empty() )
451     {
452       *text = strdup( surroundingText.c_str() );
453     }
454     else
455     {
456       *text = strdup( "" );
457     }
458   }
459
460   if ( cursorPosition )
461   {
462     *cursorPosition = GetCursorPosition();
463   }
464
465
466   return EINA_TRUE;
467 }
468
469 /**
470  * Called when an IMF delete surrounding event is received.
471  * Here we tell the application that it should delete a certain range.
472  */
473 void ImfManager::DeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
474 {
475   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
476
477   if ( Dali::Adaptor::IsAvailable() )
478   {
479     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = (Ecore_IMF_Event_Delete_Surrounding*) event_info;
480
481     const std::string keyString( "" );
482     const int cursorOffset( deleteSurroundingEvent->offset );
483     const int numberOfChars( deleteSurroundingEvent->n_chars );
484
485     Dali::ImfManager::ImfEventData imfData ( Dali::ImfManager::DELETESURROUNDING , keyString, cursorOffset, numberOfChars );
486     Dali::ImfManager handle( this );
487     mEventSignalV2.Emit( handle, imfData );
488   }
489 }
490
491 void ImfManager::NotifyCursorPosition()
492 {
493   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
494
495   if ( mIMFContext )
496   {
497     ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
498   }
499 }
500
501 int ImfManager::GetCursorPosition()
502 {
503   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
504
505   return mIMFCursorPosition;
506 }
507
508 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
509 {
510   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
511
512   mIMFCursorPosition = ( int )cursorPosition;
513 }
514
515 void ImfManager::SetSurroundingText( std::string text )
516 {
517   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
518
519   mSurroundingText = text;
520 }
521
522 std::string ImfManager::GetSurroundingText()
523 {
524   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
525
526   return mSurroundingText;
527 }
528
529 } // Adaptor
530
531 } // Internal
532
533 } // Dali