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