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