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