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