Fixing issue: On Text, reducing font-point-size when the Glyph-block-size be larger...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2021 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/integration-api/debug.h>
23 #include <dali/public-api/rendering/renderer.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
27 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/color-segmentation.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/multi-language-support.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-control-interface.h>
36 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
38 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
39
40 using namespace Dali;
41
42 namespace
43 {
44 #if defined(DEBUG_ENABLED)
45 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
46 #endif
47
48 struct BackgroundVertex
49 {
50   Vector2 mPosition; ///< Vertex posiiton
51   Vector4 mColor;    ///< Vertex color
52 };
53
54 struct BackgroundMesh
55 {
56   Vector<BackgroundVertex> mVertices; ///< container of vertices
57   Vector<unsigned short>   mIndices;  ///< container of indices
58 };
59
60 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
61 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
62 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
63 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
64 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
65
66 } // namespace
67
68 namespace Dali
69 {
70 namespace Toolkit
71 {
72 namespace Text
73 {
74 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
75 : mDecorator(decorator),
76   mInputMethodContext(inputMethodContext),
77   mPlaceholderFont(NULL),
78   mPlaceholderTextActive(),
79   mPlaceholderTextInactive(),
80   mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
81   mEventQueue(),
82   mInputStyleChangedQueue(),
83   mPreviousState(INACTIVE),
84   mState(INACTIVE),
85   mPrimaryCursorPosition(0u),
86   mLeftSelectionPosition(0u),
87   mRightSelectionPosition(0u),
88   mPreEditStartPosition(0u),
89   mPreEditLength(0u),
90   mCursorHookPositionX(0.f),
91   mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
92   mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
93   mIsShowingPlaceholderText(false),
94   mPreEditFlag(false),
95   mDecoratorUpdated(false),
96   mCursorBlinkEnabled(true),
97   mGrabHandleEnabled(true),
98   mGrabHandlePopupEnabled(true),
99   mSelectionEnabled(true),
100   mUpdateCursorHookPosition(false),
101   mUpdateCursorPosition(false),
102   mUpdateGrabHandlePosition(false),
103   mUpdateLeftSelectionPosition(false),
104   mUpdateRightSelectionPosition(false),
105   mIsLeftHandleSelected(false),
106   mIsRightHandleSelected(false),
107   mUpdateHighlightBox(false),
108   mScrollAfterUpdatePosition(false),
109   mScrollAfterDelete(false),
110   mAllTextSelected(false),
111   mUpdateInputStyle(false),
112   mPasswordInput(false),
113   mCheckScrollAmount(false),
114   mIsPlaceholderPixelSize(false),
115   mIsPlaceholderElideEnabled(false),
116   mPlaceholderEllipsisFlag(false),
117   mShiftSelectionFlag(true),
118   mUpdateAlignment(false),
119   mEditingEnabled(true)
120 {
121 }
122
123 bool Controller::Impl::ProcessInputEvents()
124 {
125   return ControllerImplEventHandler::ProcessInputEvents(*this);
126 }
127
128 void Controller::Impl::NotifyInputMethodContext()
129 {
130   if(mEventData && mEventData->mInputMethodContext)
131   {
132     CharacterIndex cursorPosition = GetLogicalCursorPosition();
133
134     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
135
136     // Update the cursor position by removing the initial white spaces.
137     if(cursorPosition < numberOfWhiteSpaces)
138     {
139       cursorPosition = 0u;
140     }
141     else
142     {
143       cursorPosition -= numberOfWhiteSpaces;
144     }
145
146     mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
147     mEventData->mInputMethodContext.NotifyCursorPosition();
148   }
149 }
150
151 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
152 {
153   if(mEventData && mEventData->mInputMethodContext)
154   {
155     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
156     mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
157   }
158 }
159
160 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
161 {
162   CharacterIndex cursorPosition = 0u;
163
164   if(mEventData)
165   {
166     if((EventData::SELECTING == mEventData->mState) ||
167        (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
168     {
169       cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
170     }
171     else
172     {
173       cursorPosition = mEventData->mPrimaryCursorPosition;
174     }
175   }
176
177   return cursorPosition;
178 }
179
180 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
181 {
182   Length numberOfWhiteSpaces = 0u;
183
184   // Get the buffer to the text.
185   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
186
187   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
188   for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
189   {
190     if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
191     {
192       break;
193     }
194   }
195
196   return numberOfWhiteSpaces;
197 }
198
199 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
200 {
201   // Get the total number of characters.
202   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
203
204   // Retrieve the text.
205   if(0u != numberOfCharacters)
206   {
207     Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
208   }
209 }
210
211 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
212 {
213   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
214   mTextUpdateInfo.mStartGlyphIndex         = 0u;
215   mTextUpdateInfo.mStartLineIndex          = 0u;
216   numberOfCharacters                       = 0u;
217
218   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
219   if(0u == numberOfParagraphs)
220   {
221     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
222     numberOfCharacters                       = 0u;
223
224     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
225
226     // Nothing else to do if there are no paragraphs.
227     return;
228   }
229
230   // Find the paragraphs to be updated.
231   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
232   if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
233   {
234     // Text is being added at the end of the current text.
235     if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
236     {
237       // Text is being added in a new paragraph after the last character of the text.
238       mTextUpdateInfo.mParagraphCharacterIndex     = mTextUpdateInfo.mPreviousNumberOfCharacters;
239       numberOfCharacters                           = 0u;
240       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
241
242       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
243       mTextUpdateInfo.mStartLineIndex  = mModel->mVisualModel->mLines.Count() - 1u;
244
245       // Nothing else to do;
246       return;
247     }
248
249     paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
250   }
251   else
252   {
253     Length numberOfCharactersToUpdate = 0u;
254     if(mTextUpdateInfo.mFullRelayoutNeeded)
255     {
256       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
257     }
258     else
259     {
260       numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
261     }
262     mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
263                                           numberOfCharactersToUpdate,
264                                           paragraphsToBeUpdated);
265   }
266
267   if(0u != paragraphsToBeUpdated.Count())
268   {
269     const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
270     const ParagraphRun&     firstParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
271     mTextUpdateInfo.mParagraphCharacterIndex    = firstParagraph.characterRun.characterIndex;
272
273     ParagraphRunIndex   lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
274     const ParagraphRun& lastParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
275
276     if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) &&                                           // Some character are removed.
277        (lastParagraphIndex < numberOfParagraphs - 1u) &&                                               // There is a next paragraph.
278        ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
279         (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
280     {
281       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
282       const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
283
284       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
285     }
286     else
287     {
288       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
289     }
290   }
291
292   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
293   mTextUpdateInfo.mStartGlyphIndex             = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
294 }
295
296 void Controller::Impl::ClearFullModelData(OperationsMask operations)
297 {
298   if(NO_OPERATION != (GET_LINE_BREAKS & operations))
299   {
300     mModel->mLogicalModel->mLineBreakInfo.Clear();
301     mModel->mLogicalModel->mParagraphInfo.Clear();
302   }
303
304   if(NO_OPERATION != (GET_SCRIPTS & operations))
305   {
306     mModel->mLogicalModel->mScriptRuns.Clear();
307   }
308
309   if(NO_OPERATION != (VALIDATE_FONTS & operations))
310   {
311     mModel->mLogicalModel->mFontRuns.Clear();
312   }
313
314   if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
315   {
316     if(NO_OPERATION != (BIDI_INFO & operations))
317     {
318       mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
319       mModel->mLogicalModel->mCharacterDirections.Clear();
320     }
321
322     if(NO_OPERATION != (REORDER & operations))
323     {
324       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
325       for(Vector<BidirectionalLineInfoRun>::Iterator it    = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
326                                                      endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
327           it != endIt;
328           ++it)
329       {
330         BidirectionalLineInfoRun& bidiLineInfo = *it;
331
332         free(bidiLineInfo.visualToLogicalMap);
333         bidiLineInfo.visualToLogicalMap = NULL;
334       }
335       mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
336     }
337   }
338
339   if(NO_OPERATION != (SHAPE_TEXT & operations))
340   {
341     mModel->mVisualModel->mGlyphs.Clear();
342     mModel->mVisualModel->mGlyphsToCharacters.Clear();
343     mModel->mVisualModel->mCharactersToGlyph.Clear();
344     mModel->mVisualModel->mCharactersPerGlyph.Clear();
345     mModel->mVisualModel->mGlyphsPerCharacter.Clear();
346     mModel->mVisualModel->mGlyphPositions.Clear();
347   }
348
349   if(NO_OPERATION != (LAYOUT & operations))
350   {
351     mModel->mVisualModel->mLines.Clear();
352   }
353
354   if(NO_OPERATION != (COLOR & operations))
355   {
356     mModel->mVisualModel->mColorIndices.Clear();
357     mModel->mVisualModel->mBackgroundColorIndices.Clear();
358   }
359 }
360
361 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
362 {
363   const CharacterIndex endIndexPlusOne = endIndex + 1u;
364
365   if(NO_OPERATION != (GET_LINE_BREAKS & operations))
366   {
367     // Clear the line break info.
368     LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
369
370     mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
371                                                 lineBreakInfoBuffer + endIndexPlusOne);
372
373     // Clear the paragraphs.
374     ClearCharacterRuns(startIndex,
375                        endIndex,
376                        mModel->mLogicalModel->mParagraphInfo);
377   }
378
379   if(NO_OPERATION != (GET_SCRIPTS & operations))
380   {
381     // Clear the scripts.
382     ClearCharacterRuns(startIndex,
383                        endIndex,
384                        mModel->mLogicalModel->mScriptRuns);
385   }
386
387   if(NO_OPERATION != (VALIDATE_FONTS & operations))
388   {
389     // Clear the fonts.
390     ClearCharacterRuns(startIndex,
391                        endIndex,
392                        mModel->mLogicalModel->mFontRuns);
393   }
394
395   if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
396   {
397     if(NO_OPERATION != (BIDI_INFO & operations))
398     {
399       // Clear the bidirectional paragraph info.
400       ClearCharacterRuns(startIndex,
401                          endIndex,
402                          mModel->mLogicalModel->mBidirectionalParagraphInfo);
403
404       // Clear the character's directions.
405       CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
406
407       mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
408                                                         characterDirectionsBuffer + endIndexPlusOne);
409     }
410
411     if(NO_OPERATION != (REORDER & operations))
412     {
413       uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
414       uint32_t endRemoveIndex   = startRemoveIndex;
415       ClearCharacterRuns(startIndex,
416                          endIndex,
417                          mModel->mLogicalModel->mBidirectionalLineInfo,
418                          startRemoveIndex,
419                          endRemoveIndex);
420
421       BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
422
423       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
424       for(Vector<BidirectionalLineInfoRun>::Iterator it    = bidirectionalLineInfoBuffer + startRemoveIndex,
425                                                      endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
426           it != endIt;
427           ++it)
428       {
429         BidirectionalLineInfoRun& bidiLineInfo = *it;
430
431         free(bidiLineInfo.visualToLogicalMap);
432         bidiLineInfo.visualToLogicalMap = NULL;
433       }
434
435       mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
436                                                           bidirectionalLineInfoBuffer + endRemoveIndex);
437     }
438   }
439 }
440
441 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
442 {
443   const CharacterIndex endIndexPlusOne           = endIndex + 1u;
444   const Length         numberOfCharactersRemoved = endIndexPlusOne - startIndex;
445
446   // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
447   GlyphIndex* charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
448   Length*     glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
449
450   const GlyphIndex endGlyphIndexPlusOne  = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
451   const Length     numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
452
453   if(NO_OPERATION != (SHAPE_TEXT & operations))
454   {
455     // Update the character to glyph indices.
456     for(Vector<GlyphIndex>::Iterator it    = charactersToGlyphBuffer + endIndexPlusOne,
457                                      endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
458         it != endIt;
459         ++it)
460     {
461       CharacterIndex& index = *it;
462       index -= numberOfGlyphsRemoved;
463     }
464
465     // Clear the character to glyph conversion table.
466     mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
467                                                    charactersToGlyphBuffer + endIndexPlusOne);
468
469     // Clear the glyphs per character table.
470     mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
471                                                     glyphsPerCharacterBuffer + endIndexPlusOne);
472
473     // Clear the glyphs buffer.
474     GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
475     mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
476                                         glyphsBuffer + endGlyphIndexPlusOne);
477
478     CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
479
480     // Update the glyph to character indices.
481     for(Vector<CharacterIndex>::Iterator it    = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
482                                          endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
483         it != endIt;
484         ++it)
485     {
486       CharacterIndex& index = *it;
487       index -= numberOfCharactersRemoved;
488     }
489
490     // Clear the glyphs to characters buffer.
491     mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
492                                                     glyphsToCharactersBuffer + endGlyphIndexPlusOne);
493
494     // Clear the characters per glyph buffer.
495     Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
496     mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
497                                                     charactersPerGlyphBuffer + endGlyphIndexPlusOne);
498
499     // Clear the positions buffer.
500     Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
501     mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
502                                                 positionsBuffer + endGlyphIndexPlusOne);
503   }
504
505   if(NO_OPERATION != (LAYOUT & operations))
506   {
507     // Clear the lines.
508     uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
509     uint32_t endRemoveIndex   = startRemoveIndex;
510     ClearCharacterRuns(startIndex,
511                        endIndex,
512                        mModel->mVisualModel->mLines,
513                        startRemoveIndex,
514                        endRemoveIndex);
515
516     // Will update the glyph runs.
517     startRemoveIndex = mModel->mVisualModel->mLines.Count();
518     endRemoveIndex   = startRemoveIndex;
519     ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
520                    endGlyphIndexPlusOne - 1u,
521                    mModel->mVisualModel->mLines,
522                    startRemoveIndex,
523                    endRemoveIndex);
524
525     // Set the line index from where to insert the new laid-out lines.
526     mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
527
528     LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
529     mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
530                                        linesBuffer + endRemoveIndex);
531   }
532
533   if(NO_OPERATION != (COLOR & operations))
534   {
535     if(0u != mModel->mVisualModel->mColorIndices.Count())
536     {
537       ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
538       mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
539                                                 colorIndexBuffer + endGlyphIndexPlusOne);
540     }
541
542     if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
543     {
544       ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
545       mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
546                                                           backgroundColorIndexBuffer + endGlyphIndexPlusOne);
547     }
548   }
549 }
550
551 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
552 {
553   if(mTextUpdateInfo.mClearAll ||
554      ((0u == startIndex) &&
555       (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
556   {
557     ClearFullModelData(operations);
558   }
559   else
560   {
561     // Clear the model data related with characters.
562     ClearCharacterModelData(startIndex, endIndex, operations);
563
564     // Clear the model data related with glyphs.
565     ClearGlyphModelData(startIndex, endIndex, operations);
566   }
567
568   // The estimated number of lines. Used to avoid reallocations when layouting.
569   mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
570
571   mModel->mVisualModel->ClearCaches();
572 }
573
574 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
575 {
576   DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
577
578   // Calculate the operations to be done.
579   const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
580
581   if(NO_OPERATION == operations)
582   {
583     // Nothing to do if no operations are pending and required.
584     return false;
585   }
586
587   Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
588   Vector<Character>  displayCharacters;
589   bool               useHiddenText = false;
590   if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
591   {
592     mHiddenInput->Substitute(srcCharacters, displayCharacters);
593     useHiddenText = true;
594   }
595
596   Vector<Character>& utf32Characters    = useHiddenText ? displayCharacters : srcCharacters;
597   const Length       numberOfCharacters = utf32Characters.Count();
598
599   // Index to the first character of the first paragraph to be updated.
600   CharacterIndex startIndex = 0u;
601   // Number of characters of the paragraphs to be removed.
602   Length paragraphCharacters = 0u;
603
604   CalculateTextUpdateIndices(paragraphCharacters);
605
606   // Check whether the indices for updating the text is valid
607   if(numberOfCharacters > 0u &&
608      (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
609       mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
610   {
611     std::string currentText;
612     Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
613
614     DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
615     DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
616
617     // Dump mTextUpdateInfo
618     DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
619     DALI_LOG_ERROR("     mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
620     DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
621     DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
622     DALI_LOG_ERROR("     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
623     DALI_LOG_ERROR("     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
624     DALI_LOG_ERROR("     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
625     DALI_LOG_ERROR("     mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
626     DALI_LOG_ERROR("     mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
627     DALI_LOG_ERROR("     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
628     DALI_LOG_ERROR("     mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
629     DALI_LOG_ERROR("     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
630     DALI_LOG_ERROR("     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
631
632     return false;
633   }
634
635   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
636
637   if(mTextUpdateInfo.mClearAll ||
638      (0u != paragraphCharacters))
639   {
640     ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
641   }
642
643   mTextUpdateInfo.mClearAll = false;
644
645   // Whether the model is updated.
646   bool updated = false;
647
648   Vector<LineBreakInfo>& lineBreakInfo               = mModel->mLogicalModel->mLineBreakInfo;
649   const Length           requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
650
651   if(NO_OPERATION != (GET_LINE_BREAKS & operations))
652   {
653     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
654     // calculate the bidirectional info for each 'paragraph'.
655     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
656     // is not shaped together).
657     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
658
659     SetLineBreakInfo(utf32Characters,
660                      startIndex,
661                      requestedNumberOfCharacters,
662                      lineBreakInfo);
663
664     // Create the paragraph info.
665     mModel->mLogicalModel->CreateParagraphInfo(startIndex,
666                                                requestedNumberOfCharacters);
667     updated = true;
668   }
669
670   const bool getScripts    = NO_OPERATION != (GET_SCRIPTS & operations);
671   const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
672
673   Vector<ScriptRun>& scripts    = mModel->mLogicalModel->mScriptRuns;
674   Vector<FontRun>&   validFonts = mModel->mLogicalModel->mFontRuns;
675
676   if(getScripts || validateFonts)
677   {
678     // Validates the fonts assigned by the application or assigns default ones.
679     // It makes sure all the characters are going to be rendered by the correct font.
680     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
681
682     if(getScripts)
683     {
684       // Retrieves the scripts used in the text.
685       multilanguageSupport.SetScripts(utf32Characters,
686                                       startIndex,
687                                       requestedNumberOfCharacters,
688                                       scripts);
689     }
690
691     if(validateFonts)
692     {
693       // Validate the fonts set through the mark-up string.
694       Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
695
696       // Get the default font's description.
697       TextAbstraction::FontDescription defaultFontDescription;
698       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
699
700       //Get the number of points per one unit of point-size
701       uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
702
703       if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
704       {
705         // If the placeholder font is set specifically, only placeholder font is changed.
706         defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
707         if(mEventData->mPlaceholderFont->sizeDefined)
708         {
709           defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
710         }
711       }
712       else if(nullptr != mFontDefaults)
713       {
714         // Set the normal font and the placeholder font.
715         defaultFontDescription = mFontDefaults->mFontDescription;
716
717         if(mTextFitEnabled)
718         {
719           defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
720         }
721         else
722         {
723           defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
724         }
725       }
726
727       // Validates the fonts. If there is a character with no assigned font it sets a default one.
728       // After this call, fonts are validated.
729       multilanguageSupport.ValidateFonts(utf32Characters,
730                                          scripts,
731                                          fontDescriptionRuns,
732                                          defaultFontDescription,
733                                          defaultPointSize,
734                                          startIndex,
735                                          requestedNumberOfCharacters,
736                                          validFonts);
737     }
738     updated = true;
739   }
740
741   Vector<Character> mirroredUtf32Characters;
742   bool              textMirrored       = false;
743   const Length      numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
744   if(NO_OPERATION != (BIDI_INFO & operations))
745   {
746     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
747     bidirectionalInfo.Reserve(numberOfParagraphs);
748
749     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
750     SetBidirectionalInfo(utf32Characters,
751                          scripts,
752                          lineBreakInfo,
753                          startIndex,
754                          requestedNumberOfCharacters,
755                          bidirectionalInfo,
756                          mModel->mMatchSystemLanguageDirection,
757                          mLayoutDirection);
758
759     if(0u != bidirectionalInfo.Count())
760     {
761       // Only set the character directions if there is right to left characters.
762       Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
763       GetCharactersDirection(bidirectionalInfo,
764                              numberOfCharacters,
765                              startIndex,
766                              requestedNumberOfCharacters,
767                              directions);
768
769       // This paragraph has right to left text. Some characters may need to be mirrored.
770       // TODO: consider if the mirrored string can be stored as well.
771
772       textMirrored = GetMirroredText(utf32Characters,
773                                      directions,
774                                      bidirectionalInfo,
775                                      startIndex,
776                                      requestedNumberOfCharacters,
777                                      mirroredUtf32Characters);
778     }
779     else
780     {
781       // There is no right to left characters. Clear the directions vector.
782       mModel->mLogicalModel->mCharacterDirections.Clear();
783     }
784     updated = true;
785   }
786
787   Vector<GlyphInfo>&      glyphs                = mModel->mVisualModel->mGlyphs;
788   Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
789   Vector<Length>&         charactersPerGlyph    = mModel->mVisualModel->mCharactersPerGlyph;
790   Vector<GlyphIndex>      newParagraphGlyphs;
791   newParagraphGlyphs.Reserve(numberOfParagraphs);
792
793   const Length currentNumberOfGlyphs = glyphs.Count();
794   if(NO_OPERATION != (SHAPE_TEXT & operations))
795   {
796     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
797     // Shapes the text.
798     ShapeText(textToShape,
799               lineBreakInfo,
800               scripts,
801               validFonts,
802               startIndex,
803               mTextUpdateInfo.mStartGlyphIndex,
804               requestedNumberOfCharacters,
805               glyphs,
806               glyphsToCharactersMap,
807               charactersPerGlyph,
808               newParagraphGlyphs);
809
810     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
811     mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
812     mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
813
814    updated = true;
815   }
816
817   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
818
819   if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
820   {
821     GlyphInfo* glyphsBuffer = glyphs.Begin();
822     mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
823
824     // Update the width and advance of all new paragraph characters.
825     for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
826     {
827       const GlyphIndex index = *it;
828       GlyphInfo&       glyph = *(glyphsBuffer + index);
829
830       glyph.xBearing = 0.f;
831       glyph.width    = 0.f;
832       glyph.advance  = 0.f;
833     }
834     updated = true;
835   }
836
837   if((nullptr != mEventData) &&
838      mEventData->mPreEditFlag &&
839      (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
840   {
841     Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
842     mEventData->mInputMethodContext.GetPreeditStyle(attrs);
843     Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
844
845     // Check the type of preedit and run it.
846     for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
847     {
848       Dali::InputMethodContext::PreeditAttributeData attrData = *it;
849       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d  start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
850       type = attrData.preeditType;
851
852       // Check the number of commit characters for the start position.
853       unsigned int numberOfCommit  = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
854       Length       numberOfIndices = attrData.endIndex - attrData.startIndex;
855
856       switch(type)
857       {
858         case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
859         {
860           // Add the underline for the pre-edit text.
861           GlyphRun underlineRun;
862           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
863           underlineRun.numberOfGlyphs = numberOfIndices;
864           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
865
866           //Mark-up processor case
867           if(mModel->mVisualModel->IsMarkupProcessorEnabled())
868           {
869             CopyUnderlinedFromLogicalToVisualModels(false);
870           }
871           break;
872         }
873         case Dali::InputMethodContext::PreeditStyle::REVERSE:
874         {
875           Vector4  textColor = mModel->mVisualModel->GetTextColor();
876           ColorRun backgroundColorRun;
877           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
878           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
879           backgroundColorRun.color                           = textColor;
880           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
881
882           Vector4          backgroundColor = mModel->mVisualModel->GetBackgroundColor();
883           Vector<ColorRun> colorRuns;
884           colorRuns.Resize(1u);
885           ColorRun& colorRun                       = *(colorRuns.Begin());
886           colorRun.color                           = backgroundColor;
887           colorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
888           colorRun.characterRun.numberOfCharacters = numberOfIndices;
889
890           mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
891           break;
892         }
893         case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
894         {
895           ColorRun backgroundColorRun;
896           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
897           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
898           backgroundColorRun.color                           = LIGHT_BLUE;
899           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
900           break;
901         }
902         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
903         {
904           // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
905           ColorRun backgroundColorRun;
906           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
907           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
908           backgroundColorRun.color                           = BACKGROUND_SUB4;
909           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
910
911           GlyphRun underlineRun;
912           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
913           underlineRun.numberOfGlyphs = numberOfIndices;
914           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
915
916           //Mark-up processor case
917           if(mModel->mVisualModel->IsMarkupProcessorEnabled())
918           {
919             CopyUnderlinedFromLogicalToVisualModels(false);
920           }
921           break;
922         }
923         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
924         {
925           // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
926           ColorRun backgroundColorRun;
927           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
928           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
929           backgroundColorRun.color                           = BACKGROUND_SUB5;
930           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
931
932           GlyphRun underlineRun;
933           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
934           underlineRun.numberOfGlyphs = numberOfIndices;
935           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
936
937           //Mark-up processor case
938           if(mModel->mVisualModel->IsMarkupProcessorEnabled())
939           {
940             CopyUnderlinedFromLogicalToVisualModels(false);
941           }
942           break;
943         }
944         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
945         {
946           // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
947           ColorRun backgroundColorRun;
948           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
949           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
950           backgroundColorRun.color                           = BACKGROUND_SUB6;
951           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
952
953           GlyphRun underlineRun;
954           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
955           underlineRun.numberOfGlyphs = numberOfIndices;
956           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
957
958           //Mark-up processor case
959           if(mModel->mVisualModel->IsMarkupProcessorEnabled())
960           {
961             CopyUnderlinedFromLogicalToVisualModels(false);
962           }
963           break;
964         }
965         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
966         {
967           // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
968           ColorRun backgroundColorRun;
969           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
970           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
971           backgroundColorRun.color                           = BACKGROUND_SUB7;
972           mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
973
974           GlyphRun underlineRun;
975           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
976           underlineRun.numberOfGlyphs = numberOfIndices;
977           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
978
979           //Mark-up processor case
980           if(mModel->mVisualModel->IsMarkupProcessorEnabled())
981           {
982             CopyUnderlinedFromLogicalToVisualModels(false);
983           }
984           break;
985         }
986         case Dali::InputMethodContext::PreeditStyle::NONE:
987         default:
988         {
989           break;
990         }
991       }
992     }
993     attrs.Clear();
994     updated = true;
995   }
996
997   if(NO_OPERATION != (COLOR & operations))
998   {
999     // Set the color runs in glyphs.
1000     SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1001                              mModel->mVisualModel->mCharactersToGlyph,
1002                              mModel->mVisualModel->mGlyphsPerCharacter,
1003                              startIndex,
1004                              mTextUpdateInfo.mStartGlyphIndex,
1005                              requestedNumberOfCharacters,
1006                              mModel->mVisualModel->mColors,
1007                              mModel->mVisualModel->mColorIndices);
1008
1009     // Set the background color runs in glyphs.
1010     SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1011                              mModel->mVisualModel->mCharactersToGlyph,
1012                              mModel->mVisualModel->mGlyphsPerCharacter,
1013                              startIndex,
1014                              mTextUpdateInfo.mStartGlyphIndex,
1015                              requestedNumberOfCharacters,
1016                              mModel->mVisualModel->mBackgroundColors,
1017                              mModel->mVisualModel->mBackgroundColorIndices);
1018
1019     updated = true;
1020   }
1021
1022   if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1023       ! ((nullptr != mEventData) &&
1024          mEventData->mPreEditFlag &&
1025          (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1026   {
1027     //Mark-up processor case
1028     if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1029     {
1030       CopyUnderlinedFromLogicalToVisualModels(true);
1031     }
1032
1033     updated = true;
1034   }
1035
1036
1037   // The estimated number of lines. Used to avoid reallocations when layouting.
1038   mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1039
1040   // Set the previous number of characters for the next time the text is updated.
1041   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1042
1043   return updated;
1044 }
1045
1046 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1047 {
1048   // Sets the default text's color.
1049   inputStyle.textColor      = mTextColor;
1050   inputStyle.isDefaultColor = true;
1051
1052   inputStyle.familyName.clear();
1053   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1054   inputStyle.width  = TextAbstraction::FontWidth::NORMAL;
1055   inputStyle.slant  = TextAbstraction::FontSlant::NORMAL;
1056   inputStyle.size   = 0.f;
1057
1058   inputStyle.lineSpacing = 0.f;
1059
1060   inputStyle.underlineProperties.clear();
1061   inputStyle.shadowProperties.clear();
1062   inputStyle.embossProperties.clear();
1063   inputStyle.outlineProperties.clear();
1064
1065   inputStyle.isFamilyDefined = false;
1066   inputStyle.isWeightDefined = false;
1067   inputStyle.isWidthDefined  = false;
1068   inputStyle.isSlantDefined  = false;
1069   inputStyle.isSizeDefined   = false;
1070
1071   inputStyle.isLineSpacingDefined = false;
1072
1073   inputStyle.isUnderlineDefined = false;
1074   inputStyle.isShadowDefined    = false;
1075   inputStyle.isEmbossDefined    = false;
1076   inputStyle.isOutlineDefined   = false;
1077
1078   // Sets the default font's family name, weight, width, slant and size.
1079   if(mFontDefaults)
1080   {
1081     if(mFontDefaults->familyDefined)
1082     {
1083       inputStyle.familyName      = mFontDefaults->mFontDescription.family;
1084       inputStyle.isFamilyDefined = true;
1085     }
1086
1087     if(mFontDefaults->weightDefined)
1088     {
1089       inputStyle.weight          = mFontDefaults->mFontDescription.weight;
1090       inputStyle.isWeightDefined = true;
1091     }
1092
1093     if(mFontDefaults->widthDefined)
1094     {
1095       inputStyle.width          = mFontDefaults->mFontDescription.width;
1096       inputStyle.isWidthDefined = true;
1097     }
1098
1099     if(mFontDefaults->slantDefined)
1100     {
1101       inputStyle.slant          = mFontDefaults->mFontDescription.slant;
1102       inputStyle.isSlantDefined = true;
1103     }
1104
1105     if(mFontDefaults->sizeDefined)
1106     {
1107       inputStyle.size          = mFontDefaults->mDefaultPointSize;
1108       inputStyle.isSizeDefined = true;
1109     }
1110   }
1111 }
1112
1113 float Controller::Impl::GetDefaultFontLineHeight()
1114 {
1115   FontId defaultFontId = 0u;
1116   if(nullptr == mFontDefaults)
1117   {
1118     TextAbstraction::FontDescription fontDescription;
1119     defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1120   }
1121   else
1122   {
1123     defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1124   }
1125
1126   Text::FontMetrics fontMetrics;
1127   mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1128
1129   return (fontMetrics.ascender - fontMetrics.descender);
1130 }
1131
1132 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1133 {
1134   if(nullptr == mEventData)
1135   {
1136     // Nothing to do if there is no text.
1137     return;
1138   }
1139
1140   if(mEventData->mSelectionEnabled && (pStart || pEnd))
1141   {
1142     uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1143
1144     if(pStart)
1145     {
1146       mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1147     }
1148     if(pEnd)
1149     {
1150       mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1151     }
1152
1153     if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1154     {
1155       ChangeState(EventData::EDITING);
1156       mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1157       mEventData->mUpdateCursorPosition                                       = true;
1158     }
1159     else
1160     {
1161       ChangeState(EventData::SELECTING);
1162       mEventData->mUpdateHighlightBox           = true;
1163       mEventData->mUpdateLeftSelectionPosition  = true;
1164       mEventData->mUpdateRightSelectionPosition = true;
1165     }
1166   }
1167 }
1168
1169 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1170 {
1171   if(nullptr == mEventData)
1172   {
1173     return 0;
1174   }
1175   return mEventData->mPrimaryCursorPosition;
1176 }
1177
1178 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1179 {
1180   if(nullptr == mEventData)
1181   {
1182     // Nothing to do if there is no text.
1183     return false;
1184   }
1185
1186   if(mEventData->mPrimaryCursorPosition == index)
1187   {
1188     // Nothing for same cursor position.
1189     return false;
1190   }
1191
1192   uint32_t length                    = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1193   mEventData->mPrimaryCursorPosition = std::min(index, length);
1194   ChangeState(EventData::EDITING);
1195   mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1196   mEventData->mUpdateCursorPosition                                        = true;
1197   ScrollTextToMatchCursor();
1198   return true;
1199 }
1200
1201 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1202 {
1203   Uint32Pair range;
1204
1205   if(mEventData)
1206   {
1207     range.first  = mEventData->mLeftSelectionPosition;
1208     range.second = mEventData->mRightSelectionPosition;
1209   }
1210
1211   return range;
1212 }
1213
1214 bool Controller::Impl::IsEditable() const
1215 {
1216   return mEventData && mEventData->mEditingEnabled;
1217 }
1218
1219 void Controller::Impl::SetEditable(bool editable)
1220 {
1221   if(mEventData)
1222   {
1223     mEventData->mEditingEnabled = editable;
1224   }
1225 }
1226
1227 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1228 {
1229   if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1230   {
1231     // Nothing to select if handles are in the same place.
1232     selectedText.clear();
1233     return;
1234   }
1235
1236   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1237
1238   //Get start and end position of selection
1239   const CharacterIndex startOfSelectedText  = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1240   const Length         lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1241
1242   Vector<Character>& utf32Characters    = mModel->mLogicalModel->mText;
1243   const Length       numberOfCharacters = utf32Characters.Count();
1244
1245   // Validate the start and end selection points
1246   if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1247   {
1248     //Get text as a UTF8 string
1249     Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1250
1251     if(deleteAfterRetrieval) // Only delete text if copied successfully
1252     {
1253       // Keep a copy of the current input style.
1254       InputStyle currentInputStyle;
1255       currentInputStyle.Copy(mEventData->mInputStyle);
1256
1257       // Set as input style the style of the first deleted character.
1258       mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1259
1260       // Compare if the input style has changed.
1261       const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1262
1263       if(hasInputStyleChanged)
1264       {
1265         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1266         // Queue the input style changed signal.
1267         mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1268       }
1269
1270       mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1271
1272       // Mark the paragraphs to be updated.
1273       if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1274       {
1275         mTextUpdateInfo.mCharacterIndex             = 0;
1276         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1277         mTextUpdateInfo.mNumberOfCharactersToAdd    = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1278         mTextUpdateInfo.mClearAll                   = true;
1279       }
1280       else
1281       {
1282         mTextUpdateInfo.mCharacterIndex             = startOfSelectedText;
1283         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1284       }
1285
1286       // Delete text between handles
1287       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1288       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1289       utf32Characters.Erase(first, last);
1290
1291       // Will show the cursor at the first character of the selection.
1292       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1293     }
1294     else
1295     {
1296       // Will show the cursor at the last character of the selection.
1297       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1298     }
1299
1300     mEventData->mDecoratorUpdated = true;
1301   }
1302 }
1303
1304 void Controller::Impl::SetSelection(int start, int end)
1305 {
1306   mEventData->mLeftSelectionPosition  = start;
1307   mEventData->mRightSelectionPosition = end;
1308   mEventData->mUpdateCursorPosition   = true;
1309 }
1310
1311 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1312 {
1313   return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1314 }
1315
1316 void Controller::Impl::ShowClipboard()
1317 {
1318   if(mClipboard)
1319   {
1320     mClipboard.ShowClipboard();
1321   }
1322 }
1323
1324 void Controller::Impl::HideClipboard()
1325 {
1326   if(mClipboard && mClipboardHideEnabled)
1327   {
1328     mClipboard.HideClipboard();
1329   }
1330 }
1331
1332 void Controller::Impl::SetClipboardHideEnable(bool enable)
1333 {
1334   mClipboardHideEnabled = enable;
1335 }
1336
1337 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1338 {
1339   //Send string to clipboard
1340   return (mClipboard && mClipboard.SetItem(source));
1341 }
1342
1343 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1344 {
1345   std::string selectedText;
1346   RetrieveSelection(selectedText, deleteAfterSending);
1347   CopyStringToClipboard(selectedText);
1348   ChangeState(EventData::EDITING);
1349 }
1350
1351 void Controller::Impl::RequestGetTextFromClipboard()
1352 {
1353   if(mClipboard)
1354   {
1355     mClipboard.RequestItem();
1356   }
1357 }
1358
1359 void Controller::Impl::RepositionSelectionHandles()
1360 {
1361   SelectionHandleController::Reposition(*this);
1362 }
1363 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1364 {
1365   SelectionHandleController::Reposition(*this, visualX, visualY, action);
1366 }
1367
1368 void Controller::Impl::SetPopupButtons()
1369 {
1370   /**
1371    *  Sets the Popup buttons to be shown depending on State.
1372    *
1373    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1374    *
1375    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1376    */
1377
1378   bool                        isEditable    = IsEditable();
1379   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1380
1381   if(EventData::SELECTING == mEventData->mState)
1382   {
1383     buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1384     if(isEditable)
1385     {
1386       buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1387     }
1388
1389     if(!IsClipboardEmpty())
1390     {
1391       if(isEditable)
1392       {
1393         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1394       }
1395       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1396     }
1397
1398     if(!mEventData->mAllTextSelected)
1399     {
1400       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1401     }
1402   }
1403   else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1404   {
1405     if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1406     {
1407       buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1408     }
1409
1410     if(!IsClipboardEmpty())
1411     {
1412       if(isEditable)
1413       {
1414         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1415       }
1416       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1417     }
1418   }
1419   else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1420   {
1421     if(!IsClipboardEmpty())
1422     {
1423       if(isEditable)
1424       {
1425         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1426       }
1427       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1428     }
1429   }
1430
1431   mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1432 }
1433
1434 void Controller::Impl::ChangeState(EventData::State newState)
1435 {
1436   if(nullptr == mEventData)
1437   {
1438     // Nothing to do if there is no text input.
1439     return;
1440   }
1441
1442   DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState);
1443
1444   if(mEventData->mState != newState)
1445   {
1446     mEventData->mPreviousState = mEventData->mState;
1447     mEventData->mState         = newState;
1448
1449     switch(mEventData->mState)
1450     {
1451       case EventData::INACTIVE:
1452       {
1453         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1454         mEventData->mDecorator->StopCursorBlink();
1455         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1456         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1457         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1458         mEventData->mDecorator->SetHighlightActive(false);
1459         mEventData->mDecorator->SetPopupActive(false);
1460         mEventData->mDecoratorUpdated = true;
1461         break;
1462       }
1463       case EventData::INTERRUPTED:
1464       {
1465         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1466         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1467         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1468         mEventData->mDecorator->SetHighlightActive(false);
1469         mEventData->mDecorator->SetPopupActive(false);
1470         mEventData->mDecoratorUpdated = true;
1471         break;
1472       }
1473       case EventData::SELECTING:
1474       {
1475         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1476         mEventData->mDecorator->StopCursorBlink();
1477         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1478         if(mEventData->mGrabHandleEnabled)
1479         {
1480           mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1481           mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1482         }
1483         mEventData->mDecorator->SetHighlightActive(true);
1484         if(mEventData->mGrabHandlePopupEnabled)
1485         {
1486           SetPopupButtons();
1487           mEventData->mDecorator->SetPopupActive(true);
1488         }
1489         mEventData->mDecoratorUpdated = true;
1490         break;
1491       }
1492       case EventData::EDITING:
1493       {
1494         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1495         if(mEventData->mCursorBlinkEnabled)
1496         {
1497           mEventData->mDecorator->StartCursorBlink();
1498         }
1499         // Grab handle is not shown until a tap is received whilst EDITING
1500         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1501         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1502         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1503         mEventData->mDecorator->SetHighlightActive(false);
1504         if(mEventData->mGrabHandlePopupEnabled)
1505         {
1506           mEventData->mDecorator->SetPopupActive(false);
1507         }
1508         mEventData->mDecoratorUpdated = true;
1509         break;
1510       }
1511       case EventData::EDITING_WITH_POPUP:
1512       {
1513         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1514
1515         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1516         if(mEventData->mCursorBlinkEnabled)
1517         {
1518           mEventData->mDecorator->StartCursorBlink();
1519         }
1520         if(mEventData->mSelectionEnabled)
1521         {
1522           mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1523           mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1524           mEventData->mDecorator->SetHighlightActive(false);
1525         }
1526         else if(mEventData->mGrabHandleEnabled)
1527         {
1528           mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1529         }
1530         if(mEventData->mGrabHandlePopupEnabled)
1531         {
1532           SetPopupButtons();
1533           mEventData->mDecorator->SetPopupActive(true);
1534         }
1535         mEventData->mDecoratorUpdated = true;
1536         break;
1537       }
1538       case EventData::EDITING_WITH_GRAB_HANDLE:
1539       {
1540         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1541
1542         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1543         if(mEventData->mCursorBlinkEnabled)
1544         {
1545           mEventData->mDecorator->StartCursorBlink();
1546         }
1547         // Grab handle is not shown until a tap is received whilst EDITING
1548         if(mEventData->mGrabHandleEnabled)
1549         {
1550           mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1551         }
1552         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1553         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1554         mEventData->mDecorator->SetHighlightActive(false);
1555         if(mEventData->mGrabHandlePopupEnabled)
1556         {
1557           mEventData->mDecorator->SetPopupActive(false);
1558         }
1559         mEventData->mDecoratorUpdated = true;
1560         break;
1561       }
1562       case EventData::SELECTION_HANDLE_PANNING:
1563       {
1564         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1565         mEventData->mDecorator->StopCursorBlink();
1566         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1567         if(mEventData->mGrabHandleEnabled)
1568         {
1569           mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1570           mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1571         }
1572         mEventData->mDecorator->SetHighlightActive(true);
1573         if(mEventData->mGrabHandlePopupEnabled)
1574         {
1575           mEventData->mDecorator->SetPopupActive(false);
1576         }
1577         mEventData->mDecoratorUpdated = true;
1578         break;
1579       }
1580       case EventData::GRAB_HANDLE_PANNING:
1581       {
1582         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1583
1584         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1585         if(mEventData->mCursorBlinkEnabled)
1586         {
1587           mEventData->mDecorator->StartCursorBlink();
1588         }
1589         if(mEventData->mGrabHandleEnabled)
1590         {
1591           mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1592         }
1593         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1594         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1595         mEventData->mDecorator->SetHighlightActive(false);
1596         if(mEventData->mGrabHandlePopupEnabled)
1597         {
1598           mEventData->mDecorator->SetPopupActive(false);
1599         }
1600         mEventData->mDecoratorUpdated = true;
1601         break;
1602       }
1603       case EventData::EDITING_WITH_PASTE_POPUP:
1604       {
1605         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1606
1607         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1608         if(mEventData->mCursorBlinkEnabled)
1609         {
1610           mEventData->mDecorator->StartCursorBlink();
1611         }
1612
1613         if(mEventData->mGrabHandleEnabled)
1614         {
1615           mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1616         }
1617         mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1618         mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1619         mEventData->mDecorator->SetHighlightActive(false);
1620
1621         if(mEventData->mGrabHandlePopupEnabled)
1622         {
1623           SetPopupButtons();
1624           mEventData->mDecorator->SetPopupActive(true);
1625         }
1626         mEventData->mDecoratorUpdated = true;
1627         break;
1628       }
1629       case EventData::TEXT_PANNING:
1630       {
1631         mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1632         mEventData->mDecorator->StopCursorBlink();
1633         mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1634         if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1635            mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1636         {
1637           mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1638           mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1639           mEventData->mDecorator->SetHighlightActive(true);
1640         }
1641
1642         if(mEventData->mGrabHandlePopupEnabled)
1643         {
1644           mEventData->mDecorator->SetPopupActive(false);
1645         }
1646
1647         mEventData->mDecoratorUpdated = true;
1648         break;
1649       }
1650     }
1651   }
1652 }
1653
1654 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1655                                          CursorInfo&    cursorInfo)
1656 {
1657   if(!IsShowingRealText())
1658   {
1659     // Do not want to use the place-holder text to set the cursor position.
1660
1661     // Use the line's height of the font's family set to set the cursor's size.
1662     // If there is no font's family set, use the default font.
1663     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1664
1665     cursorInfo.lineOffset          = 0.f;
1666     cursorInfo.lineHeight          = GetDefaultFontLineHeight();
1667     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1668
1669     bool isRTL = false;
1670     if(mModel->mMatchSystemLanguageDirection)
1671     {
1672       isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1673     }
1674
1675     switch(mModel->mHorizontalAlignment)
1676     {
1677       case Text::HorizontalAlignment::BEGIN:
1678       {
1679         if(isRTL)
1680         {
1681           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1682         }
1683         else
1684         {
1685           cursorInfo.primaryPosition.x = 0.f;
1686         }
1687         break;
1688       }
1689       case Text::HorizontalAlignment::CENTER:
1690       {
1691         cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1692         break;
1693       }
1694       case Text::HorizontalAlignment::END:
1695       {
1696         if(isRTL)
1697         {
1698           cursorInfo.primaryPosition.x = 0.f;
1699         }
1700         else
1701         {
1702           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1703         }
1704         break;
1705       }
1706     }
1707
1708     // Nothing else to do.
1709     return;
1710   }
1711
1712   const bool                  isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1713   GetCursorPositionParameters parameters;
1714   parameters.visualModel  = mModel->mVisualModel;
1715   parameters.logicalModel = mModel->mLogicalModel;
1716   parameters.metrics      = mMetrics;
1717   parameters.logical      = logical;
1718   parameters.isMultiline  = isMultiLine;
1719
1720   Text::GetCursorPosition(parameters,
1721                           cursorInfo);
1722
1723   // Adds Outline offset.
1724   const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1725   cursorInfo.primaryPosition.x += outlineWidth;
1726   cursorInfo.primaryPosition.y += outlineWidth;
1727   cursorInfo.secondaryPosition.x += outlineWidth;
1728   cursorInfo.secondaryPosition.y += outlineWidth;
1729
1730   if(isMultiLine)
1731   {
1732     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1733
1734     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1735     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1736
1737     if(0.f > cursorInfo.primaryPosition.x)
1738     {
1739       cursorInfo.primaryPosition.x = 0.f;
1740     }
1741
1742     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1743     if(cursorInfo.primaryPosition.x > edgeWidth)
1744     {
1745       cursorInfo.primaryPosition.x = edgeWidth;
1746     }
1747   }
1748 }
1749
1750 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1751 {
1752   if(nullptr == mEventData)
1753   {
1754     // Nothing to do if there is no text input.
1755     return 0u;
1756   }
1757
1758   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1759
1760   const GlyphIndex* const charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
1761   const Length* const     charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1762
1763   GlyphIndex glyphIndex         = *(charactersToGlyphBuffer + index);
1764   Length     numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1765
1766   if(numberOfCharacters > 1u)
1767   {
1768     const Script script = mModel->mLogicalModel->GetScript(index);
1769     if(HasLigatureMustBreak(script))
1770     {
1771       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1772       numberOfCharacters = 1u;
1773     }
1774   }
1775   else
1776   {
1777     while(0u == numberOfCharacters)
1778     {
1779       ++glyphIndex;
1780       numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1781     }
1782   }
1783
1784   if(index < mEventData->mPrimaryCursorPosition)
1785   {
1786     cursorIndex -= numberOfCharacters;
1787   }
1788   else
1789   {
1790     cursorIndex += numberOfCharacters;
1791   }
1792
1793   // Will update the cursor hook position.
1794   mEventData->mUpdateCursorHookPosition = true;
1795
1796   return cursorIndex;
1797 }
1798
1799 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1800 {
1801   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1802   if(nullptr == mEventData)
1803   {
1804     // Nothing to do if there is no text input.
1805     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1806     return;
1807   }
1808
1809   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1810
1811   mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1812
1813   // Sets the cursor position.
1814   mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1815                                       cursorPosition.x,
1816                                       cursorPosition.y,
1817                                       cursorInfo.primaryCursorHeight,
1818                                       cursorInfo.lineHeight);
1819   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1820
1821   if(mEventData->mUpdateGrabHandlePosition)
1822   {
1823     // Sets the grab handle position.
1824     mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1825                                         cursorPosition.x,
1826                                         cursorInfo.lineOffset + mModel->mScrollPosition.y,
1827                                         cursorInfo.lineHeight);
1828   }
1829
1830   if(cursorInfo.isSecondaryCursor)
1831   {
1832     mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1833                                         cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1834                                         cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1835                                         cursorInfo.secondaryCursorHeight,
1836                                         cursorInfo.lineHeight);
1837     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1838   }
1839
1840   // Set which cursors are active according the state.
1841   if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1842   {
1843     if(cursorInfo.isSecondaryCursor)
1844     {
1845       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1846     }
1847     else
1848     {
1849       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1850     }
1851   }
1852   else
1853   {
1854     mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1855   }
1856
1857   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1858 }
1859
1860 void Controller::Impl::UpdateSelectionHandle(HandleType        handleType,
1861                                              const CursorInfo& cursorInfo)
1862 {
1863   SelectionHandleController::Update(*this, handleType, cursorInfo);
1864 }
1865
1866 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1867 {
1868   // Clamp between -space & -alignment offset.
1869
1870   if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1871   {
1872     const float space         = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1873     mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1874     mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1875
1876     mEventData->mDecoratorUpdated = true;
1877   }
1878   else
1879   {
1880     mModel->mScrollPosition.x = 0.f;
1881   }
1882 }
1883
1884 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1885 {
1886   if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1887   {
1888     // Nothing to do if the text is single line.
1889     return;
1890   }
1891
1892   // Clamp between -space & 0.
1893   if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1894   {
1895     const float space         = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1896     mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1897     mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1898
1899     mEventData->mDecoratorUpdated = true;
1900   }
1901   else
1902   {
1903     mModel->mScrollPosition.y = 0.f;
1904   }
1905 }
1906
1907 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1908 {
1909   const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1910
1911   // position is in actor's coords.
1912   const float positionEndX = position.x + cursorWidth;
1913   const float positionEndY = position.y + lineHeight;
1914
1915   // Transform the position to decorator coords.
1916   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1917   const float decoratorPositionEndX   = positionEndX + mModel->mScrollPosition.x;
1918
1919   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1920   const float decoratorPositionEndY   = positionEndY + mModel->mScrollPosition.y;
1921
1922   if(decoratorPositionBeginX < 0.f)
1923   {
1924     mModel->mScrollPosition.x = -position.x;
1925   }
1926   else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1927   {
1928     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1929   }
1930
1931   if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1932   {
1933     if(decoratorPositionBeginY < 0.f)
1934     {
1935       mModel->mScrollPosition.y = -position.y;
1936     }
1937     else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1938     {
1939       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1940     }
1941   }
1942 }
1943
1944 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1945 {
1946   // Get the current cursor position in decorator coords.
1947   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1948
1949   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1950
1951   // Calculate the offset to match the cursor position before the character was deleted.
1952   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1953
1954   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1955   if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1956   {
1957     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1958     mModel->mScrollPosition.y            = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1959   }
1960
1961   ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1962   ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1963
1964   // Makes the new cursor position visible if needed.
1965   ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1966 }
1967
1968 void Controller::Impl::ScrollTextToMatchCursor()
1969 {
1970   CursorInfo cursorInfo;
1971   GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1972   ScrollTextToMatchCursor(cursorInfo);
1973 }
1974
1975 void Controller::Impl::RequestRelayout()
1976 {
1977   if(nullptr != mControlInterface)
1978   {
1979     mControlInterface->RequestTextRelayout();
1980   }
1981 }
1982
1983 Actor Controller::Impl::CreateBackgroundActor()
1984 {
1985   // NOTE: Currently we only support background color for one line left-to-right text,
1986   //       so the following calculation is based on one line left-to-right text only!
1987
1988   Actor actor;
1989
1990   Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1991   if(numberOfGlyphs > 0u)
1992   {
1993     Vector<GlyphInfo> glyphs;
1994     glyphs.Resize(numberOfGlyphs);
1995
1996     Vector<Vector2> positions;
1997     positions.Resize(numberOfGlyphs);
1998
1999     // Get the line where the glyphs are laid-out.
2000     const LineRun* lineRun         = mModel->mVisualModel->mLines.Begin();
2001     float          alignmentOffset = lineRun->alignmentOffset;
2002     numberOfGlyphs                 = mView.GetGlyphs(glyphs.Begin(),
2003                                      positions.Begin(),
2004                                      alignmentOffset,
2005                                      0u,
2006                                      numberOfGlyphs);
2007
2008     glyphs.Resize(numberOfGlyphs);
2009     positions.Resize(numberOfGlyphs);
2010
2011     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
2012     const Vector2* const   positionsBuffer = positions.Begin();
2013
2014     BackgroundMesh mesh;
2015     mesh.mVertices.Reserve(4u * glyphs.Size());
2016     mesh.mIndices.Reserve(6u * glyphs.Size());
2017
2018     const Vector2 textSize = mView.GetLayoutSize();
2019
2020     const float offsetX = textSize.width * 0.5f;
2021     const float offsetY = textSize.height * 0.5f;
2022
2023     const Vector4* const    backgroundColorsBuffer       = mView.GetBackgroundColors();
2024     const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2025     const Vector4&          defaultBackgroundColor       = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2026
2027     Vector4  quad;
2028     uint32_t numberOfQuads = 0u;
2029
2030     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2031     {
2032       const GlyphInfo& glyph = *(glyphsBuffer + i);
2033
2034       // Get the background color of the character.
2035       // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2036       const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2037       const Vector4&   backgroundColor      = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2038
2039       // Only create quads for glyphs with a background color
2040       if(backgroundColor != Color::TRANSPARENT)
2041       {
2042         const Vector2 position = *(positionsBuffer + i);
2043
2044         if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2045         {
2046           quad.x = position.x;
2047           quad.y = 0.0f;
2048           quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2049           quad.w = textSize.height;
2050         }
2051         else if(i == 0u) // The first glyph in the whole text
2052         {
2053           quad.x = position.x;
2054           quad.y = 0.0f;
2055           quad.z = quad.x - glyph.xBearing + glyph.advance;
2056           quad.w = textSize.height;
2057         }
2058         else if(i == glyphSize - 1u) // The last glyph in the whole text
2059         {
2060           quad.x = position.x - glyph.xBearing;
2061           quad.y = 0.0f;
2062           quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2063           quad.w = textSize.height;
2064         }
2065         else // The glyph in the middle of the text
2066         {
2067           quad.x = position.x - glyph.xBearing;
2068           quad.y = 0.0f;
2069           quad.z = quad.x + glyph.advance;
2070           quad.w = textSize.height;
2071         }
2072
2073         BackgroundVertex vertex;
2074
2075         // Top left
2076         vertex.mPosition.x = quad.x - offsetX;
2077         vertex.mPosition.y = quad.y - offsetY;
2078         vertex.mColor      = backgroundColor;
2079         mesh.mVertices.PushBack(vertex);
2080
2081         // Top right
2082         vertex.mPosition.x = quad.z - offsetX;
2083         vertex.mPosition.y = quad.y - offsetY;
2084         vertex.mColor      = backgroundColor;
2085         mesh.mVertices.PushBack(vertex);
2086
2087         // Bottom left
2088         vertex.mPosition.x = quad.x - offsetX;
2089         vertex.mPosition.y = quad.w - offsetY;
2090         vertex.mColor      = backgroundColor;
2091         mesh.mVertices.PushBack(vertex);
2092
2093         // Bottom right
2094         vertex.mPosition.x = quad.z - offsetX;
2095         vertex.mPosition.y = quad.w - offsetY;
2096         vertex.mColor      = backgroundColor;
2097         mesh.mVertices.PushBack(vertex);
2098
2099         // Six indices in counter clockwise winding
2100         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2101         mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2102         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2103         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2104         mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2105         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2106
2107         numberOfQuads++;
2108       }
2109     }
2110
2111     // Only create the background actor if there are glyphs with background color
2112     if(mesh.mVertices.Count() > 0u)
2113     {
2114       Property::Map quadVertexFormat;
2115       quadVertexFormat["aPosition"] = Property::VECTOR2;
2116       quadVertexFormat["aColor"]    = Property::VECTOR4;
2117
2118       VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2119       quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2120
2121       Geometry quadGeometry = Geometry::New();
2122       quadGeometry.AddVertexBuffer(quadVertices);
2123       quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2124
2125       if(!mShaderBackground)
2126       {
2127         mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2128       }
2129
2130       Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2131       renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2132       renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2133
2134       actor = Actor::New();
2135       actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2136       actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2137       actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2138       actor.SetProperty(Actor::Property::SIZE, textSize);
2139       actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2140       actor.AddRenderer(renderer);
2141     }
2142   }
2143
2144   return actor;
2145 }
2146
2147 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2148 {
2149     //Underlined character runs for markup-processor
2150     const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2151     const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
2152     const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
2153
2154     if(shouldClearPreUnderlineRuns)
2155     {
2156         mModel->mVisualModel->mUnderlineRuns.Clear();
2157     }
2158
2159     for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2160     {
2161         CharacterIndex characterIndex = it->characterRun.characterIndex;
2162         Length numberOfCharacters = it->characterRun.numberOfCharacters;
2163         for(Length index=0u; index<numberOfCharacters; index++)
2164         {
2165           GlyphRun underlineGlyphRun;
2166           underlineGlyphRun.glyphIndex     = charactersToGlyph[characterIndex + index];
2167           underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2168           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2169         }
2170     }
2171 }
2172
2173 } // namespace Text
2174
2175 } // namespace Toolkit
2176
2177 } // namespace Dali