fbfaf23cbef55750383318e9673a01c785ffee62
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2017 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/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24 #include <limits>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
36
37 namespace
38 {
39
40 /**
41  * @brief Struct used to calculate the selection box.
42  */
43 struct SelectionBoxInfo
44 {
45   float lineOffset;
46   float lineHeight;
47   float minX;
48   float maxX;
49 };
50
51 #if defined(DEBUG_ENABLED)
52   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
53 #endif
54
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
58
59 } // namespace
60
61 namespace Dali
62 {
63
64 namespace Toolkit
65 {
66
67 namespace Text
68 {
69
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
72   mImfManager(),
73   mPlaceholderFont( NULL ),
74   mPlaceholderTextActive(),
75   mPlaceholderTextInactive(),
76   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
77   mEventQueue(),
78   mInputStyleChangedQueue(),
79   mPreviousState( INACTIVE ),
80   mState( INACTIVE ),
81   mPrimaryCursorPosition( 0u ),
82   mLeftSelectionPosition( 0u ),
83   mRightSelectionPosition( 0u ),
84   mPreEditStartPosition( 0u ),
85   mPreEditLength( 0u ),
86   mCursorHookPositionX( 0.f ),
87   mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88   mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89   mIsShowingPlaceholderText( false ),
90   mPreEditFlag( false ),
91   mDecoratorUpdated( false ),
92   mCursorBlinkEnabled( true ),
93   mGrabHandleEnabled( true ),
94   mGrabHandlePopupEnabled( true ),
95   mSelectionEnabled( true ),
96   mUpdateCursorHookPosition( false ),
97   mUpdateCursorPosition( false ),
98   mUpdateGrabHandlePosition( false ),
99   mUpdateLeftSelectionPosition( false ),
100   mUpdateRightSelectionPosition( false ),
101   mIsLeftHandleSelected( false ),
102   mIsRightHandleSelected( false ),
103   mUpdateHighlightBox( false ),
104   mScrollAfterUpdatePosition( false ),
105   mScrollAfterDelete( false ),
106   mAllTextSelected( false ),
107   mUpdateInputStyle( false ),
108   mPasswordInput( false ),
109   mIsPlaceholderPixelSize( false ),
110   mIsPlaceholderElideEnabled( false ),
111   mPlaceholderEllipsisFlag( false ),
112   mShiftSelectionFlag( true )
113 {
114   mImfManager = ImfManager::Get();
115 }
116
117 EventData::~EventData()
118 {}
119
120 bool Controller::Impl::ProcessInputEvents()
121 {
122   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
123   if( NULL == mEventData )
124   {
125     // Nothing to do if there is no text input.
126     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
127     return false;
128   }
129
130   if( mEventData->mDecorator )
131   {
132     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
133          iter != mEventData->mEventQueue.end();
134          ++iter )
135     {
136       switch( iter->type )
137       {
138         case Event::CURSOR_KEY_EVENT:
139         {
140           OnCursorKeyEvent( *iter );
141           break;
142         }
143         case Event::TAP_EVENT:
144         {
145           OnTapEvent( *iter );
146           break;
147         }
148         case Event::LONG_PRESS_EVENT:
149         {
150           OnLongPressEvent( *iter );
151           break;
152         }
153         case Event::PAN_EVENT:
154         {
155           OnPanEvent( *iter );
156           break;
157         }
158         case Event::GRAB_HANDLE_EVENT:
159         case Event::LEFT_SELECTION_HANDLE_EVENT:
160         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
161         {
162           OnHandleEvent( *iter );
163           break;
164         }
165         case Event::SELECT:
166         {
167           OnSelectEvent( *iter );
168           break;
169         }
170         case Event::SELECT_ALL:
171         {
172           OnSelectAllEvent();
173           break;
174         }
175       }
176     }
177   }
178
179   if( mEventData->mUpdateCursorPosition ||
180       mEventData->mUpdateHighlightBox )
181   {
182     NotifyImfManager();
183   }
184
185   // The cursor must also be repositioned after inserts into the model
186   if( mEventData->mUpdateCursorPosition )
187   {
188     // Updates the cursor position and scrolls the text to make it visible.
189     CursorInfo cursorInfo;
190     // Calculate the cursor position from the new cursor index.
191     GetCursorPosition( mEventData->mPrimaryCursorPosition,
192                        cursorInfo );
193
194     if( mEventData->mUpdateCursorHookPosition )
195     {
196       // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
197       mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
198       mEventData->mUpdateCursorHookPosition = false;
199     }
200
201     // Scroll first the text after delete ...
202     if( mEventData->mScrollAfterDelete )
203     {
204       ScrollTextToMatchCursor( cursorInfo );
205     }
206
207     // ... then, text can be scrolled to make the cursor visible.
208     if( mEventData->mScrollAfterUpdatePosition )
209     {
210       const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
211       ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
212     }
213     mEventData->mScrollAfterUpdatePosition = false;
214     mEventData->mScrollAfterDelete = false;
215
216     UpdateCursorPosition( cursorInfo );
217
218     mEventData->mDecoratorUpdated = true;
219     mEventData->mUpdateCursorPosition = false;
220     mEventData->mUpdateGrabHandlePosition = false;
221   }
222   else
223   {
224     CursorInfo leftHandleInfo;
225     CursorInfo rightHandleInfo;
226
227     if( mEventData->mUpdateHighlightBox )
228     {
229       GetCursorPosition( mEventData->mLeftSelectionPosition,
230                          leftHandleInfo );
231
232       GetCursorPosition( mEventData->mRightSelectionPosition,
233                          rightHandleInfo );
234
235       if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
236       {
237         if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
238         {
239           CursorInfo& infoLeft = leftHandleInfo;
240
241           const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
242           ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
243
244           CursorInfo& infoRight = rightHandleInfo;
245
246           const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
247           ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
248         }
249         else
250         {
251           CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
252
253           const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
254           ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
255         }
256       }
257     }
258
259     if( mEventData->mUpdateLeftSelectionPosition )
260     {
261       UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
262                              leftHandleInfo );
263
264       SetPopupButtons();
265       mEventData->mDecoratorUpdated = true;
266       mEventData->mUpdateLeftSelectionPosition = false;
267     }
268
269     if( mEventData->mUpdateRightSelectionPosition )
270     {
271       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
272                              rightHandleInfo );
273
274       SetPopupButtons();
275       mEventData->mDecoratorUpdated = true;
276       mEventData->mUpdateRightSelectionPosition = false;
277     }
278
279     if( mEventData->mUpdateHighlightBox )
280     {
281       RepositionSelectionHandles();
282
283       mEventData->mUpdateLeftSelectionPosition = false;
284       mEventData->mUpdateRightSelectionPosition = false;
285       mEventData->mUpdateHighlightBox = false;
286       mEventData->mIsLeftHandleSelected = false;
287       mEventData->mIsRightHandleSelected = false;
288     }
289
290     mEventData->mScrollAfterUpdatePosition = false;
291   }
292
293   if( mEventData->mUpdateInputStyle )
294   {
295     // Keep a copy of the current input style.
296     InputStyle currentInputStyle;
297     currentInputStyle.Copy( mEventData->mInputStyle );
298
299     // Set the default style first.
300     RetrieveDefaultInputStyle( mEventData->mInputStyle );
301
302     // Get the character index from the cursor index.
303     const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
304
305     // Retrieve the style from the style runs stored in the logical model.
306     mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
307
308     // Compare if the input style has changed.
309     const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
310
311     if( hasInputStyleChanged )
312     {
313       const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
314       // Queue the input style changed signal.
315       mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
316     }
317
318     mEventData->mUpdateInputStyle = false;
319   }
320
321   mEventData->mEventQueue.clear();
322
323   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
324
325   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
326   mEventData->mDecoratorUpdated = false;
327
328   return decoratorUpdated;
329 }
330
331 void Controller::Impl::NotifyImfManager()
332 {
333   if( mEventData && mEventData->mImfManager )
334   {
335     CharacterIndex cursorPosition = GetLogicalCursorPosition();
336
337     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
338
339     // Update the cursor position by removing the initial white spaces.
340     if( cursorPosition < numberOfWhiteSpaces )
341     {
342       cursorPosition = 0u;
343     }
344     else
345     {
346       cursorPosition -= numberOfWhiteSpaces;
347     }
348
349     mEventData->mImfManager.SetCursorPosition( cursorPosition );
350     mEventData->mImfManager.NotifyCursorPosition();
351   }
352 }
353
354 void Controller::Impl::NotifyImfMultiLineStatus()
355 {
356   if ( mEventData )
357   {
358     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
359     mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
360   }
361 }
362
363 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
364 {
365   CharacterIndex cursorPosition = 0u;
366
367   if( mEventData )
368   {
369     if( ( EventData::SELECTING == mEventData->mState ) ||
370         ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
371     {
372       cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
373     }
374     else
375     {
376       cursorPosition = mEventData->mPrimaryCursorPosition;
377     }
378   }
379
380   return cursorPosition;
381 }
382
383 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
384 {
385   Length numberOfWhiteSpaces = 0u;
386
387   // Get the buffer to the text.
388   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
389
390   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
391   for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
392   {
393     if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
394     {
395       break;
396     }
397   }
398
399   return numberOfWhiteSpaces;
400 }
401
402 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
403 {
404   // Get the total number of characters.
405   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
406
407   // Retrieve the text.
408   if( 0u != numberOfCharacters )
409   {
410     Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
411   }
412 }
413
414 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
415 {
416   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
417   mTextUpdateInfo.mStartGlyphIndex = 0u;
418   mTextUpdateInfo.mStartLineIndex = 0u;
419   numberOfCharacters = 0u;
420
421   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
422   if( 0u == numberOfParagraphs )
423   {
424     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
425     numberOfCharacters = 0u;
426
427     // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
428     mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
429
430     // Nothing else to do if there are no paragraphs.
431     return;
432   }
433
434   // Find the paragraphs to be updated.
435   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
436   if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
437   {
438     // Text is being added at the end of the current text.
439     if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
440     {
441       // Text is being added in a new paragraph after the last character of the text.
442       mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
443       numberOfCharacters = 0u;
444
445       // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
446       mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
447
448       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
449       mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
450
451       // Nothing else to do;
452       return;
453     }
454
455     paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
456   }
457   else
458   {
459     Length numberOfCharactersToUpdate = 0u;
460     if( mTextUpdateInfo.mFullRelayoutNeeded )
461     {
462       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
463     }
464     else
465     {
466       numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
467     }
468     mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
469                                            numberOfCharactersToUpdate,
470                                            paragraphsToBeUpdated );
471   }
472
473   if( 0u != paragraphsToBeUpdated.Count() )
474   {
475     const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
476     const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
477     mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
478
479     ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
480     const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
481
482     if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) &&                                            // Some character are removed.
483         ( lastParagraphIndex < numberOfParagraphs - 1u ) &&                                                // There is a next paragraph.
484         ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
485           ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
486     {
487       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
488       const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
489
490       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491     }
492     else
493     {
494       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
495     }
496   }
497
498   // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
499   if( numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove )
500   {
501     mTextUpdateInfo.mRequestedNumberOfCharacters = 0u;
502   }
503   else
504   {
505     mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
506   }
507   mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
508 }
509
510 void Controller::Impl::ClearFullModelData( OperationsMask operations )
511 {
512   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
513   {
514     mModel->mLogicalModel->mLineBreakInfo.Clear();
515     mModel->mLogicalModel->mParagraphInfo.Clear();
516   }
517
518   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
519   {
520     mModel->mLogicalModel->mLineBreakInfo.Clear();
521   }
522
523   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
524   {
525     mModel->mLogicalModel->mScriptRuns.Clear();
526   }
527
528   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
529   {
530     mModel->mLogicalModel->mFontRuns.Clear();
531   }
532
533   if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
534   {
535     if( NO_OPERATION != ( BIDI_INFO & operations ) )
536     {
537       mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
538       mModel->mLogicalModel->mCharacterDirections.Clear();
539     }
540
541     if( NO_OPERATION != ( REORDER & operations ) )
542     {
543       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
544       for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
545              endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
546            it != endIt;
547            ++it )
548       {
549         BidirectionalLineInfoRun& bidiLineInfo = *it;
550
551         free( bidiLineInfo.visualToLogicalMap );
552         bidiLineInfo.visualToLogicalMap = NULL;
553       }
554       mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
555     }
556   }
557
558   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
559   {
560     mModel->mVisualModel->mGlyphs.Clear();
561     mModel->mVisualModel->mGlyphsToCharacters.Clear();
562     mModel->mVisualModel->mCharactersToGlyph.Clear();
563     mModel->mVisualModel->mCharactersPerGlyph.Clear();
564     mModel->mVisualModel->mGlyphsPerCharacter.Clear();
565     mModel->mVisualModel->mGlyphPositions.Clear();
566   }
567
568   if( NO_OPERATION != ( LAYOUT & operations ) )
569   {
570     mModel->mVisualModel->mLines.Clear();
571   }
572
573   if( NO_OPERATION != ( COLOR & operations ) )
574   {
575     mModel->mVisualModel->mColorIndices.Clear();
576   }
577 }
578
579 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
580 {
581   const CharacterIndex endIndexPlusOne = endIndex + 1u;
582
583   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
584   {
585     // Clear the line break info.
586     LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
587
588     mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
589                                                  lineBreakInfoBuffer + endIndexPlusOne );
590
591     // Clear the paragraphs.
592     ClearCharacterRuns( startIndex,
593                         endIndex,
594                         mModel->mLogicalModel->mParagraphInfo );
595   }
596
597   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
598   {
599     // Clear the word break info.
600     WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
601
602     mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
603                                          wordBreakInfoBuffer + endIndexPlusOne );
604   }
605
606   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
607   {
608     // Clear the scripts.
609     ClearCharacterRuns( startIndex,
610                         endIndex,
611                         mModel->mLogicalModel->mScriptRuns );
612   }
613
614   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
615   {
616     // Clear the fonts.
617     ClearCharacterRuns( startIndex,
618                         endIndex,
619                         mModel->mLogicalModel->mFontRuns );
620   }
621
622   if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
623   {
624     if( NO_OPERATION != ( BIDI_INFO & operations ) )
625     {
626       // Clear the bidirectional paragraph info.
627       ClearCharacterRuns( startIndex,
628                           endIndex,
629                           mModel->mLogicalModel->mBidirectionalParagraphInfo );
630
631       // Clear the character's directions.
632       CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
633
634       mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
635                                                          characterDirectionsBuffer + endIndexPlusOne );
636     }
637
638     if( NO_OPERATION != ( REORDER & operations ) )
639     {
640       uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
641       uint32_t endRemoveIndex = startRemoveIndex;
642       ClearCharacterRuns( startIndex,
643                           endIndex,
644                           mModel->mLogicalModel->mBidirectionalLineInfo,
645                           startRemoveIndex,
646                           endRemoveIndex );
647
648       BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
649
650       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
651       for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
652              endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
653            it != endIt;
654            ++it )
655       {
656         BidirectionalLineInfoRun& bidiLineInfo = *it;
657
658         free( bidiLineInfo.visualToLogicalMap );
659         bidiLineInfo.visualToLogicalMap = NULL;
660       }
661
662       mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
663                                                            bidirectionalLineInfoBuffer + endRemoveIndex );
664     }
665   }
666 }
667
668 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
669 {
670   const CharacterIndex endIndexPlusOne = endIndex + 1u;
671   const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
672
673   // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
674   GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
675   Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
676
677   const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
678   const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
679
680   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
681   {
682     // Update the character to glyph indices.
683     for( Vector<GlyphIndex>::Iterator it =  charactersToGlyphBuffer + endIndexPlusOne,
684            endIt =  charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
685          it != endIt;
686          ++it )
687     {
688       CharacterIndex& index = *it;
689       index -= numberOfGlyphsRemoved;
690     }
691
692     // Clear the character to glyph conversion table.
693     mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
694                                                     charactersToGlyphBuffer + endIndexPlusOne );
695
696     // Clear the glyphs per character table.
697     mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
698                                                      glyphsPerCharacterBuffer + endIndexPlusOne );
699
700     // Clear the glyphs buffer.
701     GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
702     mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
703                                          glyphsBuffer + endGlyphIndexPlusOne );
704
705     CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
706
707     // Update the glyph to character indices.
708     for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
709            endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
710          it != endIt;
711          ++it )
712     {
713       CharacterIndex& index = *it;
714       index -= numberOfCharactersRemoved;
715     }
716
717     // Clear the glyphs to characters buffer.
718     mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
719                                                      glyphsToCharactersBuffer  + endGlyphIndexPlusOne );
720
721     // Clear the characters per glyph buffer.
722     Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
723     mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
724                                                      charactersPerGlyphBuffer + endGlyphIndexPlusOne );
725
726     // Clear the positions buffer.
727     Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
728     mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
729                                                  positionsBuffer + endGlyphIndexPlusOne );
730   }
731
732   if( NO_OPERATION != ( LAYOUT & operations ) )
733   {
734     // Clear the lines.
735     uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
736     uint32_t endRemoveIndex = startRemoveIndex;
737     ClearCharacterRuns( startIndex,
738                         endIndex,
739                         mModel->mVisualModel->mLines,
740                         startRemoveIndex,
741                         endRemoveIndex );
742
743     // Will update the glyph runs.
744     startRemoveIndex = mModel->mVisualModel->mLines.Count();
745     endRemoveIndex = startRemoveIndex;
746     ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
747                     endGlyphIndexPlusOne - 1u,
748                     mModel->mVisualModel->mLines,
749                     startRemoveIndex,
750                     endRemoveIndex );
751
752     // Set the line index from where to insert the new laid-out lines.
753     mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
754
755     LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
756     mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
757                                         linesBuffer + endRemoveIndex );
758   }
759
760   if( NO_OPERATION != ( COLOR & operations ) )
761   {
762     if( 0u != mModel->mVisualModel->mColorIndices.Count() )
763     {
764       ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
765       mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
766                                                  colorIndexBuffer + endGlyphIndexPlusOne );
767     }
768   }
769 }
770
771 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
772 {
773   if( mTextUpdateInfo.mClearAll ||
774       ( ( 0u == startIndex ) &&
775         ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
776   {
777     ClearFullModelData( operations );
778   }
779   else
780   {
781     // Clear the model data related with characters.
782     ClearCharacterModelData( startIndex, endIndex, operations );
783
784     // Clear the model data related with glyphs.
785     ClearGlyphModelData( startIndex, endIndex, operations );
786   }
787
788   // The estimated number of lines. Used to avoid reallocations when layouting.
789   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
790
791   mModel->mVisualModel->ClearCaches();
792 }
793
794 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
795 {
796   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
797
798   // Calculate the operations to be done.
799   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
800
801   if( NO_OPERATION == operations )
802   {
803     // Nothing to do if no operations are pending and required.
804     return false;
805   }
806
807   Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
808   Vector<Character> displayCharacters;
809   bool useHiddenText = false;
810   if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
811   {
812     mHiddenInput->Substitute( srcCharacters,displayCharacters );
813     useHiddenText = true;
814   }
815
816   Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
817   const Length numberOfCharacters = utf32Characters.Count();
818
819   // Index to the first character of the first paragraph to be updated.
820   CharacterIndex startIndex = 0u;
821   // Number of characters of the paragraphs to be removed.
822   Length paragraphCharacters = 0u;
823
824   CalculateTextUpdateIndices( paragraphCharacters );
825
826   // Check whether the indices for updating the text is valid
827   if ( numberOfCharacters > 0u &&
828        ( mTextUpdateInfo.mParagraphCharacterIndex >= numberOfCharacters ||
829          mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
830   {
831     std::string currentText;
832     Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
833
834     DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
835     DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
836
837     // Dump mTextUpdateInfo
838     DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
839     DALI_LOG_ERROR( "     mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
840     DALI_LOG_ERROR( "     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
841     DALI_LOG_ERROR( "     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
842     DALI_LOG_ERROR( "     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
843     DALI_LOG_ERROR( "     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
844     DALI_LOG_ERROR( "     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
845     DALI_LOG_ERROR( "     mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
846     DALI_LOG_ERROR( "     mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
847     DALI_LOG_ERROR( "     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
848     DALI_LOG_ERROR( "     mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
849     DALI_LOG_ERROR( "     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
850     DALI_LOG_ERROR( "     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
851
852     return false;
853   }
854
855   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
856
857   if( mTextUpdateInfo.mClearAll ||
858       ( 0u != paragraphCharacters ) )
859   {
860     ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
861   }
862
863   mTextUpdateInfo.mClearAll = false;
864
865   // Whether the model is updated.
866   bool updated = false;
867
868   Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
869   const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
870
871   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
872   {
873     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
874     // calculate the bidirectional info for each 'paragraph'.
875     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
876     // is not shaped together).
877     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
878
879     SetLineBreakInfo( utf32Characters,
880                       startIndex,
881                       requestedNumberOfCharacters,
882                       lineBreakInfo );
883
884     // Create the paragraph info.
885     mModel->mLogicalModel->CreateParagraphInfo( startIndex,
886                                                 requestedNumberOfCharacters );
887     updated = true;
888   }
889
890   Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
891   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
892   {
893     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
894     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
895
896     SetWordBreakInfo( utf32Characters,
897                       startIndex,
898                       requestedNumberOfCharacters,
899                       wordBreakInfo );
900     updated = true;
901   }
902
903   const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
904   const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
905
906   Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
907   Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
908
909   if( getScripts || validateFonts )
910   {
911     // Validates the fonts assigned by the application or assigns default ones.
912     // It makes sure all the characters are going to be rendered by the correct font.
913     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
914
915     if( getScripts )
916     {
917       // Retrieves the scripts used in the text.
918       multilanguageSupport.SetScripts( utf32Characters,
919                                        startIndex,
920                                        requestedNumberOfCharacters,
921                                        scripts );
922     }
923
924     if( validateFonts )
925     {
926       // Validate the fonts set through the mark-up string.
927       Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
928
929       // Get the default font's description.
930       TextAbstraction::FontDescription defaultFontDescription;
931       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
932
933       if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
934       {
935         // If the placeholder font is set specifically, only placeholder font is changed.
936         defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
937         if( mEventData->mPlaceholderFont->sizeDefined )
938         {
939           defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
940         }
941       }
942       else if( NULL != mFontDefaults )
943       {
944         // Set the normal font and the placeholder font.
945         defaultFontDescription = mFontDefaults->mFontDescription;
946         defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
947       }
948
949       // Validates the fonts. If there is a character with no assigned font it sets a default one.
950       // After this call, fonts are validated.
951       multilanguageSupport.ValidateFonts( utf32Characters,
952                                           scripts,
953                                           fontDescriptionRuns,
954                                           defaultFontDescription,
955                                           defaultPointSize,
956                                           startIndex,
957                                           requestedNumberOfCharacters,
958                                           validFonts );
959     }
960     updated = true;
961   }
962
963   Vector<Character> mirroredUtf32Characters;
964   bool textMirrored = false;
965   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
966   if( NO_OPERATION != ( BIDI_INFO & operations ) )
967   {
968     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
969     bidirectionalInfo.Reserve( numberOfParagraphs );
970
971     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
972     SetBidirectionalInfo( utf32Characters,
973                           scripts,
974                           lineBreakInfo,
975                           startIndex,
976                           requestedNumberOfCharacters,
977                           bidirectionalInfo );
978
979     if( 0u != bidirectionalInfo.Count() )
980     {
981       // Only set the character directions if there is right to left characters.
982       Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
983       GetCharactersDirection( bidirectionalInfo,
984                               numberOfCharacters,
985                               startIndex,
986                               requestedNumberOfCharacters,
987                               directions );
988
989       // This paragraph has right to left text. Some characters may need to be mirrored.
990       // TODO: consider if the mirrored string can be stored as well.
991
992       textMirrored = GetMirroredText( utf32Characters,
993                                       directions,
994                                       bidirectionalInfo,
995                                       startIndex,
996                                       requestedNumberOfCharacters,
997                                       mirroredUtf32Characters );
998     }
999     else
1000     {
1001       // There is no right to left characters. Clear the directions vector.
1002       mModel->mLogicalModel->mCharacterDirections.Clear();
1003     }
1004     updated = true;
1005   }
1006
1007   Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1008   Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1009   Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1010   Vector<GlyphIndex> newParagraphGlyphs;
1011   newParagraphGlyphs.Reserve( numberOfParagraphs );
1012
1013   const Length currentNumberOfGlyphs = glyphs.Count();
1014   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1015   {
1016     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1017     // Shapes the text.
1018     ShapeText( textToShape,
1019                lineBreakInfo,
1020                scripts,
1021                validFonts,
1022                startIndex,
1023                mTextUpdateInfo.mStartGlyphIndex,
1024                requestedNumberOfCharacters,
1025                glyphs,
1026                glyphsToCharactersMap,
1027                charactersPerGlyph,
1028                newParagraphGlyphs );
1029
1030     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1031     mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1032     mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1033     updated = true;
1034   }
1035
1036   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1037
1038   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1039   {
1040     GlyphInfo* glyphsBuffer = glyphs.Begin();
1041     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1042
1043     // Update the width and advance of all new paragraph characters.
1044     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1045     {
1046       const GlyphIndex index = *it;
1047       GlyphInfo& glyph = *( glyphsBuffer + index );
1048
1049       glyph.xBearing = 0.f;
1050       glyph.width = 0.f;
1051       glyph.advance = 0.f;
1052     }
1053     updated = true;
1054   }
1055
1056   if( NO_OPERATION != ( COLOR & operations ) )
1057   {
1058     // Set the color runs in glyphs.
1059     SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1060                               mModel->mVisualModel->mCharactersToGlyph,
1061                               mModel->mVisualModel->mGlyphsPerCharacter,
1062                               startIndex,
1063                               mTextUpdateInfo.mStartGlyphIndex,
1064                               requestedNumberOfCharacters,
1065                               mModel->mVisualModel->mColors,
1066                               mModel->mVisualModel->mColorIndices );
1067
1068     updated = true;
1069   }
1070
1071   if( ( NULL != mEventData ) &&
1072       mEventData->mPreEditFlag &&
1073       ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1074   {
1075     // Add the underline for the pre-edit text.
1076     const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1077     const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1078
1079     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1080     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1081     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1082     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1083
1084     GlyphRun underlineRun;
1085     underlineRun.glyphIndex = glyphStart;
1086     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1087
1088     // TODO: At the moment the underline runs are only for pre-edit.
1089     mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1090   }
1091
1092   // The estimated number of lines. Used to avoid reallocations when layouting.
1093   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1094
1095   // Set the previous number of characters for the next time the text is updated.
1096   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1097
1098   return updated;
1099 }
1100
1101 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1102 {
1103   // Sets the default text's color.
1104   inputStyle.textColor = mTextColor;
1105   inputStyle.isDefaultColor = true;
1106
1107   inputStyle.familyName.clear();
1108   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1109   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1110   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1111   inputStyle.size = 0.f;
1112
1113   inputStyle.lineSpacing = 0.f;
1114
1115   inputStyle.underlineProperties.clear();
1116   inputStyle.shadowProperties.clear();
1117   inputStyle.embossProperties.clear();
1118   inputStyle.outlineProperties.clear();
1119
1120   inputStyle.isFamilyDefined = false;
1121   inputStyle.isWeightDefined = false;
1122   inputStyle.isWidthDefined = false;
1123   inputStyle.isSlantDefined = false;
1124   inputStyle.isSizeDefined = false;
1125
1126   inputStyle.isLineSpacingDefined = false;
1127
1128   inputStyle.isUnderlineDefined = false;
1129   inputStyle.isShadowDefined = false;
1130   inputStyle.isEmbossDefined = false;
1131   inputStyle.isOutlineDefined = false;
1132
1133   // Sets the default font's family name, weight, width, slant and size.
1134   if( mFontDefaults )
1135   {
1136     if( mFontDefaults->familyDefined )
1137     {
1138       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1139       inputStyle.isFamilyDefined = true;
1140     }
1141
1142     if( mFontDefaults->weightDefined )
1143     {
1144       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1145       inputStyle.isWeightDefined = true;
1146     }
1147
1148     if( mFontDefaults->widthDefined )
1149     {
1150       inputStyle.width = mFontDefaults->mFontDescription.width;
1151       inputStyle.isWidthDefined = true;
1152     }
1153
1154     if( mFontDefaults->slantDefined )
1155     {
1156       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1157       inputStyle.isSlantDefined = true;
1158     }
1159
1160     if( mFontDefaults->sizeDefined )
1161     {
1162       inputStyle.size = mFontDefaults->mDefaultPointSize;
1163       inputStyle.isSizeDefined = true;
1164     }
1165   }
1166 }
1167
1168 float Controller::Impl::GetDefaultFontLineHeight()
1169 {
1170   FontId defaultFontId = 0u;
1171   if( NULL == mFontDefaults )
1172   {
1173     TextAbstraction::FontDescription fontDescription;
1174     defaultFontId = mFontClient.GetFontId( fontDescription );
1175   }
1176   else
1177   {
1178     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1179   }
1180
1181   Text::FontMetrics fontMetrics;
1182   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1183
1184   return( fontMetrics.ascender - fontMetrics.descender );
1185 }
1186
1187 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1188 {
1189   if( NULL == mEventData )
1190   {
1191     // Nothing to do if there is no text input.
1192     return;
1193   }
1194
1195   int keyCode = event.p1.mInt;
1196   bool isShiftModifier = event.p2.mBool;
1197
1198   CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1199
1200   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1201   {
1202     if( mEventData->mPrimaryCursorPosition > 0u )
1203     {
1204       if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1205       {
1206         mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1207       }
1208       else
1209       {
1210         mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1211       }
1212     }
1213   }
1214   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1215   {
1216     if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1217     {
1218       if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1219       {
1220         mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1221       }
1222       else
1223       {
1224         mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1225       }
1226     }
1227   }
1228   else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1229   {
1230     // Ignore Shift-Up for text selection for now.
1231
1232     // Get first the line index of the current cursor position index.
1233     CharacterIndex characterIndex = 0u;
1234
1235     if( mEventData->mPrimaryCursorPosition > 0u )
1236     {
1237       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1238     }
1239
1240     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1241     const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1242
1243     // Retrieve the cursor position info.
1244     CursorInfo cursorInfo;
1245     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1246                        cursorInfo );
1247
1248     // Get the line above.
1249     const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1250
1251     // Get the next hit 'y' point.
1252     const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1253
1254     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1255     bool matchedCharacter = false;
1256     mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1257                                                                       mModel->mLogicalModel,
1258                                                                       mMetrics,
1259                                                                       mEventData->mCursorHookPositionX,
1260                                                                       hitPointY,
1261                                                                       CharacterHitTest::TAP,
1262                                                                       matchedCharacter );
1263   }
1264   else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1265   {
1266     // Ignore Shift-Down for text selection for now.
1267
1268     // Get first the line index of the current cursor position index.
1269     CharacterIndex characterIndex = 0u;
1270
1271     if( mEventData->mPrimaryCursorPosition > 0u )
1272     {
1273       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1274     }
1275
1276     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1277
1278     if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1279     {
1280       // Retrieve the cursor position info.
1281       CursorInfo cursorInfo;
1282       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1283                          cursorInfo );
1284
1285       // Get the line below.
1286       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1287
1288       // Get the next hit 'y' point.
1289       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1290
1291       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1292       bool matchedCharacter = false;
1293       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1294                                                                         mModel->mLogicalModel,
1295                                                                         mMetrics,
1296                                                                         mEventData->mCursorHookPositionX,
1297                                                                         hitPointY,
1298                                                                         CharacterHitTest::TAP,
1299                                                                         matchedCharacter );
1300     }
1301   }
1302
1303   if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1304   {
1305     // Update selection position after moving the cursor
1306     mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1307     mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1308   }
1309
1310   if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1311   {
1312     // Handle text selection
1313     bool selecting = false;
1314
1315     if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1316     {
1317       // Shift-Left/Right to select the text
1318       int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1319       if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1320       {
1321         mEventData->mRightSelectionPosition += cursorPositionDelta;
1322       }
1323       selecting = true;
1324     }
1325     else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1326     {
1327       // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1328       selecting = true;
1329     }
1330
1331     if ( selecting )
1332     {
1333       // Notify the cursor position to the imf manager.
1334       if( mEventData->mImfManager )
1335       {
1336         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1337         mEventData->mImfManager.NotifyCursorPosition();
1338       }
1339
1340       ChangeState( EventData::SELECTING );
1341
1342       mEventData->mUpdateLeftSelectionPosition = true;
1343       mEventData->mUpdateRightSelectionPosition = true;
1344       mEventData->mUpdateGrabHandlePosition = true;
1345       mEventData->mUpdateHighlightBox = true;
1346
1347       // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1348       if( mEventData->mGrabHandlePopupEnabled )
1349       {
1350         mEventData->mDecorator->SetPopupActive( false );
1351       }
1352     }
1353   }
1354   else
1355   {
1356     // Handle normal cursor move
1357     ChangeState( EventData::EDITING );
1358     mEventData->mUpdateCursorPosition = true;
1359   }
1360
1361   mEventData->mUpdateInputStyle = true;
1362   mEventData->mScrollAfterUpdatePosition = true;
1363 }
1364
1365 void Controller::Impl::OnTapEvent( const Event& event )
1366 {
1367   if( NULL != mEventData )
1368   {
1369     const unsigned int tapCount = event.p1.mUint;
1370
1371     if( 1u == tapCount )
1372     {
1373       if( IsShowingRealText() )
1374       {
1375         // Convert from control's coords to text's coords.
1376         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1377         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1378
1379         // Keep the tap 'x' position. Used to move the cursor.
1380         mEventData->mCursorHookPositionX = xPosition;
1381
1382         // Whether to touch point hits on a glyph.
1383         bool matchedCharacter = false;
1384         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1385                                                                           mModel->mLogicalModel,
1386                                                                           mMetrics,
1387                                                                           xPosition,
1388                                                                           yPosition,
1389                                                                           CharacterHitTest::TAP,
1390                                                                           matchedCharacter );
1391
1392         // When the cursor position is changing, delay cursor blinking
1393         mEventData->mDecorator->DelayCursorBlink();
1394       }
1395       else
1396       {
1397         mEventData->mPrimaryCursorPosition = 0u;
1398       }
1399
1400       // Update selection position after tapping
1401       mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1402       mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1403
1404       mEventData->mUpdateCursorPosition = true;
1405       mEventData->mUpdateGrabHandlePosition = true;
1406       mEventData->mScrollAfterUpdatePosition = true;
1407       mEventData->mUpdateInputStyle = true;
1408
1409       // Notify the cursor position to the imf manager.
1410       if( mEventData->mImfManager )
1411       {
1412         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1413         mEventData->mImfManager.NotifyCursorPosition();
1414       }
1415     }
1416     else if( 2u == tapCount )
1417     {
1418       if( mEventData->mSelectionEnabled )
1419       {
1420         // Convert from control's coords to text's coords.
1421         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1422         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1423
1424         // Calculates the logical position from the x,y coords.
1425         RepositionSelectionHandles( xPosition,
1426                                     yPosition,
1427                                     mEventData->mDoubleTapAction );
1428       }
1429     }
1430   }
1431 }
1432
1433 void Controller::Impl::OnPanEvent( const Event& event )
1434 {
1435   if( NULL == mEventData )
1436   {
1437     // Nothing to do if there is no text input.
1438     return;
1439   }
1440
1441   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1442   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1443
1444   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1445   {
1446     // Nothing to do if scrolling is not enabled.
1447     return;
1448   }
1449
1450   const int state = event.p1.mInt;
1451
1452   switch( state )
1453   {
1454     case Gesture::Started:
1455     {
1456       // Will remove the cursor, handles or text's popup, ...
1457       ChangeState( EventData::TEXT_PANNING );
1458       break;
1459     }
1460     case Gesture::Continuing:
1461     {
1462       const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1463       const Vector2 currentScroll = mModel->mScrollPosition;
1464
1465       if( isHorizontalScrollEnabled )
1466       {
1467         const float displacementX = event.p2.mFloat;
1468         mModel->mScrollPosition.x += displacementX;
1469
1470         ClampHorizontalScroll( layoutSize );
1471       }
1472
1473       if( isVerticalScrollEnabled )
1474       {
1475         const float displacementY = event.p3.mFloat;
1476         mModel->mScrollPosition.y += displacementY;
1477
1478         ClampVerticalScroll( layoutSize );
1479       }
1480
1481       mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1482       break;
1483     }
1484     case Gesture::Finished:
1485     case Gesture::Cancelled: // FALLTHROUGH
1486     {
1487       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1488       ChangeState( mEventData->mPreviousState );
1489       break;
1490     }
1491     default:
1492       break;
1493   }
1494 }
1495
1496 void Controller::Impl::OnLongPressEvent( const Event& event )
1497 {
1498   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1499
1500   if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1501   {
1502     ChangeState( EventData::EDITING_WITH_POPUP );
1503     mEventData->mDecoratorUpdated = true;
1504     mEventData->mUpdateInputStyle = true;
1505   }
1506   else
1507   {
1508     if( mEventData->mSelectionEnabled )
1509     {
1510       // Convert from control's coords to text's coords.
1511       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1512       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1513
1514       // Calculates the logical position from the x,y coords.
1515       RepositionSelectionHandles( xPosition,
1516                                   yPosition,
1517                                   mEventData->mLongPressAction );
1518     }
1519   }
1520 }
1521
1522 void Controller::Impl::OnHandleEvent( const Event& event )
1523 {
1524   if( NULL == mEventData )
1525   {
1526     // Nothing to do if there is no text input.
1527     return;
1528   }
1529
1530   const unsigned int state = event.p1.mUint;
1531   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1532   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1533
1534   if( HANDLE_PRESSED == state )
1535   {
1536     // Convert from decorator's coords to text's coords.
1537     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1538     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1539
1540     // Need to calculate the handle's new position.
1541     bool matchedCharacter = false;
1542     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1543                                                                           mModel->mLogicalModel,
1544                                                                           mMetrics,
1545                                                                           xPosition,
1546                                                                           yPosition,
1547                                                                           CharacterHitTest::SCROLL,
1548                                                                           matchedCharacter );
1549
1550     if( Event::GRAB_HANDLE_EVENT == event.type )
1551     {
1552       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1553
1554       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1555       {
1556         // Updates the cursor position if the handle's new position is different than the current one.
1557         mEventData->mUpdateCursorPosition = true;
1558         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1559         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1560         mEventData->mPrimaryCursorPosition = handleNewPosition;
1561       }
1562
1563       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1564       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1565     }
1566     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1567     {
1568       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1569
1570       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1571           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1572       {
1573         // Updates the highlight box if the handle's new position is different than the current one.
1574         mEventData->mUpdateHighlightBox = true;
1575         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1576         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1577         mEventData->mLeftSelectionPosition = handleNewPosition;
1578       }
1579
1580       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1581       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1582
1583       // Will define the order to scroll the text to match the handle position.
1584       mEventData->mIsLeftHandleSelected = true;
1585       mEventData->mIsRightHandleSelected = false;
1586     }
1587     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1588     {
1589       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1590
1591       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1592           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1593       {
1594         // Updates the highlight box if the handle's new position is different than the current one.
1595         mEventData->mUpdateHighlightBox = true;
1596         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1597         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1598         mEventData->mRightSelectionPosition = handleNewPosition;
1599       }
1600
1601       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1602       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1603
1604       // Will define the order to scroll the text to match the handle position.
1605       mEventData->mIsLeftHandleSelected = false;
1606       mEventData->mIsRightHandleSelected = true;
1607     }
1608   } // end ( HANDLE_PRESSED == state )
1609   else if( ( HANDLE_RELEASED == state ) ||
1610            handleStopScrolling )
1611   {
1612     CharacterIndex handlePosition = 0u;
1613     if( handleStopScrolling || isSmoothHandlePanEnabled )
1614     {
1615       // Convert from decorator's coords to text's coords.
1616       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1617       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1618
1619       bool matchedCharacter = false;
1620       handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1621                                                     mModel->mLogicalModel,
1622                                                     mMetrics,
1623                                                     xPosition,
1624                                                     yPosition,
1625                                                     CharacterHitTest::SCROLL,
1626                                                     matchedCharacter );
1627     }
1628
1629     if( Event::GRAB_HANDLE_EVENT == event.type )
1630     {
1631       mEventData->mUpdateCursorPosition = true;
1632       mEventData->mUpdateGrabHandlePosition = true;
1633       mEventData->mUpdateInputStyle = true;
1634
1635       if( !IsClipboardEmpty() )
1636       {
1637         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1638       }
1639
1640       if( handleStopScrolling || isSmoothHandlePanEnabled )
1641       {
1642         mEventData->mScrollAfterUpdatePosition = true;
1643         mEventData->mPrimaryCursorPosition = handlePosition;
1644       }
1645     }
1646     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1647     {
1648       ChangeState( EventData::SELECTING );
1649
1650       mEventData->mUpdateHighlightBox = true;
1651       mEventData->mUpdateLeftSelectionPosition = true;
1652       mEventData->mUpdateRightSelectionPosition = true;
1653
1654       if( handleStopScrolling || isSmoothHandlePanEnabled )
1655       {
1656         mEventData->mScrollAfterUpdatePosition = true;
1657
1658         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1659             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1660         {
1661           mEventData->mLeftSelectionPosition = handlePosition;
1662         }
1663       }
1664     }
1665     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1666     {
1667       ChangeState( EventData::SELECTING );
1668
1669       mEventData->mUpdateHighlightBox = true;
1670       mEventData->mUpdateRightSelectionPosition = true;
1671       mEventData->mUpdateLeftSelectionPosition = true;
1672
1673       if( handleStopScrolling || isSmoothHandlePanEnabled )
1674       {
1675         mEventData->mScrollAfterUpdatePosition = true;
1676         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1677             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1678         {
1679           mEventData->mRightSelectionPosition = handlePosition;
1680         }
1681       }
1682     }
1683
1684     mEventData->mDecoratorUpdated = true;
1685   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1686   else if( HANDLE_SCROLLING == state )
1687   {
1688     const float xSpeed = event.p2.mFloat;
1689     const float ySpeed = event.p3.mFloat;
1690     const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1691     const Vector2 currentScrollPosition = mModel->mScrollPosition;
1692
1693     mModel->mScrollPosition.x += xSpeed;
1694     mModel->mScrollPosition.y += ySpeed;
1695
1696     ClampHorizontalScroll( layoutSize );
1697     ClampVerticalScroll( layoutSize );
1698
1699     bool endOfScroll = false;
1700     if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1701     {
1702       // Notify the decorator there is no more text to scroll.
1703       // The decorator won't send more scroll events.
1704       mEventData->mDecorator->NotifyEndOfScroll();
1705       // Still need to set the position of the handle.
1706       endOfScroll = true;
1707     }
1708
1709     // Set the position of the handle.
1710     const bool scrollRightDirection = xSpeed > 0.f;
1711     const bool scrollBottomDirection = ySpeed > 0.f;
1712     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1713     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1714
1715     if( Event::GRAB_HANDLE_EVENT == event.type )
1716     {
1717       ChangeState( EventData::GRAB_HANDLE_PANNING );
1718
1719       // Get the grab handle position in decorator coords.
1720       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1721
1722       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1723       {
1724         // Position the grag handle close to either the left or right edge.
1725         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1726       }
1727
1728       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1729       {
1730         position.x = mEventData->mCursorHookPositionX;
1731
1732         // Position the grag handle close to either the top or bottom edge.
1733         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1734       }
1735
1736       // Get the new handle position.
1737       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1738       bool matchedCharacter = false;
1739       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1740                                                                          mModel->mLogicalModel,
1741                                                                          mMetrics,
1742                                                                          position.x - mModel->mScrollPosition.x,
1743                                                                          position.y - mModel->mScrollPosition.y,
1744                                                                          CharacterHitTest::SCROLL,
1745                                                                          matchedCharacter );
1746
1747       if( mEventData->mPrimaryCursorPosition != handlePosition )
1748       {
1749         mEventData->mUpdateCursorPosition = true;
1750         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1751         mEventData->mScrollAfterUpdatePosition = true;
1752         mEventData->mPrimaryCursorPosition = handlePosition;
1753       }
1754       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1755
1756       // Updates the decorator if the soft handle panning is enabled.
1757       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1758     }
1759     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1760     {
1761       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1762
1763       // Get the selection handle position in decorator coords.
1764       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1765
1766       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1767       {
1768         // Position the selection handle close to either the left or right edge.
1769         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1770       }
1771
1772       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1773       {
1774         position.x = mEventData->mCursorHookPositionX;
1775
1776         // Position the grag handle close to either the top or bottom edge.
1777         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1778       }
1779
1780       // Get the new handle position.
1781       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1782       bool matchedCharacter = false;
1783       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1784                                                                          mModel->mLogicalModel,
1785                                                                          mMetrics,
1786                                                                          position.x - mModel->mScrollPosition.x,
1787                                                                          position.y - mModel->mScrollPosition.y,
1788                                                                          CharacterHitTest::SCROLL,
1789                                                                          matchedCharacter );
1790
1791       if( leftSelectionHandleEvent )
1792       {
1793         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1794
1795         if( differentHandles || endOfScroll )
1796         {
1797           mEventData->mUpdateHighlightBox = true;
1798           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1799           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1800           mEventData->mLeftSelectionPosition = handlePosition;
1801         }
1802       }
1803       else
1804       {
1805         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1806         if( differentHandles || endOfScroll )
1807         {
1808           mEventData->mUpdateHighlightBox = true;
1809           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1810           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1811           mEventData->mRightSelectionPosition = handlePosition;
1812         }
1813       }
1814
1815       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1816       {
1817         RepositionSelectionHandles();
1818
1819         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1820       }
1821     }
1822     mEventData->mDecoratorUpdated = true;
1823   } // end ( HANDLE_SCROLLING == state )
1824 }
1825
1826 void Controller::Impl::OnSelectEvent( const Event& event )
1827 {
1828   if( NULL == mEventData )
1829   {
1830     // Nothing to do if there is no text.
1831     return;
1832   }
1833
1834   if( mEventData->mSelectionEnabled )
1835   {
1836     // Convert from control's coords to text's coords.
1837     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1838     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1839
1840     // Calculates the logical position from the x,y coords.
1841     RepositionSelectionHandles( xPosition,
1842                                 yPosition,
1843                                 Controller::NoTextTap::HIGHLIGHT );
1844   }
1845 }
1846
1847 void Controller::Impl::OnSelectAllEvent()
1848 {
1849   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1850
1851   if( NULL == mEventData )
1852   {
1853     // Nothing to do if there is no text.
1854     return;
1855   }
1856
1857   if( mEventData->mSelectionEnabled )
1858   {
1859     ChangeState( EventData::SELECTING );
1860
1861     mEventData->mLeftSelectionPosition = 0u;
1862     mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1863
1864     mEventData->mScrollAfterUpdatePosition = true;
1865     mEventData->mUpdateLeftSelectionPosition = true;
1866     mEventData->mUpdateRightSelectionPosition = true;
1867     mEventData->mUpdateHighlightBox = true;
1868   }
1869 }
1870
1871 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1872 {
1873   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1874   {
1875     // Nothing to select if handles are in the same place.
1876     selectedText.clear();
1877     return;
1878   }
1879
1880   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1881
1882   //Get start and end position of selection
1883   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1884   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1885
1886   Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1887   const Length numberOfCharacters = utf32Characters.Count();
1888
1889   // Validate the start and end selection points
1890   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1891   {
1892     //Get text as a UTF8 string
1893     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1894
1895     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1896     {
1897       // Keep a copy of the current input style.
1898       InputStyle currentInputStyle;
1899       currentInputStyle.Copy( mEventData->mInputStyle );
1900
1901       // Set as input style the style of the first deleted character.
1902       mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1903
1904       // Compare if the input style has changed.
1905       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1906
1907       if( hasInputStyleChanged )
1908       {
1909         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1910         // Queue the input style changed signal.
1911         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1912       }
1913
1914       mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1915
1916       // Mark the paragraphs to be updated.
1917       if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1918       {
1919         mTextUpdateInfo.mCharacterIndex = 0;
1920         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1921         mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1922         mTextUpdateInfo.mClearAll = true;
1923       }
1924       else
1925       {
1926         mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1927         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1928       }
1929
1930       // Delete text between handles
1931       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1932       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1933       utf32Characters.Erase( first, last );
1934
1935       // Will show the cursor at the first character of the selection.
1936       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1937     }
1938     else
1939     {
1940       // Will show the cursor at the last character of the selection.
1941       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1942     }
1943
1944     mEventData->mDecoratorUpdated = true;
1945   }
1946 }
1947
1948 void Controller::Impl::ShowClipboard()
1949 {
1950   if( mClipboard )
1951   {
1952     mClipboard.ShowClipboard();
1953   }
1954 }
1955
1956 void Controller::Impl::HideClipboard()
1957 {
1958   if( mClipboard && mClipboardHideEnabled )
1959   {
1960     mClipboard.HideClipboard();
1961   }
1962 }
1963
1964 void Controller::Impl::SetClipboardHideEnable(bool enable)
1965 {
1966   mClipboardHideEnabled = enable;
1967 }
1968
1969 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1970 {
1971   //Send string to clipboard
1972   return ( mClipboard && mClipboard.SetItem( source ) );
1973 }
1974
1975 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1976 {
1977   std::string selectedText;
1978   RetrieveSelection( selectedText, deleteAfterSending );
1979   CopyStringToClipboard( selectedText );
1980   ChangeState( EventData::EDITING );
1981 }
1982
1983 void Controller::Impl::RequestGetTextFromClipboard()
1984 {
1985   if ( mClipboard )
1986   {
1987     mClipboard.RequestItem();
1988   }
1989 }
1990
1991 void Controller::Impl::RepositionSelectionHandles()
1992 {
1993   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1994   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1995
1996   if( selectionStart == selectionEnd )
1997   {
1998     // Nothing to select if handles are in the same place.
1999     // So, deactive Highlight box.
2000     mEventData->mDecorator->SetHighlightActive( false );
2001     return;
2002   }
2003
2004   mEventData->mDecorator->ClearHighlights();
2005
2006   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2007   const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2008   const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2009   const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2010   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2011   const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2012   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2013
2014   const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2015   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2016   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2017
2018   // Swap the indices if the start is greater than the end.
2019   const bool indicesSwapped = selectionStart > selectionEnd;
2020
2021   // Tell the decorator to flip the selection handles if needed.
2022   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2023
2024   if( indicesSwapped )
2025   {
2026     std::swap( selectionStart, selectionEnd );
2027   }
2028
2029   // Get the indices to the first and last selected glyphs.
2030   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2031   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2032   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2033   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2034
2035   // Get the lines where the glyphs are laid-out.
2036   const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2037
2038   LineIndex lineIndex = 0u;
2039   Length numberOfLines = 0u;
2040   mModel->mVisualModel->GetNumberOfLines( glyphStart,
2041                                           1u + glyphEnd - glyphStart,
2042                                           lineIndex,
2043                                           numberOfLines );
2044   const LineIndex firstLineIndex = lineIndex;
2045
2046   // Create the structure to store some selection box info.
2047   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2048   selectionBoxLinesInfo.Resize( numberOfLines );
2049
2050   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2051   selectionBoxInfo->minX = MAX_FLOAT;
2052   selectionBoxInfo->maxX = MIN_FLOAT;
2053
2054   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2055   float minHighlightX = std::numeric_limits<float>::max();
2056   float maxHighlightX = std::numeric_limits<float>::min();
2057   Size highLightSize;
2058   Vector2 highLightPosition; // The highlight position in decorator's coords.
2059
2060   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2061
2062   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2063   selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2064                                                       firstLineIndex );
2065
2066   // Transform to decorator's (control) coords.
2067   selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2068
2069   lineRun += firstLineIndex;
2070
2071   // The line height is the addition of the line ascender and the line descender.
2072   // However, the line descender has a negative value, hence the subtraction.
2073   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2074
2075   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2076
2077   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2078   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2079   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2080
2081   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2082   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2083   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2084
2085   // The number of quads of the selection box.
2086   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2087   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2088
2089   // Count the actual number of quads.
2090   unsigned int actualNumberOfQuads = 0u;
2091   Vector4 quad;
2092
2093   // Traverse the glyphs.
2094   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2095   {
2096     const GlyphInfo& glyph = *( glyphsBuffer + index );
2097     const Vector2& position = *( positionsBuffer + index );
2098
2099     if( splitStartGlyph )
2100     {
2101       // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
2102
2103       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2104       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2105       // Get the direction of the character.
2106       CharacterDirection isCurrentRightToLeft = false;
2107       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2108       {
2109         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2110       }
2111
2112       // The end point could be in the middle of the ligature.
2113       // Calculate the number of characters selected.
2114       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2115
2116       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2117       quad.y = selectionBoxInfo->lineOffset;
2118       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2119       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2120
2121       // Store the min and max 'x' for each line.
2122       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2123       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2124
2125       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2126       ++actualNumberOfQuads;
2127
2128       splitStartGlyph = false;
2129       continue;
2130     }
2131
2132     if( splitEndGlyph && ( index == glyphEnd ) )
2133     {
2134       // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
2135
2136       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2137       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2138       // Get the direction of the character.
2139       CharacterDirection isCurrentRightToLeft = false;
2140       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2141       {
2142         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2143       }
2144
2145       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2146
2147       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2148       quad.y = selectionBoxInfo->lineOffset;
2149       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2150       quad.w = quad.y + selectionBoxInfo->lineHeight;
2151
2152       // Store the min and max 'x' for each line.
2153       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2154       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2155
2156       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2157                                             quad );
2158       ++actualNumberOfQuads;
2159
2160       splitEndGlyph = false;
2161       continue;
2162     }
2163
2164     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2165     quad.y = selectionBoxInfo->lineOffset;
2166     quad.z = quad.x + glyph.advance;
2167     quad.w = quad.y + selectionBoxInfo->lineHeight;
2168
2169     // Store the min and max 'x' for each line.
2170     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2171     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2172
2173     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2174                                           quad );
2175     ++actualNumberOfQuads;
2176
2177     // Whether to retrieve the next line.
2178     if( index == lastGlyphOfLine )
2179     {
2180       ++lineIndex;
2181       if( lineIndex < firstLineIndex + numberOfLines )
2182       {
2183         // Retrieve the next line.
2184         ++lineRun;
2185
2186         // Get the last glyph of the new line.
2187         lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2188
2189         // Keep the offset and height of the current selection box.
2190         const float currentLineOffset = selectionBoxInfo->lineOffset;
2191         const float currentLineHeight = selectionBoxInfo->lineHeight;
2192
2193         // Get the selection box info for the next line.
2194         ++selectionBoxInfo;
2195
2196         selectionBoxInfo->minX = MAX_FLOAT;
2197         selectionBoxInfo->maxX = MIN_FLOAT;
2198
2199         // Update the line's vertical offset.
2200         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2201
2202         // The line height is the addition of the line ascender and the line descender.
2203         // However, the line descender has a negative value, hence the subtraction.
2204         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2205       }
2206     }
2207   }
2208
2209   // Traverses all the lines and updates the min and max 'x' positions and the total height.
2210   // The final width is calculated after 'boxifying' the selection.
2211   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2212          endIt = selectionBoxLinesInfo.End();
2213        it != endIt;
2214        ++it )
2215   {
2216     const SelectionBoxInfo& info = *it;
2217
2218     // Update the size of the highlighted text.
2219     highLightSize.height += info.lineHeight;
2220     minHighlightX = std::min( minHighlightX, info.minX );
2221     maxHighlightX = std::max( maxHighlightX, info.maxX );
2222   }
2223
2224   // Add extra geometry to 'boxify' the selection.
2225
2226   if( 1u < numberOfLines )
2227   {
2228     // Boxify the first line.
2229     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2230     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2231
2232     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2233     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2234
2235     if( boxifyBegin )
2236     {
2237       quad.x = 0.f;
2238       quad.y = firstSelectionBoxLineInfo.lineOffset;
2239       quad.z = firstSelectionBoxLineInfo.minX;
2240       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2241
2242       // Boxify at the beginning of the line.
2243       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2244                                             quad );
2245       ++actualNumberOfQuads;
2246
2247       // Update the size of the highlighted text.
2248       minHighlightX = 0.f;
2249     }
2250
2251     if( boxifyEnd )
2252     {
2253       quad.x = firstSelectionBoxLineInfo.maxX;
2254       quad.y = firstSelectionBoxLineInfo.lineOffset;
2255       quad.z = mModel->mVisualModel->mControlSize.width;
2256       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2257
2258       // Boxify at the end of the line.
2259       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2260                                             quad );
2261       ++actualNumberOfQuads;
2262
2263       // Update the size of the highlighted text.
2264       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2265     }
2266
2267     // Boxify the central lines.
2268     if( 2u < numberOfLines )
2269     {
2270       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2271              endIt = selectionBoxLinesInfo.End() - 1u;
2272            it != endIt;
2273            ++it )
2274       {
2275         const SelectionBoxInfo& info = *it;
2276
2277         quad.x = 0.f;
2278         quad.y = info.lineOffset;
2279         quad.z = info.minX;
2280         quad.w = info.lineOffset + info.lineHeight;
2281
2282         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2283                                               quad );
2284         ++actualNumberOfQuads;
2285
2286         quad.x = info.maxX;
2287         quad.y = info.lineOffset;
2288         quad.z = mModel->mVisualModel->mControlSize.width;
2289         quad.w = info.lineOffset + info.lineHeight;
2290
2291         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2292                                               quad );
2293         ++actualNumberOfQuads;
2294       }
2295
2296       // Update the size of the highlighted text.
2297       minHighlightX = 0.f;
2298       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2299     }
2300
2301     // Boxify the last line.
2302     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2303     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2304
2305     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2306     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2307
2308     if( boxifyBegin )
2309     {
2310       quad.x = 0.f;
2311       quad.y = lastSelectionBoxLineInfo.lineOffset;
2312       quad.z = lastSelectionBoxLineInfo.minX;
2313       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2314
2315       // Boxify at the beginning of the line.
2316       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2317                                             quad );
2318       ++actualNumberOfQuads;
2319
2320       // Update the size of the highlighted text.
2321       minHighlightX = 0.f;
2322     }
2323
2324     if( boxifyEnd )
2325     {
2326       quad.x = lastSelectionBoxLineInfo.maxX;
2327       quad.y = lastSelectionBoxLineInfo.lineOffset;
2328       quad.z = mModel->mVisualModel->mControlSize.width;
2329       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2330
2331       // Boxify at the end of the line.
2332       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2333                                             quad );
2334       ++actualNumberOfQuads;
2335
2336       // Update the size of the highlighted text.
2337       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2338     }
2339   }
2340
2341   // Set the actual number of quads.
2342   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2343
2344   // Sets the highlight's size and position. In decorator's coords.
2345   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2346   highLightSize.width = maxHighlightX - minHighlightX;
2347
2348   highLightPosition.x = minHighlightX;
2349   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2350   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2351
2352   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2353
2354   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2355   {
2356     CursorInfo primaryCursorInfo;
2357     GetCursorPosition( mEventData->mLeftSelectionPosition,
2358                        primaryCursorInfo );
2359
2360     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2361
2362     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2363                                          primaryPosition.x,
2364                                          primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2365                                          primaryCursorInfo.lineHeight );
2366
2367     CursorInfo secondaryCursorInfo;
2368     GetCursorPosition( mEventData->mRightSelectionPosition,
2369                        secondaryCursorInfo );
2370
2371     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2372
2373     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2374                                          secondaryPosition.x,
2375                                          secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2376                                          secondaryCursorInfo.lineHeight );
2377   }
2378
2379   // Set the flag to update the decorator.
2380   mEventData->mDecoratorUpdated = true;
2381 }
2382
2383 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2384 {
2385   if( NULL == mEventData )
2386   {
2387     // Nothing to do if there is no text input.
2388     return;
2389   }
2390
2391   if( IsShowingPlaceholderText() )
2392   {
2393     // Nothing to do if there is the place-holder text.
2394     return;
2395   }
2396
2397   const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2398   const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
2399   if( ( 0 == numberOfGlyphs ) ||
2400       ( 0 == numberOfLines ) )
2401   {
2402     // Nothing to do if there is no text.
2403     return;
2404   }
2405
2406   // Find which word was selected
2407   CharacterIndex selectionStart( 0 );
2408   CharacterIndex selectionEnd( 0 );
2409   CharacterIndex noTextHitIndex( 0 );
2410   const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2411                                                   mModel->mLogicalModel,
2412                                                   mMetrics,
2413                                                   visualX,
2414                                                   visualY,
2415                                                   selectionStart,
2416                                                   selectionEnd,
2417                                                   noTextHitIndex );
2418   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2419
2420   if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2421   {
2422     ChangeState( EventData::SELECTING );
2423
2424     mEventData->mLeftSelectionPosition = selectionStart;
2425     mEventData->mRightSelectionPosition = selectionEnd;
2426
2427     mEventData->mUpdateLeftSelectionPosition = true;
2428     mEventData->mUpdateRightSelectionPosition = true;
2429     mEventData->mUpdateHighlightBox = true;
2430
2431     // It may happen an IMF commit event arrives before the selection event
2432     // if the IMF manager is in pre-edit state. The commit event will set the
2433     // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2434     // to false, the highlight box won't be updated.
2435     mEventData->mUpdateCursorPosition = false;
2436
2437     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2438
2439     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2440     mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2441   }
2442   else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2443   {
2444     // Nothing to select. i.e. a white space, out of bounds
2445     ChangeState( EventData::EDITING_WITH_POPUP );
2446
2447     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2448
2449     mEventData->mUpdateCursorPosition = true;
2450     mEventData->mUpdateGrabHandlePosition = true;
2451     mEventData->mScrollAfterUpdatePosition = true;
2452     mEventData->mUpdateInputStyle = true;
2453   }
2454   else if( Controller::NoTextTap::NO_ACTION == action )
2455   {
2456     // Nothing to select. i.e. a white space, out of bounds
2457     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2458
2459     mEventData->mUpdateCursorPosition = true;
2460     mEventData->mUpdateGrabHandlePosition = true;
2461     mEventData->mScrollAfterUpdatePosition = true;
2462     mEventData->mUpdateInputStyle = true;
2463   }
2464 }
2465
2466 void Controller::Impl::SetPopupButtons()
2467 {
2468   /**
2469    *  Sets the Popup buttons to be shown depending on State.
2470    *
2471    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2472    *
2473    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2474    */
2475
2476   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2477
2478   if( EventData::SELECTING == mEventData->mState )
2479   {
2480     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2481
2482     if( !IsClipboardEmpty() )
2483     {
2484       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2485       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2486     }
2487
2488     if( !mEventData->mAllTextSelected )
2489     {
2490       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2491     }
2492   }
2493   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2494   {
2495     if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2496     {
2497       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2498     }
2499
2500     if( !IsClipboardEmpty() )
2501     {
2502       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2503       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2504     }
2505   }
2506   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2507   {
2508     if ( !IsClipboardEmpty() )
2509     {
2510       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2511       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2512     }
2513   }
2514
2515   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2516 }
2517
2518 void Controller::Impl::ChangeState( EventData::State newState )
2519 {
2520   if( NULL == mEventData )
2521   {
2522     // Nothing to do if there is no text input.
2523     return;
2524   }
2525
2526   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2527
2528   if( mEventData->mState != newState )
2529   {
2530     mEventData->mPreviousState = mEventData->mState;
2531     mEventData->mState = newState;
2532
2533     switch( mEventData->mState )
2534     {
2535       case EventData::INACTIVE:
2536       {
2537         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2538         mEventData->mDecorator->StopCursorBlink();
2539         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2540         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2541         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2542         mEventData->mDecorator->SetHighlightActive( false );
2543         mEventData->mDecorator->SetPopupActive( false );
2544         mEventData->mDecoratorUpdated = true;
2545         break;
2546       }
2547       case EventData::INTERRUPTED:
2548       {
2549         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2550         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2551         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2552         mEventData->mDecorator->SetHighlightActive( false );
2553         mEventData->mDecorator->SetPopupActive( false );
2554         mEventData->mDecoratorUpdated = true;
2555         break;
2556       }
2557       case EventData::SELECTING:
2558       {
2559         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2560         mEventData->mDecorator->StopCursorBlink();
2561         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2562         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2563         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2564         mEventData->mDecorator->SetHighlightActive( true );
2565         if( mEventData->mGrabHandlePopupEnabled )
2566         {
2567           SetPopupButtons();
2568           mEventData->mDecorator->SetPopupActive( true );
2569         }
2570         mEventData->mDecoratorUpdated = true;
2571         break;
2572       }
2573       case EventData::EDITING:
2574       {
2575         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2576         if( mEventData->mCursorBlinkEnabled )
2577         {
2578           mEventData->mDecorator->StartCursorBlink();
2579         }
2580         // Grab handle is not shown until a tap is received whilst EDITING
2581         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2582         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2583         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2584         mEventData->mDecorator->SetHighlightActive( false );
2585         if( mEventData->mGrabHandlePopupEnabled )
2586         {
2587           mEventData->mDecorator->SetPopupActive( false );
2588         }
2589         mEventData->mDecoratorUpdated = true;
2590         break;
2591       }
2592       case EventData::EDITING_WITH_POPUP:
2593       {
2594         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2595
2596         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2597         if( mEventData->mCursorBlinkEnabled )
2598         {
2599           mEventData->mDecorator->StartCursorBlink();
2600         }
2601         if( mEventData->mSelectionEnabled )
2602         {
2603           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2604           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2605           mEventData->mDecorator->SetHighlightActive( false );
2606         }
2607         else
2608         {
2609           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2610         }
2611         if( mEventData->mGrabHandlePopupEnabled )
2612         {
2613           SetPopupButtons();
2614           mEventData->mDecorator->SetPopupActive( true );
2615         }
2616         mEventData->mDecoratorUpdated = true;
2617         break;
2618       }
2619       case EventData::EDITING_WITH_GRAB_HANDLE:
2620       {
2621         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2622
2623         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2624         if( mEventData->mCursorBlinkEnabled )
2625         {
2626           mEventData->mDecorator->StartCursorBlink();
2627         }
2628         // Grab handle is not shown until a tap is received whilst EDITING
2629         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2630         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2631         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2632         mEventData->mDecorator->SetHighlightActive( false );
2633         if( mEventData->mGrabHandlePopupEnabled )
2634         {
2635           mEventData->mDecorator->SetPopupActive( false );
2636         }
2637         mEventData->mDecoratorUpdated = true;
2638         break;
2639       }
2640       case EventData::SELECTION_HANDLE_PANNING:
2641       {
2642         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2643         mEventData->mDecorator->StopCursorBlink();
2644         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2645         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2646         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2647         mEventData->mDecorator->SetHighlightActive( true );
2648         if( mEventData->mGrabHandlePopupEnabled )
2649         {
2650           mEventData->mDecorator->SetPopupActive( false );
2651         }
2652         mEventData->mDecoratorUpdated = true;
2653         break;
2654       }
2655       case EventData::GRAB_HANDLE_PANNING:
2656       {
2657         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2658
2659         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2660         if( mEventData->mCursorBlinkEnabled )
2661         {
2662           mEventData->mDecorator->StartCursorBlink();
2663         }
2664         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2665         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2666         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2667         mEventData->mDecorator->SetHighlightActive( false );
2668         if( mEventData->mGrabHandlePopupEnabled )
2669         {
2670           mEventData->mDecorator->SetPopupActive( false );
2671         }
2672         mEventData->mDecoratorUpdated = true;
2673         break;
2674       }
2675       case EventData::EDITING_WITH_PASTE_POPUP:
2676       {
2677         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2678
2679         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2680         if( mEventData->mCursorBlinkEnabled )
2681         {
2682           mEventData->mDecorator->StartCursorBlink();
2683         }
2684
2685         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2686         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2687         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2688         mEventData->mDecorator->SetHighlightActive( false );
2689
2690         if( mEventData->mGrabHandlePopupEnabled )
2691         {
2692           SetPopupButtons();
2693           mEventData->mDecorator->SetPopupActive( true );
2694         }
2695         mEventData->mDecoratorUpdated = true;
2696         break;
2697       }
2698       case EventData::TEXT_PANNING:
2699       {
2700         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2701         mEventData->mDecorator->StopCursorBlink();
2702         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2703         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2704             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2705         {
2706           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2707           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2708           mEventData->mDecorator->SetHighlightActive( true );
2709         }
2710
2711         if( mEventData->mGrabHandlePopupEnabled )
2712         {
2713           mEventData->mDecorator->SetPopupActive( false );
2714         }
2715
2716         mEventData->mDecoratorUpdated = true;
2717         break;
2718       }
2719     }
2720   }
2721 }
2722
2723 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2724                                           CursorInfo& cursorInfo )
2725 {
2726   if( !IsShowingRealText() )
2727   {
2728     // Do not want to use the place-holder text to set the cursor position.
2729
2730     // Use the line's height of the font's family set to set the cursor's size.
2731     // If there is no font's family set, use the default font.
2732     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2733
2734     cursorInfo.lineOffset = 0.f;
2735     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2736     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2737
2738     switch( mModel->mHorizontalAlignment )
2739     {
2740       case Text::HorizontalAlignment::BEGIN :
2741       {
2742         cursorInfo.primaryPosition.x = 0.f;
2743         break;
2744       }
2745       case Text::HorizontalAlignment::CENTER:
2746       {
2747         cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2748         break;
2749       }
2750       case Text::HorizontalAlignment::END:
2751       {
2752         cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2753         break;
2754       }
2755     }
2756
2757     // Nothing else to do.
2758     return;
2759   }
2760
2761   const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2762   GetCursorPositionParameters parameters;
2763   parameters.visualModel = mModel->mVisualModel;
2764   parameters.logicalModel = mModel->mLogicalModel;
2765   parameters.metrics = mMetrics;
2766   parameters.logical = logical;
2767   parameters.isMultiline = isMultiLine;
2768
2769   Text::GetCursorPosition( parameters,
2770                            cursorInfo );
2771
2772   // Adds Outline offset.
2773   const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2774   cursorInfo.primaryPosition.x += outlineWidth;
2775   cursorInfo.primaryPosition.y += outlineWidth;
2776   cursorInfo.secondaryPosition.x += outlineWidth;
2777   cursorInfo.secondaryPosition.y += outlineWidth;
2778
2779   if( isMultiLine )
2780   {
2781     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2782
2783     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2784     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2785
2786     if( 0.f > cursorInfo.primaryPosition.x )
2787     {
2788       cursorInfo.primaryPosition.x = 0.f;
2789     }
2790
2791     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2792     if( cursorInfo.primaryPosition.x > edgeWidth )
2793     {
2794       cursorInfo.primaryPosition.x = edgeWidth;
2795     }
2796   }
2797 }
2798
2799 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2800 {
2801   if( NULL == mEventData )
2802   {
2803     // Nothing to do if there is no text input.
2804     return 0u;
2805   }
2806
2807   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2808
2809   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2810   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2811
2812   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2813   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2814
2815   if( numberOfCharacters > 1u )
2816   {
2817     const Script script = mModel->mLogicalModel->GetScript( index );
2818     if( HasLigatureMustBreak( script ) )
2819     {
2820       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2821       numberOfCharacters = 1u;
2822     }
2823   }
2824   else
2825   {
2826     while( 0u == numberOfCharacters )
2827     {
2828       ++glyphIndex;
2829       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2830     }
2831   }
2832
2833   if( index < mEventData->mPrimaryCursorPosition )
2834   {
2835     cursorIndex -= numberOfCharacters;
2836   }
2837   else
2838   {
2839     cursorIndex += numberOfCharacters;
2840   }
2841
2842   // Will update the cursor hook position.
2843   mEventData->mUpdateCursorHookPosition = true;
2844
2845   return cursorIndex;
2846 }
2847
2848 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2849 {
2850   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2851   if( NULL == mEventData )
2852   {
2853     // Nothing to do if there is no text input.
2854     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2855     return;
2856   }
2857
2858   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2859
2860   mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2861
2862   // Sets the cursor position.
2863   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2864                                        cursorPosition.x,
2865                                        cursorPosition.y,
2866                                        cursorInfo.primaryCursorHeight,
2867                                        cursorInfo.lineHeight );
2868   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2869
2870   if( mEventData->mUpdateGrabHandlePosition )
2871   {
2872     // Sets the grab handle position.
2873     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2874                                          cursorPosition.x,
2875                                          cursorInfo.lineOffset + mModel->mScrollPosition.y,
2876                                          cursorInfo.lineHeight );
2877   }
2878
2879   if( cursorInfo.isSecondaryCursor )
2880   {
2881     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2882                                          cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2883                                          cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2884                                          cursorInfo.secondaryCursorHeight,
2885                                          cursorInfo.lineHeight );
2886     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2887   }
2888
2889   // Set which cursors are active according the state.
2890   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2891   {
2892     if( cursorInfo.isSecondaryCursor )
2893     {
2894       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2895     }
2896     else
2897     {
2898       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2899     }
2900   }
2901   else
2902   {
2903     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2904   }
2905
2906   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2907 }
2908
2909 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2910                                               const CursorInfo& cursorInfo )
2911 {
2912   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2913       ( RIGHT_SELECTION_HANDLE != handleType ) )
2914   {
2915     return;
2916   }
2917
2918   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2919
2920   // Sets the handle's position.
2921   mEventData->mDecorator->SetPosition( handleType,
2922                                        cursorPosition.x,
2923                                        cursorInfo.lineOffset + mModel->mScrollPosition.y,
2924                                        cursorInfo.lineHeight );
2925
2926   // If selection handle at start of the text and other at end of the text then all text is selected.
2927   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2928   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2929   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2930 }
2931
2932 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2933 {
2934   // Clamp between -space & -alignment offset.
2935
2936   if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2937   {
2938     const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2939     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2940     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2941
2942     mEventData->mDecoratorUpdated = true;
2943   }
2944   else
2945   {
2946     mModel->mScrollPosition.x = 0.f;
2947   }
2948 }
2949
2950 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2951 {
2952   if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2953   {
2954     // Nothing to do if the text is single line.
2955     return;
2956   }
2957
2958   // Clamp between -space & 0.
2959   if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2960   {
2961     const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2962     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2963     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2964
2965     mEventData->mDecoratorUpdated = true;
2966   }
2967   else
2968   {
2969     mModel->mScrollPosition.y = 0.f;
2970   }
2971 }
2972
2973 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2974 {
2975   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2976
2977   // position is in actor's coords.
2978   const float positionEndX = position.x + cursorWidth;
2979   const float positionEndY = position.y + lineHeight;
2980
2981   // Transform the position to decorator coords.
2982   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2983   const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2984
2985   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2986   const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2987
2988   if( decoratorPositionBeginX < 0.f )
2989   {
2990     mModel->mScrollPosition.x = -position.x;
2991   }
2992   else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2993   {
2994     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2995   }
2996
2997   if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2998   {
2999     if( decoratorPositionBeginY < 0.f )
3000     {
3001       mModel->mScrollPosition.y = -position.y;
3002     }
3003     else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3004     {
3005       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3006     }
3007   }
3008 }
3009
3010 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3011 {
3012   // Get the current cursor position in decorator coords.
3013   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3014
3015   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition  );
3016
3017
3018
3019   // Calculate the offset to match the cursor position before the character was deleted.
3020   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3021
3022   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3023   if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3024   {
3025     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3026     mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3027   }
3028
3029
3030   ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3031   ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3032
3033   // Makes the new cursor position visible if needed.
3034   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3035 }
3036
3037 void Controller::Impl::RequestRelayout()
3038 {
3039   if( NULL != mControlInterface )
3040   {
3041     mControlInterface->RequestTextRelayout();
3042   }
3043 }
3044
3045 } // namespace Text
3046
3047 } // namespace Toolkit
3048
3049 } // namespace Dali