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