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