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