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