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