b9b260b80f95b7de6f8f60346059b8be0600d52d
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-event-handler.cpp
1 /*
2  * Copyright (c) 2020 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 <dali-toolkit/internal/text/text-controller-event-handler.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
24 #include <dali/devel-api/adaptor-framework/key-devel.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/text-controller-impl.h>
28 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
29
30 namespace
31 {
32
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
35 #endif
36
37 const std::string KEY_C_NAME = "c";
38 const std::string KEY_V_NAME = "v";
39 const std::string KEY_X_NAME = "x";
40 const std::string KEY_A_NAME = "a";
41 const std::string KEY_INSERT_NAME = "Insert";
42
43 } // namespace
44
45 namespace Dali
46 {
47
48 namespace Toolkit
49 {
50
51 namespace Text
52 {
53
54 void Controller::EventHandler::KeyboardFocusGainEvent(Controller& controller)
55 {
56   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
57
58   if( NULL != controller.mImpl->mEventData )
59   {
60     if( ( EventData::INACTIVE == controller.mImpl->mEventData->mState ) ||
61         ( EventData::INTERRUPTED == controller.mImpl->mEventData->mState ) )
62     {
63       controller.mImpl->ChangeState( EventData::EDITING );
64       controller.mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
65       controller.mImpl->mEventData->mUpdateInputStyle = true;
66       controller.mImpl->mEventData->mScrollAfterUpdatePosition = true;
67     }
68     controller.mImpl->NotifyInputMethodContextMultiLineStatus();
69     if( controller.mImpl->IsShowingPlaceholderText() )
70     {
71       // Show alternative placeholder-text when editing
72       controller.ShowPlaceholderText();
73     }
74
75     controller.mImpl->RequestRelayout();
76   }
77 }
78
79 void Controller::EventHandler::KeyboardFocusLostEvent(Controller& controller)
80 {
81   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
82
83   if( NULL != controller.mImpl->mEventData )
84   {
85     if( EventData::INTERRUPTED != controller.mImpl->mEventData->mState )
86     {
87       controller.mImpl->ChangeState( EventData::INACTIVE );
88
89       if( !controller.mImpl->IsShowingRealText() )
90       {
91         // Revert to regular placeholder-text when not editing
92         controller.ShowPlaceholderText();
93       }
94     }
95   }
96   controller.mImpl->RequestRelayout();
97 }
98
99 bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyEvent& keyEvent)
100 {
101   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected KeyEvent" );
102
103   bool textChanged = false;
104   bool relayoutNeeded = false;
105
106   if( ( NULL != controller.mImpl->mEventData ) &&
107       ( keyEvent.GetState() == KeyEvent::DOWN ) )
108   {
109     int keyCode = keyEvent.GetKeyCode();
110     const std::string& keyString = keyEvent.GetKeyString();
111     const std::string keyName = keyEvent.GetKeyName();
112
113     const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
114
115     // Pre-process to separate modifying events from non-modifying input events.
116     if( isNullKey )
117     {
118       // In some platforms arrive key events with no key code.
119       // Do nothing.
120       return false;
121     }
122     else if( Dali::DALI_KEY_ESCAPE == keyCode || Dali::DALI_KEY_BACK == keyCode  || Dali::DALI_KEY_SEARCH == keyCode )
123     {
124       // Do nothing
125       return false;
126     }
127     else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
128              ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
129              ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
130              ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
131     {
132       // If don't have any text, do nothing.
133       if( !controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
134       {
135         return false;
136       }
137
138       uint32_t cursorPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
139       uint32_t numberOfCharacters = controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
140       uint32_t cursorLine = controller.mImpl->mModel->mVisualModel->GetLineOfCharacter( cursorPosition );
141       uint32_t numberOfLines = controller.mImpl->mModel->GetNumberOfLines();
142
143       // Logic to determine whether this text control will lose focus or not.
144       if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition && !keyEvent.IsShiftModifier() ) ||
145           ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition && !keyEvent.IsShiftModifier() ) ||
146           ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) ||
147           ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) ||
148           ( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) ||
149           ( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) )
150       {
151         // Release the active highlight.
152         if( controller.mImpl->mEventData->mState == EventData::SELECTING )
153         {
154           controller.mImpl->ChangeState( EventData::EDITING );
155
156           // Update selection position.
157           controller.mImpl->mEventData->mLeftSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
158           controller.mImpl->mEventData->mRightSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
159           controller.mImpl->mEventData->mUpdateCursorPosition = true;
160           controller.mImpl->RequestRelayout();
161         }
162         return false;
163       }
164
165       controller.mImpl->mEventData->mCheckScrollAmount = true;
166       Event event( Event::CURSOR_KEY_EVENT );
167       event.p1.mInt = keyCode;
168       event.p2.mBool = keyEvent.IsShiftModifier();
169       controller.mImpl->mEventData->mEventQueue.push_back( event );
170
171       // Will request for relayout.
172       relayoutNeeded = true;
173     }
174     else if ( Dali::DevelKey::DALI_KEY_CONTROL_LEFT == keyCode || Dali::DevelKey::DALI_KEY_CONTROL_RIGHT == keyCode )
175     {
176       // Left or Right Control key event is received before Ctrl-C/V/X key event is received
177       // If not handle it here, any selected text will be deleted
178
179       // Do nothing
180       return false;
181     }
182     else if ( keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier())
183     {
184       bool consumed = false;
185       if (keyName == KEY_C_NAME || keyName == KEY_INSERT_NAME)
186       {
187         // Ctrl-C or Ctrl+Insert to copy the selected text
188         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::COPY );
189         consumed = true;
190       }
191       else if (keyName == KEY_V_NAME)
192       {
193         // Ctrl-V to paste the copied text
194         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::PASTE );
195         consumed = true;
196       }
197       else if (keyName == KEY_X_NAME)
198       {
199         // Ctrl-X to cut the selected text
200         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::CUT );
201         consumed = true;
202       }
203       else if (keyName == KEY_A_NAME)
204       {
205         // Ctrl-A to select All the text
206         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::SELECT_ALL );
207         consumed = true;
208       }
209       return consumed;
210     }
211     else if( ( Dali::DALI_KEY_BACKSPACE == keyCode ) ||
212              ( Dali::DevelKey::DALI_KEY_DELETE == keyCode ) )
213     {
214       textChanged = controller.DeleteEvent( keyCode );
215
216       // Will request for relayout.
217       relayoutNeeded = true;
218     }
219     else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
220              IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
221              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
222     {
223       // Power key/Menu/Home key behaviour does not allow edit mode to resume.
224       controller.mImpl->ChangeState( EventData::INACTIVE );
225
226       // Will request for relayout.
227       relayoutNeeded = true;
228
229       // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
230     }
231     else if( ( Dali::DALI_KEY_SHIFT_LEFT == keyCode ) || ( Dali::DALI_KEY_SHIFT_RIGHT == keyCode ) )
232     {
233       // DALI_KEY_SHIFT_LEFT or DALI_KEY_SHIFT_RIGHT is the key code for Shift. It's sent (by the InputMethodContext?) when the predictive text is enabled
234       // and a character is typed after the type of a upper case latin character.
235
236       // Do nothing.
237       return false;
238     }
239     else if( ( Dali::DALI_KEY_VOLUME_UP == keyCode ) || ( Dali::DALI_KEY_VOLUME_DOWN == keyCode ) )
240     {
241       // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
242       // Do nothing.
243       return false;
244     }
245     else
246     {
247       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", &controller, keyString.c_str() );
248       if (!controller.IsEditable()) return false;
249
250       if( !keyString.empty() )
251       {
252         // InputMethodContext is no longer handling key-events
253         controller.mImpl->ClearPreEditFlag();
254
255         controller.InsertText( keyString, COMMIT );
256
257         textChanged = true;
258
259         // Will request for relayout.
260         relayoutNeeded = true;
261       }
262
263     }
264
265     if ( ( controller.mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
266          ( controller.mImpl->mEventData->mState != EventData::INACTIVE ) &&
267          ( !isNullKey ) &&
268          ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
269          ( Dali::DALI_KEY_SHIFT_RIGHT != keyCode ) &&
270          ( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
271          ( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) )
272     {
273       // Should not change the state if the key is the shift send by the InputMethodContext.
274       // Otherwise, when the state is SELECTING the text controller can't send the right
275       // surrounding info to the InputMethodContext.
276       controller.mImpl->ChangeState( EventData::EDITING );
277
278       // Will request for relayout.
279       relayoutNeeded = true;
280     }
281
282     if( relayoutNeeded )
283     {
284       controller.mImpl->RequestRelayout();
285     }
286   }
287
288   if( textChanged &&
289       ( NULL != controller.mImpl->mEditableControlInterface ) )
290   {
291     // Do this last since it provides callbacks into application code
292     controller.mImpl->mEditableControlInterface->TextChanged();
293   }
294
295   return true;
296 }
297
298 void Controller::EventHandler::TapEvent(Controller& controller, unsigned int tapCount, float x, float y)
299 {
300   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected TapEvent" );
301
302   if( NULL != controller.mImpl->mEventData )
303   {
304     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", controller.mImpl->mEventData->mState );
305     EventData::State state( controller.mImpl->mEventData->mState );
306     bool relayoutNeeded( false );   // to avoid unnecessary relayouts when tapping an empty text-field
307
308     if( controller.mImpl->IsClipboardVisible() )
309     {
310       if( EventData::INACTIVE == state || EventData::EDITING == state)
311       {
312         controller.mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
313       }
314       relayoutNeeded = true;
315     }
316     else if( 1u == tapCount )
317     {
318       if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
319       {
320         controller.mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );  // If Popup shown hide it here so can be shown again if required.
321       }
322
323       if( controller.mImpl->IsShowingRealText() && ( EventData::INACTIVE != state ) )
324       {
325         controller.mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
326         relayoutNeeded = true;
327       }
328       else
329       {
330         if( controller.mImpl->IsShowingPlaceholderText() && !controller.mImpl->IsFocusedPlaceholderAvailable() )
331         {
332           // Hide placeholder text
333           controller.ResetText();
334         }
335
336         if( EventData::INACTIVE == state )
337         {
338           controller.mImpl->ChangeState( EventData::EDITING );
339         }
340         else if( !controller.mImpl->IsClipboardEmpty() )
341         {
342           controller.mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
343         }
344         relayoutNeeded = true;
345       }
346     }
347     else if( 2u == tapCount )
348     {
349       if( controller.mImpl->mEventData->mSelectionEnabled &&
350           controller.mImpl->IsShowingRealText() )
351       {
352         relayoutNeeded = true;
353         controller.mImpl->mEventData->mIsLeftHandleSelected = true;
354         controller.mImpl->mEventData->mIsRightHandleSelected = true;
355       }
356     }
357
358     // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
359     if( relayoutNeeded )
360     {
361       Event event( Event::TAP_EVENT );
362       event.p1.mUint = tapCount;
363       event.p2.mFloat = x;
364       event.p3.mFloat = y;
365       controller.mImpl->mEventData->mEventQueue.push_back( event );
366
367       controller.mImpl->RequestRelayout();
368     }
369   }
370
371   // Reset keyboard as tap event has occurred.
372   controller.mImpl->ResetInputMethodContext();
373 }
374
375 void Controller::EventHandler::PanEvent(Controller& controller, GestureState state, const Vector2& displacement)
376 {
377   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected PanEvent" );
378
379   if( NULL != controller.mImpl->mEventData )
380   {
381     Event event( Event::PAN_EVENT );
382     event.p1.mInt = static_cast<int>( state );
383     event.p2.mFloat = displacement.x;
384     event.p3.mFloat = displacement.y;
385     controller.mImpl->mEventData->mEventQueue.push_back( event );
386
387     controller.mImpl->RequestRelayout();
388   }
389 }
390
391 void Controller::EventHandler::LongPressEvent(Controller& controller, GestureState state, float x, float y)
392 {
393   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected LongPressEvent" );
394
395   if( ( state == GestureState::STARTED ) &&
396       ( NULL != controller.mImpl->mEventData ) )
397   {
398     // The 1st long-press on inactive text-field is treated as tap
399     if( EventData::INACTIVE == controller.mImpl->mEventData->mState )
400     {
401       controller.mImpl->ChangeState( EventData::EDITING );
402
403       Event event( Event::TAP_EVENT );
404       event.p1.mUint = 1;
405       event.p2.mFloat = x;
406       event.p3.mFloat = y;
407       controller.mImpl->mEventData->mEventQueue.push_back( event );
408
409       controller.mImpl->RequestRelayout();
410     }
411     else if( !controller.mImpl->IsShowingRealText() )
412     {
413       Event event( Event::LONG_PRESS_EVENT );
414       event.p1.mInt = static_cast<int>( state );
415       event.p2.mFloat = x;
416       event.p3.mFloat = y;
417       controller.mImpl->mEventData->mEventQueue.push_back( event );
418       controller.mImpl->RequestRelayout();
419     }
420     else if( !controller.mImpl->IsClipboardVisible() )
421     {
422       // Reset the InputMethodContext to commit the pre-edit before selecting the text.
423       controller.mImpl->ResetInputMethodContext();
424
425       Event event( Event::LONG_PRESS_EVENT );
426       event.p1.mInt = static_cast<int>( state );
427       event.p2.mFloat = x;
428       event.p3.mFloat = y;
429       controller.mImpl->mEventData->mEventQueue.push_back( event );
430       controller.mImpl->RequestRelayout();
431
432       controller.mImpl->mEventData->mIsLeftHandleSelected = true;
433       controller.mImpl->mEventData->mIsRightHandleSelected = true;
434     }
435   }
436 }
437
438 void Controller::EventHandler::SelectEvent(Controller& controller, float x, float y, SelectionType selectType)
439 {
440   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
441
442   if( NULL != controller.mImpl->mEventData )
443   {
444     if( selectType == SelectionType::ALL )
445     {
446       Event event( Event::SELECT_ALL );
447       controller.mImpl->mEventData->mEventQueue.push_back( event );
448     }
449     else if( selectType == SelectionType::NONE )
450     {
451       Event event( Event::SELECT_NONE );
452       controller.mImpl->mEventData->mEventQueue.push_back( event );
453     }
454     else
455     {
456       Event event( Event::SELECT );
457       event.p2.mFloat = x;
458       event.p3.mFloat = y;
459       controller.mImpl->mEventData->mEventQueue.push_back( event );
460     }
461
462     controller.mImpl->mEventData->mCheckScrollAmount = true;
463     controller.mImpl->mEventData->mIsLeftHandleSelected = true;
464     controller.mImpl->mEventData->mIsRightHandleSelected = true;
465     controller.mImpl->RequestRelayout();
466   }
467 }
468
469 void Controller::EventHandler::ProcessModifyEvents(Controller& controller)
470 {
471   Vector<ModifyEvent>& events = controller.mImpl->mModifyEvents;
472
473   if( 0u == events.Count() )
474   {
475     // Nothing to do.
476     return;
477   }
478
479   for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
480          endIt = events.End();
481        it != endIt;
482        ++it )
483   {
484     const ModifyEvent& event = *it;
485
486     if( ModifyEvent::TEXT_REPLACED == event.type )
487     {
488       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
489       DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
490
491       controller.TextReplacedEvent();
492     }
493     else if( ModifyEvent::TEXT_INSERTED == event.type )
494     {
495       controller.TextInsertedEvent();
496     }
497     else if( ModifyEvent::TEXT_DELETED == event.type )
498     {
499       // Placeholder-text cannot be deleted
500       if( !controller.mImpl->IsShowingPlaceholderText() )
501       {
502         controller.TextDeletedEvent();
503       }
504     }
505   }
506
507   if( NULL != controller.mImpl->mEventData )
508   {
509     // When the text is being modified, delay cursor blinking
510     controller.mImpl->mEventData->mDecorator->DelayCursorBlink();
511
512     // Update selection position after modifying the text
513     controller.mImpl->mEventData->mLeftSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
514     controller.mImpl->mEventData->mRightSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
515   }
516
517   // DISCARD temporary text
518   events.Clear();
519 }
520
521 void Controller::EventHandler::TextReplacedEvent(Controller& controller)
522 {
523   // The natural size needs to be re-calculated.
524   controller.mImpl->mRecalculateNaturalSize = true;
525
526   // The text direction needs to be updated.
527   controller.mImpl->mUpdateTextDirection = true;
528
529   // Apply modifications to the model
530   controller.mImpl->mOperationsPending = ALL_OPERATIONS;
531 }
532
533 void Controller::EventHandler::TextInsertedEvent(Controller& controller)
534 {
535   DALI_ASSERT_DEBUG( NULL != controller.mImpl->mEventData && "Unexpected TextInsertedEvent" );
536
537   if( NULL == controller.mImpl->mEventData )
538   {
539     return;
540   }
541
542   controller.mImpl->mEventData->mCheckScrollAmount = true;
543
544   // The natural size needs to be re-calculated.
545   controller.mImpl->mRecalculateNaturalSize = true;
546
547   // The text direction needs to be updated.
548   controller.mImpl->mUpdateTextDirection = true;
549
550   // Apply modifications to the model; TODO - Optimize this
551   controller.mImpl->mOperationsPending = ALL_OPERATIONS;
552 }
553
554 void Controller::EventHandler::TextDeletedEvent(Controller& controller)
555 {
556   DALI_ASSERT_DEBUG( NULL != controller.mImpl->mEventData && "Unexpected TextDeletedEvent" );
557
558   if( NULL == controller.mImpl->mEventData )
559   {
560     return;
561   }
562
563   if (!controller.IsEditable()) return;
564
565   controller.mImpl->mEventData->mCheckScrollAmount = true;
566
567   // The natural size needs to be re-calculated.
568   controller.mImpl->mRecalculateNaturalSize = true;
569
570   // The text direction needs to be updated.
571   controller.mImpl->mUpdateTextDirection = true;
572
573   // Apply modifications to the model; TODO - Optimize this
574   controller.mImpl->mOperationsPending = ALL_OPERATIONS;
575 }
576
577 bool Controller::EventHandler::DeleteEvent(Controller& controller, int keyCode)
578 {
579   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p KeyCode : %d \n", &controller, keyCode );
580
581   bool removed = false;
582
583   if( NULL == controller.mImpl->mEventData )
584   {
585     return removed;
586   }
587
588   if (!controller.IsEditable()) return false;
589
590   // InputMethodContext is no longer handling key-events
591   controller.mImpl->ClearPreEditFlag();
592
593   if( EventData::SELECTING == controller.mImpl->mEventData->mState )
594   {
595     removed = controller.RemoveSelectedText();
596   }
597   else if( ( controller.mImpl->mEventData->mPrimaryCursorPosition > 0 ) && ( keyCode == Dali::DALI_KEY_BACKSPACE) )
598   {
599     // Remove the character before the current cursor position
600     removed = controller.RemoveText( -1,
601                           1,
602                           UPDATE_INPUT_STYLE );
603   }
604   else if( keyCode == Dali::DevelKey::DALI_KEY_DELETE )
605   {
606     // Remove the character after the current cursor position
607     removed = controller.RemoveText( 0,
608                           1,
609                           UPDATE_INPUT_STYLE );
610   }
611
612   if( removed )
613   {
614     if( ( 0u != controller.mImpl->mModel->mLogicalModel->mText.Count() ) ||
615         !controller.mImpl->IsPlaceholderAvailable() )
616     {
617       controller.mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
618     }
619     else
620     {
621       controller.ShowPlaceholderText();
622     }
623     controller.mImpl->mEventData->mUpdateCursorPosition = true;
624     controller.mImpl->mEventData->mScrollAfterDelete = true;
625   }
626
627   return removed;
628 }
629
630 InputMethodContext::CallbackData Controller::EventHandler::OnInputMethodContextEvent(Controller& controller, InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent)
631 {
632   // Whether the text needs to be relaid-out.
633   bool requestRelayout = false;
634
635   // Whether to retrieve the text and cursor position to be sent to the InputMethodContext.
636   bool retrieveText = false;
637   bool retrieveCursor = false;
638
639   switch( inputMethodContextEvent.eventName )
640   {
641     case InputMethodContext::COMMIT:
642     {
643       controller.InsertText( inputMethodContextEvent.predictiveString, Text::Controller::COMMIT );
644       requestRelayout = true;
645       retrieveCursor = true;
646       break;
647     }
648     case InputMethodContext::PRE_EDIT:
649     {
650       controller.InsertText( inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT );
651       requestRelayout = true;
652       retrieveCursor = true;
653       break;
654     }
655     case InputMethodContext::DELETE_SURROUNDING:
656     {
657       const bool textDeleted = controller.RemoveText( inputMethodContextEvent.cursorOffset,
658                                                       inputMethodContextEvent.numberOfChars,
659                                                       DONT_UPDATE_INPUT_STYLE );
660
661       if( textDeleted )
662       {
663         if( ( 0u != controller.mImpl->mModel->mLogicalModel->mText.Count() ) ||
664             !controller.mImpl->IsPlaceholderAvailable() )
665         {
666           controller.mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
667         }
668         else
669         {
670           controller.ShowPlaceholderText();
671         }
672         controller.mImpl->mEventData->mUpdateCursorPosition = true;
673         controller.mImpl->mEventData->mScrollAfterDelete = true;
674
675         requestRelayout = true;
676       }
677       break;
678     }
679     case InputMethodContext::GET_SURROUNDING:
680     {
681       retrieveText = true;
682       retrieveCursor = true;
683       break;
684     }
685     case InputMethodContext::PRIVATE_COMMAND:
686     {
687       // PRIVATECOMMAND event is just for getting the private command message
688       retrieveText = true;
689       retrieveCursor = true;
690       break;
691     }
692     case InputMethodContext::VOID:
693     {
694       // do nothing
695       break;
696     }
697   } // end switch
698
699   if( requestRelayout )
700   {
701     controller.mImpl->mOperationsPending = ALL_OPERATIONS;
702     controller.mImpl->RequestRelayout();
703   }
704
705   std::string text;
706   CharacterIndex cursorPosition = 0u;
707   Length numberOfWhiteSpaces = 0u;
708
709   if( retrieveCursor )
710   {
711     numberOfWhiteSpaces = controller.mImpl->GetNumberOfWhiteSpaces( 0u );
712
713     cursorPosition = controller.mImpl->GetLogicalCursorPosition();
714
715     if( cursorPosition < numberOfWhiteSpaces )
716     {
717       cursorPosition = 0u;
718     }
719     else
720     {
721       cursorPosition -= numberOfWhiteSpaces;
722     }
723   }
724
725   if( retrieveText )
726   {
727     if( !controller.mImpl->IsShowingPlaceholderText() )
728     {
729       // Retrieves the normal text string.
730       controller.mImpl->GetText( numberOfWhiteSpaces, text );
731     }
732     else
733     {
734       // When the current text is Placeholder Text, the surrounding text should be empty string.
735       // It means DALi should send empty string ("") to IME.
736       text = "";
737     }
738   }
739
740   InputMethodContext::CallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
741
742   if( requestRelayout &&
743       ( NULL != controller.mImpl->mEditableControlInterface ) )
744   {
745     // Do this last since it provides callbacks into application code
746     controller.mImpl->mEditableControlInterface->TextChanged();
747   }
748
749   return callbackData;
750 }
751
752 void Controller::EventHandler::PasteClipboardItemEvent(Controller& controller)
753 {
754   // Retrieve the clipboard contents first
755   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
756   std::string stringToPaste( notifier.GetContent() );
757
758   // Commit the current pre-edit text; the contents of the clipboard should be appended
759   controller.mImpl->ResetInputMethodContext();
760
761   // Temporary disable hiding clipboard
762   controller.mImpl->SetClipboardHideEnable( false );
763
764   // Paste
765   controller.PasteText( stringToPaste );
766
767   controller.mImpl->SetClipboardHideEnable( true );
768 }
769
770 void Controller::EventHandler::DecorationEvent(Controller& controller, HandleType handleType, HandleState state, float x, float y)
771 {
772   DALI_ASSERT_DEBUG( controller.mImpl->mEventData && "Unexpected DecorationEvent" );
773
774   if( NULL != controller.mImpl->mEventData )
775   {
776     switch( handleType )
777     {
778       case GRAB_HANDLE:
779       {
780         Event event( Event::GRAB_HANDLE_EVENT );
781         event.p1.mUint  = state;
782         event.p2.mFloat = x;
783         event.p3.mFloat = y;
784
785         controller.mImpl->mEventData->mEventQueue.push_back( event );
786         break;
787       }
788       case LEFT_SELECTION_HANDLE:
789       {
790         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
791         event.p1.mUint  = state;
792         event.p2.mFloat = x;
793         event.p3.mFloat = y;
794
795         controller.mImpl->mEventData->mEventQueue.push_back( event );
796         break;
797       }
798       case RIGHT_SELECTION_HANDLE:
799       {
800         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
801         event.p1.mUint  = state;
802         event.p2.mFloat = x;
803         event.p3.mFloat = y;
804
805         controller.mImpl->mEventData->mEventQueue.push_back( event );
806         break;
807       }
808       case LEFT_SELECTION_HANDLE_MARKER:
809       case RIGHT_SELECTION_HANDLE_MARKER:
810       {
811         // Markers do not move the handles.
812         break;
813       }
814       case HANDLE_TYPE_COUNT:
815       {
816         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
817       }
818     }
819
820     controller.mImpl->RequestRelayout();
821   }
822 }
823
824 void Controller::EventHandler::TextPopupButtonTouched(Controller& controller, Dali::Toolkit::TextSelectionPopup::Buttons button)
825 {
826   if( NULL == controller.mImpl->mEventData )
827   {
828     return;
829   }
830
831   switch( button )
832   {
833     case Toolkit::TextSelectionPopup::CUT:
834     {
835       if (!controller.IsEditable()) return;
836       controller.mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
837       controller.mImpl->mOperationsPending = ALL_OPERATIONS;
838
839       if( ( 0u != controller.mImpl->mModel->mLogicalModel->mText.Count() ) ||
840           !controller.mImpl->IsPlaceholderAvailable() )
841       {
842         controller.mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
843       }
844       else
845       {
846         controller.ShowPlaceholderText();
847       }
848
849       controller.mImpl->mEventData->mUpdateCursorPosition = true;
850       controller.mImpl->mEventData->mScrollAfterDelete = true;
851
852       controller.mImpl->RequestRelayout();
853
854       if( NULL != controller.mImpl->mEditableControlInterface )
855       {
856         controller.mImpl->mEditableControlInterface->TextChanged();
857       }
858       break;
859     }
860     case Toolkit::TextSelectionPopup::COPY:
861     {
862       controller.mImpl->SendSelectionToClipboard( false ); // Text not modified
863
864       controller.mImpl->mEventData->mUpdateCursorPosition = true;
865
866       controller.mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
867       break;
868     }
869     case Toolkit::TextSelectionPopup::PASTE:
870     {
871       controller.mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
872       break;
873     }
874     case Toolkit::TextSelectionPopup::SELECT:
875     {
876       const Vector2& currentCursorPosition = controller.mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
877
878       if( controller.mImpl->mEventData->mSelectionEnabled )
879       {
880         // Creates a SELECT event.
881         controller.SelectEvent( currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE );
882       }
883       break;
884     }
885     case Toolkit::TextSelectionPopup::SELECT_ALL:
886     {
887       // Creates a SELECT_ALL event
888       controller.SelectEvent( 0.f, 0.f, SelectionType::ALL );
889       break;
890     }
891     case Toolkit::TextSelectionPopup::CLIPBOARD:
892     {
893       controller.mImpl->ShowClipboard();
894       break;
895     }
896     case Toolkit::TextSelectionPopup::NONE:
897     {
898       // Nothing to do.
899       break;
900     }
901   }
902 }
903
904 } // namespace Text
905
906 } // namespace Toolkit
907
908 } // namespace Dali