Merge "Text controller checks data type of the clipboard" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / controller / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2024 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/controller/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/adaptor-framework/scene-holder.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/actors/layer.h>
25 #include <dali/public-api/rendering/renderer.h>
26 #include <cmath>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/controller/text-controller-impl-data-clearer.h>
31 #include <dali-toolkit/internal/text/controller/text-controller-impl-event-handler.h>
32 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
33 #include <dali-toolkit/internal/text/controller/text-controller-placeholder-handler.h>
34 #include <dali-toolkit/internal/text/controller/text-controller-relayouter.h>
35 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
36 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
37 #include <dali-toolkit/internal/text/text-control-interface.h>
38 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
39 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
40 #include <dali-toolkit/internal/text/text-run-container.h>
41 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
42 #include <dali-toolkit/internal/text/underlined-glyph-run.h>
43
44 using namespace Dali;
45
46 namespace
47 {
48 #if defined(DEBUG_ENABLED)
49 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
50 #endif
51
52 constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
53
54 const char* EMPTY_STRING         = "";
55 const char* MIME_TYPE_TEXT_PLAIN = "text/plain;charset=utf-8";
56 const char* MIME_TYPE_HTML       = "application/xhtml+xml";
57
58 } // namespace
59
60 namespace Dali::Toolkit::Text
61 {
62 namespace
63 {
64 void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
65 {
66   // Sets the default text's color.
67   inputStyle.textColor      = textColor;
68   inputStyle.isDefaultColor = true;
69
70   inputStyle.familyName.clear();
71   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
72   inputStyle.width  = TextAbstraction::FontWidth::NORMAL;
73   inputStyle.slant  = TextAbstraction::FontSlant::NORMAL;
74   inputStyle.size   = 0.f;
75
76   inputStyle.lineSpacing = 0.f;
77
78   inputStyle.underlineProperties.clear();
79   inputStyle.shadowProperties.clear();
80   inputStyle.embossProperties.clear();
81   inputStyle.outlineProperties.clear();
82
83   inputStyle.isFamilyDefined = false;
84   inputStyle.isWeightDefined = false;
85   inputStyle.isWidthDefined  = false;
86   inputStyle.isSlantDefined  = false;
87   inputStyle.isSizeDefined   = false;
88
89   inputStyle.isLineSpacingDefined = false;
90
91   inputStyle.isUnderlineDefined = false;
92   inputStyle.isShadowDefined    = false;
93   inputStyle.isEmbossDefined    = false;
94   inputStyle.isOutlineDefined   = false;
95
96   // Sets the default font's family name, weight, width, slant and size.
97   if(fontDefaults)
98   {
99     if(fontDefaults->familyDefined)
100     {
101       inputStyle.familyName      = fontDefaults->mFontDescription.family;
102       inputStyle.isFamilyDefined = true;
103     }
104
105     if(fontDefaults->weightDefined)
106     {
107       inputStyle.weight          = fontDefaults->mFontDescription.weight;
108       inputStyle.isWeightDefined = true;
109     }
110
111     if(fontDefaults->widthDefined)
112     {
113       inputStyle.width          = fontDefaults->mFontDescription.width;
114       inputStyle.isWidthDefined = true;
115     }
116
117     if(fontDefaults->slantDefined)
118     {
119       inputStyle.slant          = fontDefaults->mFontDescription.slant;
120       inputStyle.isSlantDefined = true;
121     }
122
123     if(fontDefaults->sizeDefined)
124     {
125       inputStyle.size          = fontDefaults->mDefaultPointSize;
126       inputStyle.isSizeDefined = true;
127     }
128   }
129 }
130
131 void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState)
132 {
133   EventData* eventData = impl.mEventData;
134
135   if(nullptr == eventData)
136   {
137     // Nothing to do if there is no text input.
138     return;
139   }
140
141   DecoratorPtr& decorator = eventData->mDecorator;
142   if(!decorator)
143   {
144     // Nothing to do if there is no decorator.
145     return;
146   }
147
148   DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", eventData->mState, newState);
149
150   if(eventData->mState != newState)
151   {
152     eventData->mPreviousState = eventData->mState;
153     eventData->mState         = newState;
154
155     switch(eventData->mState)
156     {
157       case EventData::INACTIVE:
158       {
159         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
160         decorator->StopCursorBlink();
161         decorator->SetHandleActive(GRAB_HANDLE, false);
162         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
163         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
164         decorator->SetHighlightActive(false);
165         decorator->SetPopupActive(false);
166         eventData->mDecoratorUpdated = true;
167         break;
168       }
169
170       case EventData::INTERRUPTED:
171       {
172         decorator->SetHandleActive(GRAB_HANDLE, false);
173         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
174         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
175         decorator->SetHighlightActive(false);
176         decorator->SetPopupActive(false);
177         eventData->mDecoratorUpdated = true;
178         break;
179       }
180
181       case EventData::SELECTING:
182       {
183         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
184         decorator->StopCursorBlink();
185         decorator->SetHandleActive(GRAB_HANDLE, false);
186         if(eventData->mGrabHandleEnabled)
187         {
188           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
189           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
190         }
191         decorator->SetHighlightActive(true);
192         if(eventData->mGrabHandlePopupEnabled)
193         {
194           impl.SetPopupButtons();
195           decorator->SetPopupActive(true);
196         }
197         eventData->mDecoratorUpdated = true;
198         break;
199       }
200
201       case EventData::EDITING:
202       {
203         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
204         if(eventData->mCursorBlinkEnabled)
205         {
206           decorator->StartCursorBlink();
207         }
208         // Grab handle is not shown until a tap is received whilst EDITING
209         decorator->SetHandleActive(GRAB_HANDLE, false);
210         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
211         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
212         decorator->SetHighlightActive(false);
213         if(eventData->mGrabHandlePopupEnabled)
214         {
215           decorator->SetPopupActive(false);
216         }
217         eventData->mDecoratorUpdated = true;
218         break;
219       }
220       case EventData::EDITING_WITH_POPUP:
221       {
222         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
223
224         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
225         if(eventData->mCursorBlinkEnabled)
226         {
227           decorator->StartCursorBlink();
228         }
229         if(eventData->mSelectionEnabled)
230         {
231           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
232           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
233           decorator->SetHighlightActive(false);
234         }
235         else if(eventData->mGrabHandleEnabled)
236         {
237           decorator->SetHandleActive(GRAB_HANDLE, true);
238         }
239         if(eventData->mGrabHandlePopupEnabled)
240         {
241           impl.SetPopupButtons();
242           decorator->SetPopupActive(true);
243         }
244         eventData->mDecoratorUpdated = true;
245         break;
246       }
247       case EventData::EDITING_WITH_GRAB_HANDLE:
248       {
249         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
250
251         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
252         if(eventData->mCursorBlinkEnabled)
253         {
254           decorator->StartCursorBlink();
255         }
256         // Grab handle is not shown until a tap is received whilst EDITING
257         if(eventData->mGrabHandleEnabled)
258         {
259           decorator->SetHandleActive(GRAB_HANDLE, true);
260         }
261         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
262         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
263         decorator->SetHighlightActive(false);
264         if(eventData->mGrabHandlePopupEnabled)
265         {
266           decorator->SetPopupActive(false);
267         }
268         eventData->mDecoratorUpdated = true;
269         break;
270       }
271
272       case EventData::SELECTION_HANDLE_PANNING:
273       {
274         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
275         decorator->StopCursorBlink();
276         decorator->SetHandleActive(GRAB_HANDLE, false);
277         if(eventData->mGrabHandleEnabled)
278         {
279           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
280           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
281         }
282         decorator->SetHighlightActive(true);
283         if(eventData->mGrabHandlePopupEnabled)
284         {
285           decorator->SetPopupActive(false);
286         }
287         eventData->mDecoratorUpdated = true;
288         break;
289       }
290
291       case EventData::GRAB_HANDLE_PANNING:
292       {
293         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
294
295         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
296         if(eventData->mCursorBlinkEnabled)
297         {
298           decorator->StartCursorBlink();
299         }
300         if(eventData->mGrabHandleEnabled)
301         {
302           decorator->SetHandleActive(GRAB_HANDLE, true);
303         }
304         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
305         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
306         decorator->SetHighlightActive(false);
307         if(eventData->mGrabHandlePopupEnabled)
308         {
309           decorator->SetPopupActive(false);
310         }
311         eventData->mDecoratorUpdated = true;
312         break;
313       }
314
315       case EventData::EDITING_WITH_PASTE_POPUP:
316       {
317         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
318
319         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
320         if(eventData->mCursorBlinkEnabled)
321         {
322           decorator->StartCursorBlink();
323         }
324
325         if(eventData->mGrabHandleEnabled)
326         {
327           decorator->SetHandleActive(GRAB_HANDLE, true);
328         }
329         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
330         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
331         decorator->SetHighlightActive(false);
332
333         if(eventData->mGrabHandlePopupEnabled)
334         {
335           impl.SetPopupButtons();
336           decorator->SetPopupActive(true);
337         }
338         eventData->mDecoratorUpdated = true;
339         break;
340       }
341
342       case EventData::TEXT_PANNING:
343       {
344         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
345         decorator->StopCursorBlink();
346         decorator->SetHandleActive(GRAB_HANDLE, false);
347         if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
348            decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
349         {
350           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
351           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
352           decorator->SetHighlightActive(true);
353         }
354
355         if(eventData->mGrabHandlePopupEnabled)
356         {
357           decorator->SetPopupActive(false);
358         }
359
360         eventData->mDecoratorUpdated = true;
361         break;
362       }
363     }
364   }
365 }
366
367 void UpdateCursorPositionForAlignment(Controller::Impl& impl, bool needFullAlignment)
368 {
369   EventData* eventData = impl.mEventData;
370
371   // Set the flag to redo the alignment operation
372   impl.mOperationsPending = static_cast<Controller::OperationsMask>(impl.mOperationsPending | Controller::OperationsMask::ALIGN);
373
374   if(eventData)
375   {
376     // Note: mUpdateAlignment is currently only needed for horizontal alignment
377     eventData->mUpdateAlignment = needFullAlignment;
378
379     // Update the cursor if it's in editing mode
380     if(EventData::IsEditingState(eventData->mState))
381     {
382       impl.ChangeState(EventData::EDITING);
383       eventData->mUpdateCursorPosition = true;
384     }
385   }
386 }
387
388 } // unnamed Namespace
389
390 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
391 : mDecorator(decorator),
392   mInputMethodContext(inputMethodContext),
393   mPlaceholderFont(nullptr),
394   mPlaceholderTextActive(),
395   mPlaceholderTextInactive(),
396   mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
397   mEventQueue(),
398   mInputStyleChangedQueue(),
399   mPreviousState(INACTIVE),
400   mState(INACTIVE),
401   mPrimaryCursorPosition(0u),
402   mLeftSelectionPosition(0u),
403   mRightSelectionPosition(0u),
404   mPreEditStartPosition(0u),
405   mPreEditLength(0u),
406   mCursorHookPositionX(0.f),
407   mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
408   mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
409   mIsShowingPlaceholderText(false),
410   mPreEditFlag(false),
411   mDecoratorUpdated(false),
412   mCursorBlinkEnabled(true),
413   mGrabHandleEnabled(true),
414   mGrabHandlePopupEnabled(true),
415   mSelectionEnabled(true),
416   mUpdateCursorHookPosition(false),
417   mUpdateCursorPosition(false),
418   mUpdateGrabHandlePosition(false),
419   mUpdateLeftSelectionPosition(false),
420   mUpdateRightSelectionPosition(false),
421   mIsLeftHandleSelected(false),
422   mIsRightHandleSelected(false),
423   mUpdateHighlightBox(false),
424   mScrollAfterUpdatePosition(false),
425   mScrollAfterDelete(false),
426   mAllTextSelected(false),
427   mUpdateInputStyle(false),
428   mPasswordInput(false),
429   mCheckScrollAmount(false),
430   mIsPlaceholderPixelSize(false),
431   mIsPlaceholderElideEnabled(false),
432   mPlaceholderEllipsisFlag(false),
433   mShiftSelectionFlag(true),
434   mUpdateAlignment(false),
435   mEditingEnabled(true)
436 {
437 }
438
439 bool Controller::Impl::ProcessInputEvents()
440 {
441   return ControllerImplEventHandler::ProcessInputEvents(*this);
442 }
443
444 void Controller::Impl::SetAnchorColor(const Vector4& color)
445 {
446   mAnchorColor = color;
447   UpdateAnchorColor();
448 }
449
450 const Vector4& Controller::Impl::GetAnchorColor() const
451 {
452   return mAnchorColor;
453 }
454
455 void Controller::Impl::SetAnchorClickedColor(const Vector4& color)
456 {
457   mAnchorClickedColor = color;
458   UpdateAnchorColor();
459 }
460
461 const Vector4& Controller::Impl::GetAnchorClickedColor() const
462 {
463   return mAnchorClickedColor;
464 }
465
466 void Controller::Impl::UpdateAnchorColor()
467 {
468   if(!mAnchorControlInterface ||
469      !mMarkupProcessorEnabled ||
470      !mModel->mLogicalModel->mAnchors.Count() ||
471      !IsShowingRealText())
472   {
473     return;
474   }
475
476   bool updateNeeded = false;
477
478   // The anchor color & clicked color needs to be updated with the property's color.
479   for(auto& anchor : mModel->mLogicalModel->mAnchors)
480   {
481     if(!anchor.isMarkupColorSet && !anchor.isClicked)
482     {
483       if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
484       {
485         ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
486         colorRun.color     = mAnchorColor;
487         updateNeeded       = true;
488       }
489       if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
490       {
491         UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
492         underlineRun.properties.color        = mAnchorColor;
493         updateNeeded                         = true;
494       }
495     }
496     else if(!anchor.isMarkupClickedColorSet && anchor.isClicked)
497     {
498       if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
499       {
500         ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
501         colorRun.color     = mAnchorClickedColor;
502         updateNeeded       = true;
503       }
504       if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
505       {
506         UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
507         underlineRun.properties.color        = mAnchorClickedColor;
508         updateNeeded                         = true;
509       }
510     }
511   }
512
513   if(updateNeeded)
514   {
515     ClearFontData();
516     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
517     RequestRelayout();
518   }
519 }
520
521 void Controller::Impl::NotifyInputMethodContext()
522 {
523   if(mEventData && mEventData->mInputMethodContext)
524   {
525     CharacterIndex cursorPosition = GetLogicalCursorPosition();
526     mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
527     mEventData->mInputMethodContext.NotifyCursorPosition();
528   }
529 }
530
531 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
532 {
533   if(mEventData && mEventData->mInputMethodContext)
534   {
535     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
536     mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
537   }
538 }
539
540 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
541 {
542   CharacterIndex cursorPosition = 0u;
543
544   if(mEventData)
545   {
546     if((EventData::SELECTING == mEventData->mState) ||
547        (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
548     {
549       cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
550     }
551     else
552     {
553       cursorPosition = mEventData->mPrimaryCursorPosition;
554     }
555   }
556
557   return cursorPosition;
558 }
559
560 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
561 {
562   Length numberOfWhiteSpaces = 0u;
563
564   // Get the buffer to the text.
565   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
566
567   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
568   for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
569   {
570     if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
571     {
572       break;
573     }
574   }
575
576   return numberOfWhiteSpaces;
577 }
578
579 void Controller::Impl::GetText(std::string& text) const
580 {
581   if(!IsShowingPlaceholderText())
582   {
583     // Retrieves the text string.
584     GetText(0u, text);
585   }
586   else
587   {
588     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
589   }
590 }
591
592 Length Controller::Impl::GetNumberOfCharacters() const
593 {
594   if(!IsShowingPlaceholderText())
595   {
596     return mModel->GetNumberOfCharacters();
597   }
598   else
599   {
600     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetNumberOfCharacters %p empty (but showing placeholder)\n", this);
601     return 0u;
602   }
603 }
604
605 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
606 {
607   // Get the total number of characters.
608   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
609
610   // Retrieve the text.
611   if(0u != numberOfCharacters)
612   {
613     Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
614   }
615 }
616
617 Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const
618 {
619   if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
620      (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
621   {
622     Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(actor);
623     return static_cast<Dali::LayoutDirection::Type>(sceneHolder ? sceneHolder.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
624   }
625   else
626   {
627     return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
628   }
629 }
630
631 Toolkit::DevelText::TextDirection::Type Controller::Impl::GetTextDirection()
632 {
633   if(mUpdateTextDirection)
634   {
635     // Operations that can be done only once until the text changes.
636     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
637                                                                           GET_SCRIPTS |
638                                                                           VALIDATE_FONTS |
639                                                                           GET_LINE_BREAKS |
640                                                                           BIDI_INFO |
641                                                                           SHAPE_TEXT |
642                                                                           GET_GLYPH_METRICS);
643
644     // Set the update info to relayout the whole text.
645     mTextUpdateInfo.mParagraphCharacterIndex     = 0u;
646     mTextUpdateInfo.mRequestedNumberOfCharacters = mModel->mLogicalModel->mText.Count();
647
648     // Make sure the model is up-to-date before layouting
649     UpdateModel(onlyOnceOperations);
650
651     Vector3 naturalSize;
652     Relayouter::DoRelayout(*this,
653                            Size(MAX_FLOAT, MAX_FLOAT),
654                            static_cast<OperationsMask>(onlyOnceOperations |
655                                                        LAYOUT | REORDER | UPDATE_DIRECTION),
656                            naturalSize.GetVectorXY());
657
658     // Do not do again the only once operations.
659     mOperationsPending = static_cast<OperationsMask>(mOperationsPending & ~onlyOnceOperations);
660
661     // Clear the update info. This info will be set the next time the text is updated.
662     mTextUpdateInfo.Clear();
663
664     // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
665     mTextUpdateInfo.mFullRelayoutNeeded = true;
666
667     mUpdateTextDirection = false;
668   }
669
670   return mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
671 }
672
673 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
674 {
675   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
676   mTextUpdateInfo.mStartGlyphIndex         = 0u;
677   mTextUpdateInfo.mStartLineIndex          = 0u;
678   numberOfCharacters                       = 0u;
679
680   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
681   if(0u == numberOfParagraphs)
682   {
683     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
684     numberOfCharacters                       = 0u;
685
686     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
687
688     // Nothing else to do if there are no paragraphs.
689     return;
690   }
691
692   // Find the paragraphs to be updated.
693   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
694   if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
695   {
696     // Text is being added at the end of the current text.
697     if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
698     {
699       // Text is being added in a new paragraph after the last character of the text.
700       mTextUpdateInfo.mParagraphCharacterIndex     = mTextUpdateInfo.mPreviousNumberOfCharacters;
701       numberOfCharacters                           = 0u;
702       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
703
704       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
705       mTextUpdateInfo.mStartLineIndex  = (mModel->mVisualModel->mLines.Count() > 0u) ? mModel->mVisualModel->mLines.Count() - 1u : 0u;
706
707       // Nothing else to do;
708       return;
709     }
710
711     paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
712   }
713   else
714   {
715     Length numberOfCharactersToUpdate = 0u;
716     if(mTextUpdateInfo.mFullRelayoutNeeded)
717     {
718       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
719     }
720     else
721     {
722       numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
723     }
724     mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
725                                           numberOfCharactersToUpdate,
726                                           paragraphsToBeUpdated);
727   }
728
729   if(0u != paragraphsToBeUpdated.Count())
730   {
731     const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
732     const ParagraphRun&     firstParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
733     mTextUpdateInfo.mParagraphCharacterIndex    = firstParagraph.characterRun.characterIndex;
734
735     ParagraphRunIndex   lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
736     const ParagraphRun& lastParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
737
738     if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) &&                                           // Some character are removed.
739        (lastParagraphIndex < numberOfParagraphs - 1u) &&                                               // There is a next paragraph.
740        ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
741         (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
742     {
743       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
744       const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
745
746       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
747     }
748     else
749     {
750       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
751     }
752   }
753
754   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
755   mTextUpdateInfo.mStartGlyphIndex             = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
756 }
757
758 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
759 {
760   ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
761 }
762
763 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
764 {
765   return ControllerImplModelUpdater::Update(*this, operationsRequired);
766 }
767
768 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
769 {
770   SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
771 }
772
773 float Controller::Impl::GetDefaultFontLineHeight()
774 {
775   FontId defaultFontId = 0u;
776   if(nullptr == mFontDefaults)
777   {
778     TextAbstraction::FontDescription fontDescription;
779     defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * GetFontSizeScale());
780   }
781   else
782   {
783     defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale());
784   }
785
786   Text::FontMetrics fontMetrics;
787   mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
788
789   return (fontMetrics.ascender - fontMetrics.descender);
790 }
791
792 bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
793 {
794   if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
795   {
796     mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
797
798     RelayoutAllCharacters();
799     return true;
800   }
801   return false;
802 }
803
804 bool Controller::Impl::SetDefaultLineSize(float lineSize)
805 {
806   if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
807   {
808     mLayoutEngine.SetDefaultLineSize(lineSize);
809
810     RelayoutAllCharacters();
811     return true;
812   }
813   return false;
814 }
815
816 bool Controller::Impl::SetRelativeLineSize(float relativeLineSize)
817 {
818   if(std::fabs(relativeLineSize - GetRelativeLineSize()) > Math::MACHINE_EPSILON_1000)
819   {
820     mLayoutEngine.SetRelativeLineSize(relativeLineSize);
821
822     RelayoutAllCharacters();
823     return true;
824   }
825   return false;
826 }
827
828 float Controller::Impl::GetRelativeLineSize()
829 {
830   return mLayoutEngine.GetRelativeLineSize();
831 }
832
833 string Controller::Impl::GetSelectedText()
834 {
835   string text;
836   if(EventData::SELECTING == mEventData->mState)
837   {
838     RetrieveSelection(text, false);
839   }
840   return text;
841 }
842
843 string Controller::Impl::CopyText()
844 {
845   string text;
846   RetrieveSelection(text, false);
847   SendSelectionToClipboard(false); // Text not modified
848
849   mEventData->mUpdateCursorPosition = true;
850
851   RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
852
853   return text;
854 }
855
856 string Controller::Impl::CutText()
857 {
858   string text;
859   RetrieveSelection(text, false);
860
861   if(!IsEditable())
862   {
863     return EMPTY_STRING;
864   }
865
866   SendSelectionToClipboard(true); // Synchronous call to modify text
867   mOperationsPending = ALL_OPERATIONS;
868
869   if((0u != mModel->mLogicalModel->mText.Count()) ||
870      !IsPlaceholderAvailable())
871   {
872     QueueModifyEvent(ModifyEvent::TEXT_DELETED);
873   }
874   else
875   {
876     PlaceholderHandler::ShowPlaceholderText(*this);
877   }
878
879   mEventData->mUpdateCursorPosition = true;
880   mEventData->mScrollAfterDelete    = true;
881
882   RequestRelayout();
883
884   if(nullptr != mEditableControlInterface)
885   {
886     mEditableControlInterface->TextChanged(true);
887   }
888   return text;
889 }
890
891 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
892 {
893   if(nullptr == mEventData)
894   {
895     // Nothing to do if there is no text.
896     return;
897   }
898
899   if(mEventData->mSelectionEnabled && (pStart || pEnd))
900   {
901     uint32_t length   = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
902     uint32_t oldStart = mEventData->mLeftSelectionPosition;
903     uint32_t oldEnd   = mEventData->mRightSelectionPosition;
904
905     if(pStart)
906     {
907       mEventData->mLeftSelectionPosition = std::min(*pStart, length);
908     }
909     if(pEnd)
910     {
911       mEventData->mRightSelectionPosition = std::min(*pEnd, length);
912     }
913
914     if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
915     {
916       ChangeState(EventData::EDITING);
917       mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
918       mEventData->mUpdateCursorPosition                                       = true;
919     }
920     else
921     {
922       ChangeState(EventData::SELECTING);
923       mEventData->mUpdateHighlightBox           = true;
924       mEventData->mUpdateLeftSelectionPosition  = true;
925       mEventData->mUpdateRightSelectionPosition = true;
926     }
927
928     if(mSelectableControlInterface != nullptr)
929     {
930       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
931     }
932   }
933 }
934
935 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
936 {
937   if(nullptr == mEventData)
938   {
939     return 0;
940   }
941   return mEventData->mPrimaryCursorPosition;
942 }
943
944 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
945 {
946   if(nullptr == mEventData)
947   {
948     // Nothing to do if there is no text.
949     return false;
950   }
951
952   if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
953   {
954     // Nothing for same cursor position.
955     return false;
956   }
957
958   uint32_t length                    = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
959   uint32_t oldCursorPos              = mEventData->mPrimaryCursorPosition;
960   mEventData->mPrimaryCursorPosition = std::min(index, length);
961   // If there is no focus, only the value is updated.
962   if(focused)
963   {
964     bool     wasInSelectingState = mEventData->mState == EventData::SELECTING;
965     uint32_t oldStart            = mEventData->mLeftSelectionPosition;
966     uint32_t oldEnd              = mEventData->mRightSelectionPosition;
967     ChangeState(EventData::EDITING);
968     mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
969     mEventData->mUpdateCursorPosition                                        = true;
970
971     if(mSelectableControlInterface != nullptr && wasInSelectingState)
972     {
973       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
974     }
975
976     ScrollTextToMatchCursor();
977   }
978
979   if(nullptr != mEditableControlInterface)
980   {
981     mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
982   }
983
984   return true;
985 }
986
987 Uint32Pair Controller::Impl::GetTextSelectionRange() const
988 {
989   Uint32Pair range;
990
991   if(mEventData)
992   {
993     range.first  = mEventData->mLeftSelectionPosition;
994     range.second = mEventData->mRightSelectionPosition;
995   }
996
997   return range;
998 }
999
1000 bool Controller::Impl::IsEditable() const
1001 {
1002   return mEventData && mEventData->mEditingEnabled;
1003 }
1004
1005 void Controller::Impl::SetEditable(bool editable)
1006 {
1007   if(mEventData)
1008   {
1009     mEventData->mEditingEnabled = editable;
1010
1011     if(mEventData->mDecorator)
1012     {
1013       bool decoratorEditable = editable && mIsUserInteractionEnabled;
1014       mEventData->mDecorator->SetEditable(decoratorEditable);
1015       mEventData->mDecoratorUpdated = true;
1016       RequestRelayout();
1017     }
1018   }
1019 }
1020
1021 void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont)
1022 {
1023   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
1024
1025   if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes
1026   {
1027     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str());
1028     mFontDefaults->mFontDescription.family = newDefaultFont;
1029
1030     ClearFontData();
1031
1032     RequestRelayout();
1033   }
1034 }
1035
1036 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1037 {
1038   if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1039   {
1040     // Nothing to select if handles are in the same place.
1041     selectedText.clear();
1042     return;
1043   }
1044
1045   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1046
1047   //Get start and end position of selection
1048   const CharacterIndex startOfSelectedText  = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1049   const Length         lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1050
1051   Vector<Character>& utf32Characters    = mModel->mLogicalModel->mText;
1052   const Length       numberOfCharacters = utf32Characters.Count();
1053
1054   // Validate the start and end selection points
1055   if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1056   {
1057     //Get text as a UTF8 string
1058     Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1059
1060     if(deleteAfterRetrieval) // Only delete text if copied successfully
1061     {
1062       // Keep a copy of the current input style.
1063       InputStyle currentInputStyle;
1064       currentInputStyle.Copy(mEventData->mInputStyle);
1065
1066       // Set as input style the style of the first deleted character.
1067       mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1068
1069       // Compare if the input style has changed.
1070       const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1071
1072       if(hasInputStyleChanged)
1073       {
1074         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1075         // Queue the input style changed signal.
1076         mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1077       }
1078
1079       mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1080
1081       // Mark the paragraphs to be updated.
1082       if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1083       {
1084         mTextUpdateInfo.mCharacterIndex             = 0;
1085         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1086         mTextUpdateInfo.mNumberOfCharactersToAdd    = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1087         mTextUpdateInfo.mClearAll                   = true;
1088       }
1089       else
1090       {
1091         mTextUpdateInfo.mCharacterIndex             = startOfSelectedText;
1092         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1093       }
1094
1095       // Delete text between handles
1096       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1097       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1098       utf32Characters.Erase(first, last);
1099
1100       // Will show the cursor at the first character of the selection.
1101       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1102     }
1103     else
1104     {
1105       // Will show the cursor at the last character of the selection.
1106       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1107     }
1108
1109     mEventData->mDecoratorUpdated = true;
1110   }
1111 }
1112
1113 void Controller::Impl::SetSelection(int start, int end)
1114 {
1115   uint32_t oldStart = mEventData->mLeftSelectionPosition;
1116   uint32_t oldEnd   = mEventData->mRightSelectionPosition;
1117
1118   mEventData->mLeftSelectionPosition  = start;
1119   mEventData->mRightSelectionPosition = end;
1120   mEventData->mUpdateCursorPosition   = true;
1121
1122   if(mSelectableControlInterface != nullptr)
1123   {
1124     mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
1125   }
1126 }
1127
1128 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1129 {
1130   return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1131 }
1132
1133 void Controller::Impl::ShowClipboard()
1134 {
1135   if(EnsureClipboardCreated())
1136   {
1137     mClipboard.ShowClipboard();
1138   }
1139 }
1140
1141 void Controller::Impl::HideClipboard()
1142 {
1143   if(EnsureClipboardCreated() && mClipboardHideEnabled)
1144   {
1145     mClipboard.HideClipboard();
1146   }
1147 }
1148
1149 void Controller::Impl::SetClipboardHideEnable(bool enable)
1150 {
1151   mClipboardHideEnabled = enable;
1152 }
1153
1154 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1155 {
1156   if(EnsureClipboardCreated())
1157   {
1158     Dali::Clipboard::ClipData data(MIME_TYPE_TEXT_PLAIN, source.c_str());
1159     return mClipboard.SetData(data); // Send clipboard data to clipboard.
1160   }
1161
1162   return false;
1163 }
1164
1165 bool Controller::Impl::IsClipboardEmpty()
1166 {
1167   bool result(Clipboard::IsAvailable() && EnsureClipboardCreated() && (mClipboard.HasType(MIME_TYPE_TEXT_PLAIN) || mClipboard.HasType(MIME_TYPE_HTML)));
1168   return !result;
1169 }
1170
1171 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1172 {
1173   std::string selectedText;
1174   RetrieveSelection(selectedText, deleteAfterSending);
1175   CopyStringToClipboard(selectedText);
1176   ChangeState(EventData::EDITING);
1177 }
1178
1179 void Controller::Impl::RepositionSelectionHandles()
1180 {
1181   SelectionHandleController::Reposition(*this);
1182 }
1183 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1184 {
1185   SelectionHandleController::Reposition(*this, visualX, visualY, action);
1186 }
1187
1188 void Controller::Impl::SetPopupButtons()
1189 {
1190   /**
1191    *  Sets the Popup buttons to be shown depending on State.
1192    *
1193    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1194    *
1195    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1196    */
1197
1198   bool                        isEditable    = IsEditable();
1199   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1200
1201   if(EventData::SELECTING == mEventData->mState)
1202   {
1203     buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1204     if(isEditable)
1205     {
1206       buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1207     }
1208
1209     if(!IsClipboardEmpty())
1210     {
1211       if(isEditable)
1212       {
1213         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1214       }
1215       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1216     }
1217
1218     if(!mEventData->mAllTextSelected)
1219     {
1220       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1221     }
1222   }
1223   else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1224   {
1225     if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1226     {
1227       buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1228     }
1229
1230     if(!IsClipboardEmpty())
1231     {
1232       if(isEditable)
1233       {
1234         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1235       }
1236       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1237     }
1238   }
1239   else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1240   {
1241     if(!IsClipboardEmpty())
1242     {
1243       if(isEditable)
1244       {
1245         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1246       }
1247       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1248     }
1249   }
1250
1251   mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1252 }
1253
1254 void Controller::Impl::ChangeState(EventData::State newState)
1255 {
1256   ChangeTextControllerState(*this, newState);
1257 }
1258
1259 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1260                                          CursorInfo&    cursorInfo)
1261 {
1262   if(!IsShowingRealText())
1263   {
1264     // Do not want to use the place-holder text to set the cursor position.
1265
1266     // Use the line's height of the font's family set to set the cursor's size.
1267     // If there is no font's family set, use the default font.
1268     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1269
1270     cursorInfo.lineOffset          = 0.f;
1271     cursorInfo.lineHeight          = GetDefaultFontLineHeight();
1272     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1273
1274     bool isRTL = false;
1275     if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1276     {
1277       isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1278     }
1279
1280     switch(mModel->mHorizontalAlignment)
1281     {
1282       case Text::HorizontalAlignment::BEGIN:
1283       {
1284         if(isRTL)
1285         {
1286           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1287         }
1288         else
1289         {
1290           cursorInfo.primaryPosition.x = 0.f;
1291         }
1292         break;
1293       }
1294       case Text::HorizontalAlignment::CENTER:
1295       {
1296         cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1297         break;
1298       }
1299       case Text::HorizontalAlignment::END:
1300       {
1301         if(isRTL)
1302         {
1303           cursorInfo.primaryPosition.x = 0.f;
1304         }
1305         else
1306         {
1307           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1308         }
1309         break;
1310       }
1311     }
1312
1313     // Nothing else to do.
1314     return;
1315   }
1316
1317   const bool                  isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1318   GetCursorPositionParameters parameters;
1319   parameters.visualModel  = mModel->mVisualModel;
1320   parameters.logicalModel = mModel->mLogicalModel;
1321   parameters.metrics      = mMetrics;
1322   parameters.logical      = logical;
1323   parameters.isMultiline  = isMultiLine;
1324
1325   float defaultFontLineHeight = GetDefaultFontLineHeight();
1326
1327   Text::GetCursorPosition(parameters,
1328                           defaultFontLineHeight,
1329                           cursorInfo);
1330
1331   // Adds Outline offset.
1332   const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1333   cursorInfo.primaryPosition.x += outlineWidth;
1334   cursorInfo.primaryPosition.y += outlineWidth;
1335   cursorInfo.secondaryPosition.x += outlineWidth;
1336   cursorInfo.secondaryPosition.y += outlineWidth;
1337
1338   if(isMultiLine)
1339   {
1340     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1341
1342     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1343     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1344
1345     if(0.f > cursorInfo.primaryPosition.x)
1346     {
1347       cursorInfo.primaryPosition.x = 0.f;
1348     }
1349
1350     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1351     if(cursorInfo.primaryPosition.x > edgeWidth)
1352     {
1353       cursorInfo.primaryPosition.x = edgeWidth;
1354     }
1355   }
1356 }
1357
1358 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1359 {
1360   if(nullptr == mEventData)
1361   {
1362     // Nothing to do if there is no text input.
1363     return 0u;
1364   }
1365
1366   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1367
1368   const GlyphIndex* const charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
1369   const Length* const     charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1370
1371   GlyphIndex glyphIndex         = *(charactersToGlyphBuffer + index);
1372   Length     numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1373
1374   if(numberOfCharacters > 1u)
1375   {
1376     const Script script = mModel->mLogicalModel->GetScript(index);
1377     if(HasLigatureMustBreak(script))
1378     {
1379       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1380       numberOfCharacters = 1u;
1381     }
1382   }
1383   else
1384   {
1385     while(0u == numberOfCharacters)
1386     {
1387       ++glyphIndex;
1388       numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1389     }
1390   }
1391
1392   if(index < mEventData->mPrimaryCursorPosition)
1393   {
1394     cursorIndex = cursorIndex < numberOfCharacters ? 0u : cursorIndex - numberOfCharacters;
1395   }
1396   else
1397   {
1398     Length textLength = mModel->mVisualModel->mCharactersToGlyph.Count();
1399     cursorIndex       = cursorIndex + numberOfCharacters > textLength ? textLength : cursorIndex + numberOfCharacters;
1400   }
1401
1402   // Will update the cursor hook position.
1403   mEventData->mUpdateCursorHookPosition = true;
1404
1405   return cursorIndex;
1406 }
1407
1408 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1409 {
1410   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1411   if(nullptr == mEventData)
1412   {
1413     // Nothing to do if there is no text input.
1414     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1415     return;
1416   }
1417
1418   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1419
1420   mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1421
1422   // Sets the cursor position.
1423   mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1424                                       cursorPosition.x,
1425                                       cursorPosition.y,
1426                                       cursorInfo.primaryCursorHeight,
1427                                       cursorInfo.lineHeight);
1428   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1429
1430   if(mEventData->mUpdateGrabHandlePosition)
1431   {
1432     // Sets the grab handle position.
1433     mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1434                                         cursorPosition.x,
1435                                         cursorInfo.lineOffset + mModel->mScrollPosition.y,
1436                                         cursorInfo.lineHeight);
1437   }
1438
1439   if(cursorInfo.isSecondaryCursor)
1440   {
1441     mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1442                                         cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1443                                         cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1444                                         cursorInfo.secondaryCursorHeight,
1445                                         cursorInfo.lineHeight);
1446     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1447   }
1448
1449   // Set which cursors are active according the state.
1450   if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1451   {
1452     if(cursorInfo.isSecondaryCursor)
1453     {
1454       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1455     }
1456     else
1457     {
1458       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1459     }
1460   }
1461   else
1462   {
1463     mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1464   }
1465
1466   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1467 }
1468
1469 void Controller::Impl::UpdateSelectionHandle(HandleType        handleType,
1470                                              const CursorInfo& cursorInfo)
1471 {
1472   SelectionHandleController::Update(*this, handleType, cursorInfo);
1473 }
1474
1475 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1476 {
1477   // Clamp between -space & -alignment offset.
1478
1479   if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1480   {
1481     const float space         = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1482     mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1483     mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1484
1485     mEventData->mDecoratorUpdated = true;
1486   }
1487   else
1488   {
1489     mModel->mScrollPosition.x = 0.f;
1490   }
1491 }
1492
1493 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1494 {
1495   if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1496   {
1497     // Nothing to do if the text is single line.
1498     return;
1499   }
1500
1501   // Clamp between -space & 0.
1502   if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1503   {
1504     const float space         = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1505     mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1506     mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1507
1508     mEventData->mDecoratorUpdated = true;
1509   }
1510   else
1511   {
1512     mModel->mScrollPosition.y = 0.f;
1513   }
1514 }
1515
1516 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1517 {
1518   const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1519
1520   // position is in actor's coords.
1521   const float positionEndX = position.x + cursorWidth;
1522   const float positionEndY = position.y + lineHeight;
1523
1524   // Transform the position to decorator coords.
1525   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1526   const float decoratorPositionEndX   = positionEndX + mModel->mScrollPosition.x;
1527
1528   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1529   const float decoratorPositionEndY   = positionEndY + mModel->mScrollPosition.y;
1530
1531   if(decoratorPositionBeginX < 0.f)
1532   {
1533     mModel->mScrollPosition.x = -position.x;
1534   }
1535   else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1536   {
1537     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1538   }
1539
1540   if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1541   {
1542     if(decoratorPositionBeginY < 0.f)
1543     {
1544       mModel->mScrollPosition.y = -position.y;
1545     }
1546     else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1547     {
1548       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1549     }
1550     else if(mModel->mLogicalModel->mText.Count() == 0u)
1551     {
1552       Relayouter::CalculateVerticalOffset(*this, mModel->mVisualModel->mControlSize);
1553     }
1554   }
1555 }
1556
1557 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1558 {
1559   // Get the current cursor position in decorator coords.
1560   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1561
1562   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1563
1564   // Calculate the offset to match the cursor position before the character was deleted.
1565   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1566
1567   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1568   if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1569   {
1570     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1571     mModel->mScrollPosition.y            = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1572   }
1573
1574   ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1575   ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1576
1577   // Makes the new cursor position visible if needed.
1578   ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1579 }
1580
1581 void Controller::Impl::ScrollTextToMatchCursor()
1582 {
1583   CursorInfo cursorInfo;
1584   GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1585   ScrollTextToMatchCursor(cursorInfo);
1586 }
1587
1588 void Controller::Impl::RequestRelayout()
1589 {
1590   if(nullptr != mControlInterface)
1591   {
1592     mControlInterface->RequestTextRelayout();
1593   }
1594 }
1595
1596 void Controller::Impl::RelayoutAllCharacters()
1597 {
1598   // relayout all characters
1599   mTextUpdateInfo.mCharacterIndex             = 0;
1600   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1601   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1602   mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
1603
1604   mTextUpdateInfo.mFullRelayoutNeeded = true;
1605
1606   // Need to recalculate natural size
1607   mRecalculateNaturalSize = true;
1608
1609   //remove selection
1610   if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
1611   {
1612     ChangeState(EventData::EDITING);
1613   }
1614
1615   RequestRelayout();
1616 }
1617
1618 bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
1619 {
1620   return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
1621 }
1622
1623 void Controller::Impl::ProcessInputStyleChangedSignals()
1624 {
1625   if(mEventData)
1626   {
1627     if(mEditableControlInterface)
1628     {
1629       // Emit the input style changed signal for each mask
1630       std::for_each(mEventData->mInputStyleChangedQueue.begin(),
1631                     mEventData->mInputStyleChangedQueue.end(),
1632                     [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
1633     }
1634
1635     mEventData->mInputStyleChangedQueue.Clear();
1636   }
1637 }
1638
1639 void Controller::Impl::ScrollBy(Vector2 scroll)
1640 {
1641   if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
1642   {
1643     const Vector2& layoutSize    = mModel->mVisualModel->GetLayoutSize();
1644     const Vector2  currentScroll = mModel->mScrollPosition;
1645
1646     scroll.x = -scroll.x;
1647     scroll.y = -scroll.y;
1648
1649     if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
1650     {
1651       mModel->mScrollPosition.x += scroll.x;
1652       ClampHorizontalScroll(layoutSize);
1653     }
1654
1655     if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
1656     {
1657       mModel->mScrollPosition.y += scroll.y;
1658       ClampVerticalScroll(layoutSize);
1659     }
1660
1661     if(mModel->mScrollPosition != currentScroll)
1662     {
1663       mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
1664       RequestRelayout();
1665     }
1666   }
1667 }
1668
1669 bool Controller::Impl::IsScrollable(const Vector2& displacement)
1670 {
1671   bool isScrollable = false;
1672   if(mEventData)
1673   {
1674     const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1675     const bool isVerticalScrollEnabled   = mEventData->mDecorator->IsVerticalScrollEnabled();
1676     if(isHorizontalScrollEnabled || isVerticalScrollEnabled)
1677     {
1678       const Vector2& targetSize     = mModel->mVisualModel->mControlSize;
1679       const Vector2& layoutSize     = mModel->mVisualModel->GetLayoutSize();
1680       const Vector2& scrollPosition = mModel->mScrollPosition;
1681
1682       if(isHorizontalScrollEnabled)
1683       {
1684         const float displacementX = displacement.x;
1685         const float positionX     = scrollPosition.x + displacementX;
1686         if(layoutSize.width > targetSize.width && -positionX > 0.f && -positionX < layoutSize.width - targetSize.width)
1687         {
1688           isScrollable = true;
1689         }
1690       }
1691
1692       if(isVerticalScrollEnabled)
1693       {
1694         const float displacementY = displacement.y;
1695         const float positionY     = scrollPosition.y + displacementY;
1696         if(layoutSize.height > targetSize.height && -positionY > 0 && -positionY < layoutSize.height - targetSize.height)
1697         {
1698           isScrollable = true;
1699         }
1700       }
1701     }
1702   }
1703   return isScrollable;
1704 }
1705
1706 float Controller::Impl::GetHorizontalScrollPosition()
1707 {
1708   // Scroll values are negative internally so we convert them to positive numbers
1709   return mEventData ? -mModel->mScrollPosition.x : 0.0f;
1710 }
1711
1712 float Controller::Impl::GetVerticalScrollPosition()
1713 {
1714   // Scroll values are negative internally so we convert them to positive numbers
1715   return mEventData ? -mModel->mScrollPosition.y : 0.0f;
1716 }
1717
1718 Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
1719 {
1720   //TODO
1721   return Vector3(10.f, 10.f, 10.f);
1722 }
1723
1724 Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
1725 {
1726   //TODO
1727   return Vector2(10.f, 10.f);
1728 }
1729
1730 Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
1731 {
1732   auto actor = Toolkit::TextAnchor::New();
1733   actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1734   actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1735   const Vector3 anchorPosition = GetAnchorPosition(anchor);
1736   actor.SetProperty(Actor::Property::POSITION, anchorPosition);
1737   const Vector2 anchorSize = GetAnchorSize(anchor);
1738   actor.SetProperty(Actor::Property::SIZE, anchorSize);
1739   std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
1740   actor.SetProperty(Actor::Property::NAME, anchorText);
1741   actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
1742   actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
1743   actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
1744   return actor;
1745 }
1746
1747 void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
1748 {
1749   /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
1750            we need to create and destroy potentially many actors. Some optimization can be considered here.
1751            Maybe a "dirty" flag in mLogicalModel? */
1752   anchorActors.clear();
1753   for(auto& anchor : mModel->mLogicalModel->mAnchors)
1754   {
1755     auto actor = CreateAnchorActor(anchor);
1756     anchorActors.push_back(actor);
1757   }
1758 }
1759
1760 int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
1761 {
1762   Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
1763
1764   while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
1765   {
1766     it++;
1767   }
1768
1769   return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
1770 }
1771
1772 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1773 {
1774   //Underlined character runs for markup-processor
1775   const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1776   const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
1777   const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
1778
1779   if(shouldClearPreUnderlineRuns)
1780   {
1781     mModel->mVisualModel->mUnderlineRuns.Clear();
1782   }
1783
1784   for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1785   {
1786     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1787     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1788
1789     if(numberOfCharacters == 0)
1790     {
1791       continue;
1792     }
1793
1794     // Create one run for all glyphs of all run's characters that has same properties
1795     // This enhance performance and reduce the needed memory to store glyphs-runs
1796     UnderlinedGlyphRun underlineGlyphRun;
1797     underlineGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1798     underlineGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1799     //Copy properties (attributes)
1800     underlineGlyphRun.properties = it->properties;
1801
1802     for(Length index = 1u; index < numberOfCharacters; index++)
1803     {
1804       underlineGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1805     }
1806
1807     mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1808   }
1809
1810   // Reset flag. The updates have been applied from logical to visual.
1811   mModel->mLogicalModel->mUnderlineRunsUpdated = false;
1812 }
1813
1814 void Controller::Impl::CopyStrikethroughFromLogicalToVisualModels()
1815 {
1816   //Strikethrough character runs from markup-processor
1817   const Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns = mModel->mLogicalModel->mStrikethroughCharacterRuns;
1818   const Vector<GlyphIndex>&                charactersToGlyph          = mModel->mVisualModel->mCharactersToGlyph;
1819   const Vector<Length>&                    glyphsPerCharacter         = mModel->mVisualModel->mGlyphsPerCharacter;
1820
1821   mModel->mVisualModel->mStrikethroughRuns.Clear();
1822
1823   for(Vector<StrikethroughCharacterRun>::ConstIterator it = strikethroughCharacterRuns.Begin(), endIt = strikethroughCharacterRuns.End(); it != endIt; ++it)
1824   {
1825     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1826     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1827
1828     if(numberOfCharacters == 0)
1829     {
1830       continue;
1831     }
1832
1833     StrikethroughGlyphRun strikethroughGlyphRun;
1834     strikethroughGlyphRun.properties              = it->properties;
1835     strikethroughGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1836     strikethroughGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1837
1838     for(Length index = 1u; index < numberOfCharacters; index++)
1839     {
1840       strikethroughGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1841     }
1842
1843     mModel->mVisualModel->mStrikethroughRuns.PushBack(strikethroughGlyphRun);
1844   }
1845
1846   // Reset flag. The updates have been applied from logical to visual.
1847   mModel->mLogicalModel->mStrikethroughRunsUpdated = false;
1848 }
1849
1850 void Controller::Impl::CopyCharacterSpacingFromLogicalToVisualModels()
1851 {
1852   //CharacterSpacing character runs from markup-processor
1853   const Vector<CharacterSpacingCharacterRun>& characterSpacingCharacterRuns = mModel->mLogicalModel->mCharacterSpacingCharacterRuns;
1854   const Vector<GlyphIndex>&                   charactersToGlyph             = mModel->mVisualModel->mCharactersToGlyph;
1855   const Vector<Length>&                       glyphsPerCharacter            = mModel->mVisualModel->mGlyphsPerCharacter;
1856
1857   mModel->mVisualModel->mCharacterSpacingRuns.Clear();
1858
1859   for(Vector<CharacterSpacingCharacterRun>::ConstIterator it = characterSpacingCharacterRuns.Begin(), endIt = characterSpacingCharacterRuns.End(); it != endIt; ++it)
1860   {
1861     const CharacterIndex& characterIndex     = it->characterRun.characterIndex;
1862     const Length&         numberOfCharacters = it->characterRun.numberOfCharacters;
1863
1864     if(numberOfCharacters == 0)
1865     {
1866       continue;
1867     }
1868
1869     CharacterSpacingGlyphRun characterSpacingGlyphRun;
1870     characterSpacingGlyphRun.value                   = it->value;
1871     characterSpacingGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1872     characterSpacingGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1873
1874     for(Length index = 1u; index < numberOfCharacters; index++)
1875     {
1876       characterSpacingGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1877     }
1878
1879     mModel->mVisualModel->mCharacterSpacingRuns.PushBack(characterSpacingGlyphRun);
1880   }
1881   mModel->mLogicalModel->mCharacterSpacingRunsUpdated = false;
1882 }
1883
1884 void Controller::Impl::SetAutoScrollEnabled(bool enable)
1885 {
1886   if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
1887   {
1888     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1889                                                      LAYOUT |
1890                                                      ALIGN |
1891                                                      UPDATE_LAYOUT_SIZE |
1892                                                      REORDER);
1893
1894     if(enable)
1895     {
1896       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
1897       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
1898     }
1899     else
1900     {
1901       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
1902     }
1903
1904     mIsAutoScrollEnabled = enable;
1905     RequestRelayout();
1906   }
1907   else
1908   {
1909     DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
1910     mIsAutoScrollEnabled = false;
1911   }
1912 }
1913
1914 void Controller::Impl::SetEnableCursorBlink(bool enable)
1915 {
1916   DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
1917
1918   if(mEventData)
1919   {
1920     mEventData->mCursorBlinkEnabled = enable;
1921
1922     if(!enable && mEventData->mDecorator)
1923     {
1924       mEventData->mDecorator->StopCursorBlink();
1925     }
1926   }
1927 }
1928
1929 void Controller::Impl::SetMultiLineEnabled(bool enable)
1930 {
1931   const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
1932
1933   if(layout != mLayoutEngine.GetLayout())
1934   {
1935     // Set the layout type.
1936     mLayoutEngine.SetLayout(layout);
1937
1938     // Set the flags to redo the layout operations
1939     const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
1940                                                                         UPDATE_LAYOUT_SIZE |
1941                                                                         ALIGN |
1942                                                                         REORDER);
1943
1944     mTextUpdateInfo.mFullRelayoutNeeded = true;
1945     mOperationsPending                  = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
1946
1947     // Need to recalculate natural size
1948     mRecalculateNaturalSize = true;
1949
1950     RequestRelayout();
1951   }
1952 }
1953
1954 void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
1955 {
1956   if(alignment != mModel->mHorizontalAlignment)
1957   {
1958     // Set the alignment.
1959     mModel->mHorizontalAlignment = alignment;
1960     UpdateCursorPositionForAlignment(*this, true);
1961     RequestRelayout();
1962   }
1963 }
1964
1965 void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
1966 {
1967   if(alignment != mModel->mVerticalAlignment)
1968   {
1969     // Set the alignment.
1970     mModel->mVerticalAlignment = alignment;
1971     UpdateCursorPositionForAlignment(*this, false);
1972     RequestRelayout();
1973   }
1974 }
1975
1976 void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
1977 {
1978   if(lineWrapMode != mModel->mLineWrapMode)
1979   {
1980     // Update Text layout for applying wrap mode
1981     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1982                                                      ALIGN |
1983                                                      LAYOUT |
1984                                                      UPDATE_LAYOUT_SIZE |
1985                                                      REORDER);
1986
1987     if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
1988        (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
1989     {
1990       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
1991     }
1992
1993     // Set the text wrap mode.
1994     mModel->mLineWrapMode = lineWrapMode;
1995
1996     mTextUpdateInfo.mCharacterIndex             = 0u;
1997     mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1998     mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1999
2000     // Request relayout
2001     RequestRelayout();
2002   }
2003 }
2004
2005 void Controller::Impl::SetDefaultColor(const Vector4& color)
2006 {
2007   mTextColor = color;
2008
2009   if(!IsShowingPlaceholderText())
2010   {
2011     mModel->mVisualModel->SetTextColor(color);
2012     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
2013     RequestRelayout();
2014   }
2015 }
2016
2017 void Controller::Impl::SetUserInteractionEnabled(bool enabled)
2018 {
2019   mIsUserInteractionEnabled = enabled;
2020
2021   if(mEventData && mEventData->mDecorator)
2022   {
2023     bool editable = mEventData->mEditingEnabled && enabled;
2024     mEventData->mDecorator->SetEditable(editable);
2025     mEventData->mDecoratorUpdated = true;
2026     RequestRelayout();
2027   }
2028 }
2029
2030 void Controller::Impl::ClearFontData()
2031 {
2032   if(mFontDefaults)
2033   {
2034     mFontDefaults->mFontId = 0u; // Remove old font ID
2035   }
2036
2037   // Set flags to update the model.
2038   mTextUpdateInfo.mCharacterIndex             = 0u;
2039   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2040   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
2041
2042   mTextUpdateInfo.mClearAll           = true;
2043   mTextUpdateInfo.mFullRelayoutNeeded = true;
2044   mRecalculateNaturalSize             = true;
2045
2046   mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
2047                                                    VALIDATE_FONTS |
2048                                                    SHAPE_TEXT |
2049                                                    BIDI_INFO |
2050                                                    GET_GLYPH_METRICS |
2051                                                    LAYOUT |
2052                                                    UPDATE_LAYOUT_SIZE |
2053                                                    REORDER |
2054                                                    ALIGN);
2055 }
2056
2057 void Controller::Impl::ClearStyleData()
2058 {
2059   mModel->mLogicalModel->mColorRuns.Clear();
2060   mModel->mLogicalModel->ClearFontDescriptionRuns();
2061   mModel->mLogicalModel->ClearStrikethroughRuns();
2062   mModel->mLogicalModel->ClearUnderlineRuns();
2063 }
2064
2065 void Controller::Impl::ResetScrollPosition()
2066 {
2067   if(mEventData)
2068   {
2069     // Reset the scroll position.
2070     mModel->mScrollPosition                = Vector2::ZERO;
2071     mEventData->mScrollAfterUpdatePosition = true;
2072   }
2073 }
2074
2075 } // namespace Dali::Toolkit::Text