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