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