[4.0] Fix the cursor position after text selecting using tap
[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   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
826
827   if( mTextUpdateInfo.mClearAll ||
828       ( 0u != paragraphCharacters ) )
829   {
830     ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
831   }
832
833   mTextUpdateInfo.mClearAll = false;
834
835   // Whether the model is updated.
836   bool updated = false;
837
838   Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
839   const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
840
841   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
842   {
843     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
844     // calculate the bidirectional info for each 'paragraph'.
845     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
846     // is not shaped together).
847     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
848
849     SetLineBreakInfo( utf32Characters,
850                       startIndex,
851                       requestedNumberOfCharacters,
852                       lineBreakInfo );
853
854     // Create the paragraph info.
855     mModel->mLogicalModel->CreateParagraphInfo( startIndex,
856                                                 requestedNumberOfCharacters );
857     updated = true;
858   }
859
860   Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
861   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
862   {
863     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
864     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
865
866     SetWordBreakInfo( utf32Characters,
867                       startIndex,
868                       requestedNumberOfCharacters,
869                       wordBreakInfo );
870     updated = true;
871   }
872
873   const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
874   const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
875
876   Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
877   Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
878
879   if( getScripts || validateFonts )
880   {
881     // Validates the fonts assigned by the application or assigns default ones.
882     // It makes sure all the characters are going to be rendered by the correct font.
883     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
884
885     if( getScripts )
886     {
887       // Retrieves the scripts used in the text.
888       multilanguageSupport.SetScripts( utf32Characters,
889                                        startIndex,
890                                        requestedNumberOfCharacters,
891                                        scripts );
892     }
893
894     if( validateFonts )
895     {
896       // Validate the fonts set through the mark-up string.
897       Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
898
899       // Get the default font's description.
900       TextAbstraction::FontDescription defaultFontDescription;
901       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
902
903       if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
904       {
905         // If the placeholder font is set specifically, only placeholder font is changed.
906         defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
907         if( mEventData->mPlaceholderFont->sizeDefined )
908         {
909           defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
910         }
911       }
912       else if( NULL != mFontDefaults )
913       {
914         // Set the normal font and the placeholder font.
915         defaultFontDescription = mFontDefaults->mFontDescription;
916         defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
917       }
918
919       // Validates the fonts. If there is a character with no assigned font it sets a default one.
920       // After this call, fonts are validated.
921       multilanguageSupport.ValidateFonts( utf32Characters,
922                                           scripts,
923                                           fontDescriptionRuns,
924                                           defaultFontDescription,
925                                           defaultPointSize,
926                                           startIndex,
927                                           requestedNumberOfCharacters,
928                                           validFonts );
929     }
930     updated = true;
931   }
932
933   Vector<Character> mirroredUtf32Characters;
934   bool textMirrored = false;
935   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
936   if( NO_OPERATION != ( BIDI_INFO & operations ) )
937   {
938     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
939     bidirectionalInfo.Reserve( numberOfParagraphs );
940
941     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
942     SetBidirectionalInfo( utf32Characters,
943                           scripts,
944                           lineBreakInfo,
945                           startIndex,
946                           requestedNumberOfCharacters,
947                           bidirectionalInfo );
948
949     if( 0u != bidirectionalInfo.Count() )
950     {
951       // Only set the character directions if there is right to left characters.
952       Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
953       GetCharactersDirection( bidirectionalInfo,
954                               numberOfCharacters,
955                               startIndex,
956                               requestedNumberOfCharacters,
957                               directions );
958
959       // This paragraph has right to left text. Some characters may need to be mirrored.
960       // TODO: consider if the mirrored string can be stored as well.
961
962       textMirrored = GetMirroredText( utf32Characters,
963                                       directions,
964                                       bidirectionalInfo,
965                                       startIndex,
966                                       requestedNumberOfCharacters,
967                                       mirroredUtf32Characters );
968     }
969     else
970     {
971       // There is no right to left characters. Clear the directions vector.
972       mModel->mLogicalModel->mCharacterDirections.Clear();
973     }
974     updated = true;
975   }
976
977   Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
978   Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
979   Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
980   Vector<GlyphIndex> newParagraphGlyphs;
981   newParagraphGlyphs.Reserve( numberOfParagraphs );
982
983   const Length currentNumberOfGlyphs = glyphs.Count();
984   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
985   {
986     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
987     // Shapes the text.
988     ShapeText( textToShape,
989                lineBreakInfo,
990                scripts,
991                validFonts,
992                startIndex,
993                mTextUpdateInfo.mStartGlyphIndex,
994                requestedNumberOfCharacters,
995                glyphs,
996                glyphsToCharactersMap,
997                charactersPerGlyph,
998                newParagraphGlyphs );
999
1000     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1001     mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1002     mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1003     updated = true;
1004   }
1005
1006   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1007
1008   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1009   {
1010     GlyphInfo* glyphsBuffer = glyphs.Begin();
1011     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1012
1013     // Update the width and advance of all new paragraph characters.
1014     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1015     {
1016       const GlyphIndex index = *it;
1017       GlyphInfo& glyph = *( glyphsBuffer + index );
1018
1019       glyph.xBearing = 0.f;
1020       glyph.width = 0.f;
1021       glyph.advance = 0.f;
1022     }
1023     updated = true;
1024   }
1025
1026   if( NO_OPERATION != ( COLOR & operations ) )
1027   {
1028     // Set the color runs in glyphs.
1029     SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1030                               mModel->mVisualModel->mCharactersToGlyph,
1031                               mModel->mVisualModel->mGlyphsPerCharacter,
1032                               startIndex,
1033                               mTextUpdateInfo.mStartGlyphIndex,
1034                               requestedNumberOfCharacters,
1035                               mModel->mVisualModel->mColors,
1036                               mModel->mVisualModel->mColorIndices );
1037
1038     updated = true;
1039   }
1040
1041   if( ( NULL != mEventData ) &&
1042       mEventData->mPreEditFlag &&
1043       ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1044   {
1045     // Add the underline for the pre-edit text.
1046     const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1047     const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1048
1049     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1050     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1051     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1052     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1053
1054     GlyphRun underlineRun;
1055     underlineRun.glyphIndex = glyphStart;
1056     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1057
1058     // TODO: At the moment the underline runs are only for pre-edit.
1059     mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1060   }
1061
1062   // The estimated number of lines. Used to avoid reallocations when layouting.
1063   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1064
1065   // Set the previous number of characters for the next time the text is updated.
1066   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1067
1068   return updated;
1069 }
1070
1071 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1072 {
1073   // Sets the default text's color.
1074   inputStyle.textColor = mTextColor;
1075   inputStyle.isDefaultColor = true;
1076
1077   inputStyle.familyName.clear();
1078   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1079   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1080   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1081   inputStyle.size = 0.f;
1082
1083   inputStyle.lineSpacing = 0.f;
1084
1085   inputStyle.underlineProperties.clear();
1086   inputStyle.shadowProperties.clear();
1087   inputStyle.embossProperties.clear();
1088   inputStyle.outlineProperties.clear();
1089
1090   inputStyle.isFamilyDefined = false;
1091   inputStyle.isWeightDefined = false;
1092   inputStyle.isWidthDefined = false;
1093   inputStyle.isSlantDefined = false;
1094   inputStyle.isSizeDefined = false;
1095
1096   inputStyle.isLineSpacingDefined = false;
1097
1098   inputStyle.isUnderlineDefined = false;
1099   inputStyle.isShadowDefined = false;
1100   inputStyle.isEmbossDefined = false;
1101   inputStyle.isOutlineDefined = false;
1102
1103   // Sets the default font's family name, weight, width, slant and size.
1104   if( mFontDefaults )
1105   {
1106     if( mFontDefaults->familyDefined )
1107     {
1108       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1109       inputStyle.isFamilyDefined = true;
1110     }
1111
1112     if( mFontDefaults->weightDefined )
1113     {
1114       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1115       inputStyle.isWeightDefined = true;
1116     }
1117
1118     if( mFontDefaults->widthDefined )
1119     {
1120       inputStyle.width = mFontDefaults->mFontDescription.width;
1121       inputStyle.isWidthDefined = true;
1122     }
1123
1124     if( mFontDefaults->slantDefined )
1125     {
1126       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1127       inputStyle.isSlantDefined = true;
1128     }
1129
1130     if( mFontDefaults->sizeDefined )
1131     {
1132       inputStyle.size = mFontDefaults->mDefaultPointSize;
1133       inputStyle.isSizeDefined = true;
1134     }
1135   }
1136 }
1137
1138 float Controller::Impl::GetDefaultFontLineHeight()
1139 {
1140   FontId defaultFontId = 0u;
1141   if( NULL == mFontDefaults )
1142   {
1143     TextAbstraction::FontDescription fontDescription;
1144     defaultFontId = mFontClient.GetFontId( fontDescription );
1145   }
1146   else
1147   {
1148     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1149   }
1150
1151   Text::FontMetrics fontMetrics;
1152   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1153
1154   return( fontMetrics.ascender - fontMetrics.descender );
1155 }
1156
1157 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1158 {
1159   if( NULL == mEventData )
1160   {
1161     // Nothing to do if there is no text input.
1162     return;
1163   }
1164
1165   int keyCode = event.p1.mInt;
1166   bool isShiftModifier = event.p2.mBool;
1167
1168   CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1169
1170   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1171   {
1172     if( mEventData->mPrimaryCursorPosition > 0u )
1173     {
1174       if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1175       {
1176         mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1177       }
1178       else
1179       {
1180         mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1181       }
1182     }
1183   }
1184   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1185   {
1186     if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1187     {
1188       if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1189       {
1190         mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1191       }
1192       else
1193       {
1194         mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1195       }
1196     }
1197   }
1198   else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1199   {
1200     // Ignore Shift-Up for text selection for now.
1201
1202     // Get first the line index of the current cursor position index.
1203     CharacterIndex characterIndex = 0u;
1204
1205     if( mEventData->mPrimaryCursorPosition > 0u )
1206     {
1207       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1208     }
1209
1210     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1211     const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1212
1213     // Retrieve the cursor position info.
1214     CursorInfo cursorInfo;
1215     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1216                        cursorInfo );
1217
1218     // Get the line above.
1219     const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1220
1221     // Get the next hit 'y' point.
1222     const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1223
1224     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1225     bool matchedCharacter = false;
1226     mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1227                                                                       mModel->mLogicalModel,
1228                                                                       mMetrics,
1229                                                                       mEventData->mCursorHookPositionX,
1230                                                                       hitPointY,
1231                                                                       CharacterHitTest::TAP,
1232                                                                       matchedCharacter );
1233   }
1234   else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1235   {
1236     // Ignore Shift-Down for text selection for now.
1237
1238     // Get first the line index of the current cursor position index.
1239     CharacterIndex characterIndex = 0u;
1240
1241     if( mEventData->mPrimaryCursorPosition > 0u )
1242     {
1243       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1244     }
1245
1246     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1247
1248     if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1249     {
1250       // Retrieve the cursor position info.
1251       CursorInfo cursorInfo;
1252       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1253                          cursorInfo );
1254
1255       // Get the line below.
1256       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1257
1258       // Get the next hit 'y' point.
1259       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1260
1261       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1262       bool matchedCharacter = false;
1263       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1264                                                                         mModel->mLogicalModel,
1265                                                                         mMetrics,
1266                                                                         mEventData->mCursorHookPositionX,
1267                                                                         hitPointY,
1268                                                                         CharacterHitTest::TAP,
1269                                                                         matchedCharacter );
1270     }
1271   }
1272
1273   if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1274   {
1275     // Update selection position after moving the cursor
1276     mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1277     mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1278   }
1279
1280   if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1281   {
1282     // Handle text selection
1283     bool selecting = false;
1284
1285     if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1286     {
1287       // Shift-Left/Right to select the text
1288       int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1289       if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1290       {
1291         mEventData->mRightSelectionPosition += cursorPositionDelta;
1292       }
1293       selecting = true;
1294     }
1295     else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1296     {
1297       // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1298       selecting = true;
1299     }
1300
1301     if ( selecting )
1302     {
1303       // Notify the cursor position to the imf manager.
1304       if( mEventData->mImfManager )
1305       {
1306         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1307         mEventData->mImfManager.NotifyCursorPosition();
1308       }
1309
1310       ChangeState( EventData::SELECTING );
1311
1312       mEventData->mUpdateLeftSelectionPosition = true;
1313       mEventData->mUpdateRightSelectionPosition = true;
1314       mEventData->mUpdateGrabHandlePosition = true;
1315       mEventData->mUpdateHighlightBox = true;
1316
1317       // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1318       if( mEventData->mGrabHandlePopupEnabled )
1319       {
1320         mEventData->mDecorator->SetPopupActive( false );
1321       }
1322     }
1323   }
1324   else
1325   {
1326     // Handle normal cursor move
1327     ChangeState( EventData::EDITING );
1328     mEventData->mUpdateCursorPosition = true;
1329   }
1330
1331   mEventData->mUpdateInputStyle = true;
1332   mEventData->mScrollAfterUpdatePosition = true;
1333 }
1334
1335 void Controller::Impl::OnTapEvent( const Event& event )
1336 {
1337   if( NULL != mEventData )
1338   {
1339     const unsigned int tapCount = event.p1.mUint;
1340
1341     if( 1u == tapCount )
1342     {
1343       if( IsShowingRealText() )
1344       {
1345         // Convert from control's coords to text's coords.
1346         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1347         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1348
1349         // Keep the tap 'x' position. Used to move the cursor.
1350         mEventData->mCursorHookPositionX = xPosition;
1351
1352         // Whether to touch point hits on a glyph.
1353         bool matchedCharacter = false;
1354         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1355                                                                           mModel->mLogicalModel,
1356                                                                           mMetrics,
1357                                                                           xPosition,
1358                                                                           yPosition,
1359                                                                           CharacterHitTest::TAP,
1360                                                                           matchedCharacter );
1361
1362         // When the cursor position is changing, delay cursor blinking
1363         mEventData->mDecorator->DelayCursorBlink();
1364       }
1365       else
1366       {
1367         mEventData->mPrimaryCursorPosition = 0u;
1368       }
1369
1370       // Update selection position after tapping
1371       mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1372       mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1373
1374       mEventData->mUpdateCursorPosition = true;
1375       mEventData->mUpdateGrabHandlePosition = true;
1376       mEventData->mScrollAfterUpdatePosition = true;
1377       mEventData->mUpdateInputStyle = true;
1378
1379       // Notify the cursor position to the imf manager.
1380       if( mEventData->mImfManager )
1381       {
1382         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1383         mEventData->mImfManager.NotifyCursorPosition();
1384       }
1385     }
1386     else if( 2u == tapCount )
1387     {
1388       if( mEventData->mSelectionEnabled )
1389       {
1390         // Convert from control's coords to text's coords.
1391         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1392         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1393
1394         // Calculates the logical position from the x,y coords.
1395         RepositionSelectionHandles( xPosition,
1396                                     yPosition,
1397                                     mEventData->mDoubleTapAction );
1398       }
1399     }
1400   }
1401 }
1402
1403 void Controller::Impl::OnPanEvent( const Event& event )
1404 {
1405   if( NULL == mEventData )
1406   {
1407     // Nothing to do if there is no text input.
1408     return;
1409   }
1410
1411   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1412   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1413
1414   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1415   {
1416     // Nothing to do if scrolling is not enabled.
1417     return;
1418   }
1419
1420   const int state = event.p1.mInt;
1421
1422   switch( state )
1423   {
1424     case Gesture::Started:
1425     {
1426       // Will remove the cursor, handles or text's popup, ...
1427       ChangeState( EventData::TEXT_PANNING );
1428       break;
1429     }
1430     case Gesture::Continuing:
1431     {
1432       const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1433       const Vector2 currentScroll = mModel->mScrollPosition;
1434
1435       if( isHorizontalScrollEnabled )
1436       {
1437         const float displacementX = event.p2.mFloat;
1438         mModel->mScrollPosition.x += displacementX;
1439
1440         ClampHorizontalScroll( layoutSize );
1441       }
1442
1443       if( isVerticalScrollEnabled )
1444       {
1445         const float displacementY = event.p3.mFloat;
1446         mModel->mScrollPosition.y += displacementY;
1447
1448         ClampVerticalScroll( layoutSize );
1449       }
1450
1451       mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1452       break;
1453     }
1454     case Gesture::Finished:
1455     case Gesture::Cancelled: // FALLTHROUGH
1456     {
1457       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1458       ChangeState( mEventData->mPreviousState );
1459       break;
1460     }
1461     default:
1462       break;
1463   }
1464 }
1465
1466 void Controller::Impl::OnLongPressEvent( const Event& event )
1467 {
1468   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1469
1470   if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1471   {
1472     ChangeState( EventData::EDITING_WITH_POPUP );
1473     mEventData->mDecoratorUpdated = true;
1474     mEventData->mUpdateInputStyle = true;
1475   }
1476   else
1477   {
1478     if( mEventData->mSelectionEnabled )
1479     {
1480       // Convert from control's coords to text's coords.
1481       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1482       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1483
1484       // Calculates the logical position from the x,y coords.
1485       RepositionSelectionHandles( xPosition,
1486                                   yPosition,
1487                                   mEventData->mLongPressAction );
1488     }
1489   }
1490 }
1491
1492 void Controller::Impl::OnHandleEvent( const Event& event )
1493 {
1494   if( NULL == mEventData )
1495   {
1496     // Nothing to do if there is no text input.
1497     return;
1498   }
1499
1500   const unsigned int state = event.p1.mUint;
1501   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1502   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1503
1504   if( HANDLE_PRESSED == state )
1505   {
1506     // Convert from decorator's coords to text's coords.
1507     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1508     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1509
1510     // Need to calculate the handle's new position.
1511     bool matchedCharacter = false;
1512     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1513                                                                           mModel->mLogicalModel,
1514                                                                           mMetrics,
1515                                                                           xPosition,
1516                                                                           yPosition,
1517                                                                           CharacterHitTest::SCROLL,
1518                                                                           matchedCharacter );
1519
1520     if( Event::GRAB_HANDLE_EVENT == event.type )
1521     {
1522       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1523
1524       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1525       {
1526         // Updates the cursor position if the handle's new position is different than the current one.
1527         mEventData->mUpdateCursorPosition = true;
1528         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1529         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1530         mEventData->mPrimaryCursorPosition = handleNewPosition;
1531       }
1532
1533       // 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.
1534       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1535     }
1536     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1537     {
1538       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1539
1540       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1541           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1542       {
1543         // Updates the highlight box if the handle's new position is different than the current one.
1544         mEventData->mUpdateHighlightBox = true;
1545         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1546         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1547         mEventData->mLeftSelectionPosition = handleNewPosition;
1548       }
1549
1550       // 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.
1551       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1552
1553       // Will define the order to scroll the text to match the handle position.
1554       mEventData->mIsLeftHandleSelected = true;
1555       mEventData->mIsRightHandleSelected = false;
1556     }
1557     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1558     {
1559       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1560
1561       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1562           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1563       {
1564         // Updates the highlight box if the handle's new position is different than the current one.
1565         mEventData->mUpdateHighlightBox = true;
1566         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1567         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1568         mEventData->mRightSelectionPosition = handleNewPosition;
1569       }
1570
1571       // 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.
1572       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1573
1574       // Will define the order to scroll the text to match the handle position.
1575       mEventData->mIsLeftHandleSelected = false;
1576       mEventData->mIsRightHandleSelected = true;
1577     }
1578   } // end ( HANDLE_PRESSED == state )
1579   else if( ( HANDLE_RELEASED == state ) ||
1580            handleStopScrolling )
1581   {
1582     CharacterIndex handlePosition = 0u;
1583     if( handleStopScrolling || isSmoothHandlePanEnabled )
1584     {
1585       // Convert from decorator's coords to text's coords.
1586       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1587       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1588
1589       bool matchedCharacter = false;
1590       handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1591                                                     mModel->mLogicalModel,
1592                                                     mMetrics,
1593                                                     xPosition,
1594                                                     yPosition,
1595                                                     CharacterHitTest::SCROLL,
1596                                                     matchedCharacter );
1597     }
1598
1599     if( Event::GRAB_HANDLE_EVENT == event.type )
1600     {
1601       mEventData->mUpdateCursorPosition = true;
1602       mEventData->mUpdateGrabHandlePosition = true;
1603       mEventData->mUpdateInputStyle = true;
1604
1605       if( !IsClipboardEmpty() )
1606       {
1607         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1608       }
1609
1610       if( handleStopScrolling || isSmoothHandlePanEnabled )
1611       {
1612         mEventData->mScrollAfterUpdatePosition = true;
1613         mEventData->mPrimaryCursorPosition = handlePosition;
1614       }
1615     }
1616     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1617     {
1618       ChangeState( EventData::SELECTING );
1619
1620       mEventData->mUpdateHighlightBox = true;
1621       mEventData->mUpdateLeftSelectionPosition = true;
1622       mEventData->mUpdateRightSelectionPosition = true;
1623
1624       if( handleStopScrolling || isSmoothHandlePanEnabled )
1625       {
1626         mEventData->mScrollAfterUpdatePosition = true;
1627
1628         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1629             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1630         {
1631           mEventData->mLeftSelectionPosition = handlePosition;
1632         }
1633       }
1634     }
1635     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1636     {
1637       ChangeState( EventData::SELECTING );
1638
1639       mEventData->mUpdateHighlightBox = true;
1640       mEventData->mUpdateRightSelectionPosition = true;
1641       mEventData->mUpdateLeftSelectionPosition = true;
1642
1643       if( handleStopScrolling || isSmoothHandlePanEnabled )
1644       {
1645         mEventData->mScrollAfterUpdatePosition = true;
1646         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1647             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1648         {
1649           mEventData->mRightSelectionPosition = handlePosition;
1650         }
1651       }
1652     }
1653
1654     mEventData->mDecoratorUpdated = true;
1655   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1656   else if( HANDLE_SCROLLING == state )
1657   {
1658     const float xSpeed = event.p2.mFloat;
1659     const float ySpeed = event.p3.mFloat;
1660     const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1661     const Vector2 currentScrollPosition = mModel->mScrollPosition;
1662
1663     mModel->mScrollPosition.x += xSpeed;
1664     mModel->mScrollPosition.y += ySpeed;
1665
1666     ClampHorizontalScroll( layoutSize );
1667     ClampVerticalScroll( layoutSize );
1668
1669     bool endOfScroll = false;
1670     if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1671     {
1672       // Notify the decorator there is no more text to scroll.
1673       // The decorator won't send more scroll events.
1674       mEventData->mDecorator->NotifyEndOfScroll();
1675       // Still need to set the position of the handle.
1676       endOfScroll = true;
1677     }
1678
1679     // Set the position of the handle.
1680     const bool scrollRightDirection = xSpeed > 0.f;
1681     const bool scrollBottomDirection = ySpeed > 0.f;
1682     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1683     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1684
1685     if( Event::GRAB_HANDLE_EVENT == event.type )
1686     {
1687       ChangeState( EventData::GRAB_HANDLE_PANNING );
1688
1689       // Get the grab handle position in decorator coords.
1690       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1691
1692       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1693       {
1694         // Position the grag handle close to either the left or right edge.
1695         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1696       }
1697
1698       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1699       {
1700         position.x = mEventData->mCursorHookPositionX;
1701
1702         // Position the grag handle close to either the top or bottom edge.
1703         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1704       }
1705
1706       // Get the new handle position.
1707       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1708       bool matchedCharacter = false;
1709       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1710                                                                          mModel->mLogicalModel,
1711                                                                          mMetrics,
1712                                                                          position.x - mModel->mScrollPosition.x,
1713                                                                          position.y - mModel->mScrollPosition.y,
1714                                                                          CharacterHitTest::SCROLL,
1715                                                                          matchedCharacter );
1716
1717       if( mEventData->mPrimaryCursorPosition != handlePosition )
1718       {
1719         mEventData->mUpdateCursorPosition = true;
1720         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1721         mEventData->mScrollAfterUpdatePosition = true;
1722         mEventData->mPrimaryCursorPosition = handlePosition;
1723       }
1724       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1725
1726       // Updates the decorator if the soft handle panning is enabled.
1727       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1728     }
1729     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1730     {
1731       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1732
1733       // Get the selection handle position in decorator coords.
1734       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1735
1736       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1737       {
1738         // Position the selection handle close to either the left or right edge.
1739         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1740       }
1741
1742       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1743       {
1744         position.x = mEventData->mCursorHookPositionX;
1745
1746         // Position the grag handle close to either the top or bottom edge.
1747         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1748       }
1749
1750       // Get the new handle position.
1751       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1752       bool matchedCharacter = false;
1753       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1754                                                                          mModel->mLogicalModel,
1755                                                                          mMetrics,
1756                                                                          position.x - mModel->mScrollPosition.x,
1757                                                                          position.y - mModel->mScrollPosition.y,
1758                                                                          CharacterHitTest::SCROLL,
1759                                                                          matchedCharacter );
1760
1761       if( leftSelectionHandleEvent )
1762       {
1763         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1764
1765         if( differentHandles || endOfScroll )
1766         {
1767           mEventData->mUpdateHighlightBox = true;
1768           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1769           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1770           mEventData->mLeftSelectionPosition = handlePosition;
1771         }
1772       }
1773       else
1774       {
1775         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1776         if( differentHandles || endOfScroll )
1777         {
1778           mEventData->mUpdateHighlightBox = true;
1779           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1780           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1781           mEventData->mRightSelectionPosition = handlePosition;
1782         }
1783       }
1784
1785       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1786       {
1787         RepositionSelectionHandles();
1788
1789         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1790       }
1791     }
1792     mEventData->mDecoratorUpdated = true;
1793   } // end ( HANDLE_SCROLLING == state )
1794 }
1795
1796 void Controller::Impl::OnSelectEvent( const Event& event )
1797 {
1798   if( NULL == mEventData )
1799   {
1800     // Nothing to do if there is no text.
1801     return;
1802   }
1803
1804   if( mEventData->mSelectionEnabled )
1805   {
1806     // Convert from control's coords to text's coords.
1807     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1808     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1809
1810     // Calculates the logical position from the x,y coords.
1811     RepositionSelectionHandles( xPosition,
1812                                 yPosition,
1813                                 Controller::NoTextTap::HIGHLIGHT );
1814   }
1815 }
1816
1817 void Controller::Impl::OnSelectAllEvent()
1818 {
1819   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1820
1821   if( NULL == mEventData )
1822   {
1823     // Nothing to do if there is no text.
1824     return;
1825   }
1826
1827   if( mEventData->mSelectionEnabled )
1828   {
1829     ChangeState( EventData::SELECTING );
1830
1831     mEventData->mLeftSelectionPosition = 0u;
1832     mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1833
1834     mEventData->mScrollAfterUpdatePosition = true;
1835     mEventData->mUpdateLeftSelectionPosition = true;
1836     mEventData->mUpdateRightSelectionPosition = true;
1837     mEventData->mUpdateHighlightBox = true;
1838   }
1839 }
1840
1841 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1842 {
1843   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1844   {
1845     // Nothing to select if handles are in the same place.
1846     selectedText.clear();
1847     return;
1848   }
1849
1850   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1851
1852   //Get start and end position of selection
1853   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1854   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1855
1856   Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1857   const Length numberOfCharacters = utf32Characters.Count();
1858
1859   // Validate the start and end selection points
1860   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1861   {
1862     //Get text as a UTF8 string
1863     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1864
1865     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1866     {
1867       // Keep a copy of the current input style.
1868       InputStyle currentInputStyle;
1869       currentInputStyle.Copy( mEventData->mInputStyle );
1870
1871       // Set as input style the style of the first deleted character.
1872       mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1873
1874       // Compare if the input style has changed.
1875       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1876
1877       if( hasInputStyleChanged )
1878       {
1879         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1880         // Queue the input style changed signal.
1881         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1882       }
1883
1884       mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1885
1886       // Mark the paragraphs to be updated.
1887       if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1888       {
1889         mTextUpdateInfo.mCharacterIndex = 0;
1890         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1891         mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1892         mTextUpdateInfo.mClearAll = true;
1893       }
1894       else
1895       {
1896         mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1897         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1898       }
1899
1900       // Delete text between handles
1901       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1902       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1903       utf32Characters.Erase( first, last );
1904
1905       // Will show the cursor at the first character of the selection.
1906       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1907     }
1908     else
1909     {
1910       // Will show the cursor at the last character of the selection.
1911       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1912     }
1913
1914     mEventData->mDecoratorUpdated = true;
1915   }
1916 }
1917
1918 void Controller::Impl::ShowClipboard()
1919 {
1920   if( mClipboard )
1921   {
1922     mClipboard.ShowClipboard();
1923   }
1924 }
1925
1926 void Controller::Impl::HideClipboard()
1927 {
1928   if( mClipboard && mClipboardHideEnabled )
1929   {
1930     mClipboard.HideClipboard();
1931   }
1932 }
1933
1934 void Controller::Impl::SetClipboardHideEnable(bool enable)
1935 {
1936   mClipboardHideEnabled = enable;
1937 }
1938
1939 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1940 {
1941   //Send string to clipboard
1942   return ( mClipboard && mClipboard.SetItem( source ) );
1943 }
1944
1945 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1946 {
1947   std::string selectedText;
1948   RetrieveSelection( selectedText, deleteAfterSending );
1949   CopyStringToClipboard( selectedText );
1950   ChangeState( EventData::EDITING );
1951 }
1952
1953 void Controller::Impl::RequestGetTextFromClipboard()
1954 {
1955   if ( mClipboard )
1956   {
1957     mClipboard.RequestItem();
1958   }
1959 }
1960
1961 void Controller::Impl::RepositionSelectionHandles()
1962 {
1963   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1964   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1965
1966   if( selectionStart == selectionEnd )
1967   {
1968     // Nothing to select if handles are in the same place.
1969     // So, deactive Highlight box.
1970     mEventData->mDecorator->SetHighlightActive( false );
1971     return;
1972   }
1973
1974   mEventData->mDecorator->ClearHighlights();
1975
1976   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1977   const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1978   const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1979   const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1980   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1981   const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1982   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1983
1984   const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1985   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1986   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1987
1988   // Swap the indices if the start is greater than the end.
1989   const bool indicesSwapped = selectionStart > selectionEnd;
1990
1991   // Tell the decorator to flip the selection handles if needed.
1992   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1993
1994   if( indicesSwapped )
1995   {
1996     std::swap( selectionStart, selectionEnd );
1997   }
1998
1999   // Get the indices to the first and last selected glyphs.
2000   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2001   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2002   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2003   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2004
2005   // Get the lines where the glyphs are laid-out.
2006   const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2007
2008   LineIndex lineIndex = 0u;
2009   Length numberOfLines = 0u;
2010   mModel->mVisualModel->GetNumberOfLines( glyphStart,
2011                                           1u + glyphEnd - glyphStart,
2012                                           lineIndex,
2013                                           numberOfLines );
2014   const LineIndex firstLineIndex = lineIndex;
2015
2016   // Create the structure to store some selection box info.
2017   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2018   selectionBoxLinesInfo.Resize( numberOfLines );
2019
2020   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2021   selectionBoxInfo->minX = MAX_FLOAT;
2022   selectionBoxInfo->maxX = MIN_FLOAT;
2023
2024   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2025   float minHighlightX = std::numeric_limits<float>::max();
2026   float maxHighlightX = std::numeric_limits<float>::min();
2027   Size highLightSize;
2028   Vector2 highLightPosition; // The highlight position in decorator's coords.
2029
2030   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2031
2032   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2033   selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2034                                                       firstLineIndex );
2035
2036   // Transform to decorator's (control) coords.
2037   selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2038
2039   lineRun += firstLineIndex;
2040
2041   // The line height is the addition of the line ascender and the line descender.
2042   // However, the line descender has a negative value, hence the subtraction.
2043   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2044
2045   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2046
2047   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2048   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2049   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2050
2051   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2052   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2053   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2054
2055   // The number of quads of the selection box.
2056   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2057   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2058
2059   // Count the actual number of quads.
2060   unsigned int actualNumberOfQuads = 0u;
2061   Vector4 quad;
2062
2063   // Traverse the glyphs.
2064   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2065   {
2066     const GlyphInfo& glyph = *( glyphsBuffer + index );
2067     const Vector2& position = *( positionsBuffer + index );
2068
2069     if( splitStartGlyph )
2070     {
2071       // 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.
2072
2073       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2074       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2075       // Get the direction of the character.
2076       CharacterDirection isCurrentRightToLeft = false;
2077       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2078       {
2079         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2080       }
2081
2082       // The end point could be in the middle of the ligature.
2083       // Calculate the number of characters selected.
2084       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2085
2086       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2087       quad.y = selectionBoxInfo->lineOffset;
2088       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2089       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2090
2091       // Store the min and max 'x' for each line.
2092       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2093       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2094
2095       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2096       ++actualNumberOfQuads;
2097
2098       splitStartGlyph = false;
2099       continue;
2100     }
2101
2102     if( splitEndGlyph && ( index == glyphEnd ) )
2103     {
2104       // 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.
2105
2106       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2107       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2108       // Get the direction of the character.
2109       CharacterDirection isCurrentRightToLeft = false;
2110       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2111       {
2112         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2113       }
2114
2115       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2116
2117       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2118       quad.y = selectionBoxInfo->lineOffset;
2119       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2120       quad.w = quad.y + selectionBoxInfo->lineHeight;
2121
2122       // Store the min and max 'x' for each line.
2123       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2124       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2125
2126       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2127                                             quad );
2128       ++actualNumberOfQuads;
2129
2130       splitEndGlyph = false;
2131       continue;
2132     }
2133
2134     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2135     quad.y = selectionBoxInfo->lineOffset;
2136     quad.z = quad.x + glyph.advance;
2137     quad.w = quad.y + selectionBoxInfo->lineHeight;
2138
2139     // Store the min and max 'x' for each line.
2140     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2141     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2142
2143     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2144                                           quad );
2145     ++actualNumberOfQuads;
2146
2147     // Whether to retrieve the next line.
2148     if( index == lastGlyphOfLine )
2149     {
2150       ++lineIndex;
2151       if( lineIndex < firstLineIndex + numberOfLines )
2152       {
2153         // Retrieve the next line.
2154         ++lineRun;
2155
2156         // Get the last glyph of the new line.
2157         lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2158
2159         // Keep the offset and height of the current selection box.
2160         const float currentLineOffset = selectionBoxInfo->lineOffset;
2161         const float currentLineHeight = selectionBoxInfo->lineHeight;
2162
2163         // Get the selection box info for the next line.
2164         ++selectionBoxInfo;
2165
2166         selectionBoxInfo->minX = MAX_FLOAT;
2167         selectionBoxInfo->maxX = MIN_FLOAT;
2168
2169         // Update the line's vertical offset.
2170         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2171
2172         // The line height is the addition of the line ascender and the line descender.
2173         // However, the line descender has a negative value, hence the subtraction.
2174         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2175       }
2176     }
2177   }
2178
2179   // Traverses all the lines and updates the min and max 'x' positions and the total height.
2180   // The final width is calculated after 'boxifying' the selection.
2181   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2182          endIt = selectionBoxLinesInfo.End();
2183        it != endIt;
2184        ++it )
2185   {
2186     const SelectionBoxInfo& info = *it;
2187
2188     // Update the size of the highlighted text.
2189     highLightSize.height += info.lineHeight;
2190     minHighlightX = std::min( minHighlightX, info.minX );
2191     maxHighlightX = std::max( maxHighlightX, info.maxX );
2192   }
2193
2194   // Add extra geometry to 'boxify' the selection.
2195
2196   if( 1u < numberOfLines )
2197   {
2198     // Boxify the first line.
2199     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2200     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2201
2202     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2203     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2204
2205     if( boxifyBegin )
2206     {
2207       quad.x = 0.f;
2208       quad.y = firstSelectionBoxLineInfo.lineOffset;
2209       quad.z = firstSelectionBoxLineInfo.minX;
2210       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2211
2212       // Boxify at the beginning of the line.
2213       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2214                                             quad );
2215       ++actualNumberOfQuads;
2216
2217       // Update the size of the highlighted text.
2218       minHighlightX = 0.f;
2219     }
2220
2221     if( boxifyEnd )
2222     {
2223       quad.x = firstSelectionBoxLineInfo.maxX;
2224       quad.y = firstSelectionBoxLineInfo.lineOffset;
2225       quad.z = mModel->mVisualModel->mControlSize.width;
2226       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2227
2228       // Boxify at the end of the line.
2229       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2230                                             quad );
2231       ++actualNumberOfQuads;
2232
2233       // Update the size of the highlighted text.
2234       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2235     }
2236
2237     // Boxify the central lines.
2238     if( 2u < numberOfLines )
2239     {
2240       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2241              endIt = selectionBoxLinesInfo.End() - 1u;
2242            it != endIt;
2243            ++it )
2244       {
2245         const SelectionBoxInfo& info = *it;
2246
2247         quad.x = 0.f;
2248         quad.y = info.lineOffset;
2249         quad.z = info.minX;
2250         quad.w = info.lineOffset + info.lineHeight;
2251
2252         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2253                                               quad );
2254         ++actualNumberOfQuads;
2255
2256         quad.x = info.maxX;
2257         quad.y = info.lineOffset;
2258         quad.z = mModel->mVisualModel->mControlSize.width;
2259         quad.w = info.lineOffset + info.lineHeight;
2260
2261         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2262                                               quad );
2263         ++actualNumberOfQuads;
2264       }
2265
2266       // Update the size of the highlighted text.
2267       minHighlightX = 0.f;
2268       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2269     }
2270
2271     // Boxify the last line.
2272     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2273     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2274
2275     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2276     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2277
2278     if( boxifyBegin )
2279     {
2280       quad.x = 0.f;
2281       quad.y = lastSelectionBoxLineInfo.lineOffset;
2282       quad.z = lastSelectionBoxLineInfo.minX;
2283       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2284
2285       // Boxify at the beginning of the line.
2286       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2287                                             quad );
2288       ++actualNumberOfQuads;
2289
2290       // Update the size of the highlighted text.
2291       minHighlightX = 0.f;
2292     }
2293
2294     if( boxifyEnd )
2295     {
2296       quad.x = lastSelectionBoxLineInfo.maxX;
2297       quad.y = lastSelectionBoxLineInfo.lineOffset;
2298       quad.z = mModel->mVisualModel->mControlSize.width;
2299       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2300
2301       // Boxify at the end of the line.
2302       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2303                                             quad );
2304       ++actualNumberOfQuads;
2305
2306       // Update the size of the highlighted text.
2307       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2308     }
2309   }
2310
2311   // Set the actual number of quads.
2312   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2313
2314   // Sets the highlight's size and position. In decorator's coords.
2315   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2316   highLightSize.width = maxHighlightX - minHighlightX;
2317
2318   highLightPosition.x = minHighlightX;
2319   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2320   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2321
2322   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2323
2324   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2325   {
2326     CursorInfo primaryCursorInfo;
2327     GetCursorPosition( mEventData->mLeftSelectionPosition,
2328                        primaryCursorInfo );
2329
2330     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2331
2332     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2333                                          primaryPosition.x,
2334                                          primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2335                                          primaryCursorInfo.lineHeight );
2336
2337     CursorInfo secondaryCursorInfo;
2338     GetCursorPosition( mEventData->mRightSelectionPosition,
2339                        secondaryCursorInfo );
2340
2341     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2342
2343     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2344                                          secondaryPosition.x,
2345                                          secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2346                                          secondaryCursorInfo.lineHeight );
2347   }
2348
2349   // Set the flag to update the decorator.
2350   mEventData->mDecoratorUpdated = true;
2351 }
2352
2353 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2354 {
2355   if( NULL == mEventData )
2356   {
2357     // Nothing to do if there is no text input.
2358     return;
2359   }
2360
2361   if( IsShowingPlaceholderText() )
2362   {
2363     // Nothing to do if there is the place-holder text.
2364     return;
2365   }
2366
2367   const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2368   const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
2369   if( ( 0 == numberOfGlyphs ) ||
2370       ( 0 == numberOfLines ) )
2371   {
2372     // Nothing to do if there is no text.
2373     return;
2374   }
2375
2376   // Find which word was selected
2377   CharacterIndex selectionStart( 0 );
2378   CharacterIndex selectionEnd( 0 );
2379   CharacterIndex noTextHitIndex( 0 );
2380   const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2381                                                   mModel->mLogicalModel,
2382                                                   mMetrics,
2383                                                   visualX,
2384                                                   visualY,
2385                                                   selectionStart,
2386                                                   selectionEnd,
2387                                                   noTextHitIndex );
2388   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2389
2390   if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2391   {
2392     ChangeState( EventData::SELECTING );
2393
2394     mEventData->mLeftSelectionPosition = selectionStart;
2395     mEventData->mRightSelectionPosition = selectionEnd;
2396
2397     mEventData->mUpdateLeftSelectionPosition = true;
2398     mEventData->mUpdateRightSelectionPosition = true;
2399     mEventData->mUpdateHighlightBox = true;
2400
2401     // It may happen an IMF commit event arrives before the selection event
2402     // if the IMF manager is in pre-edit state. The commit event will set the
2403     // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2404     // to false, the highlight box won't be updated.
2405     mEventData->mUpdateCursorPosition = false;
2406
2407     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2408
2409     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2410     mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2411   }
2412   else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2413   {
2414     // Nothing to select. i.e. a white space, out of bounds
2415     ChangeState( EventData::EDITING_WITH_POPUP );
2416
2417     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2418
2419     mEventData->mUpdateCursorPosition = true;
2420     mEventData->mUpdateGrabHandlePosition = true;
2421     mEventData->mScrollAfterUpdatePosition = true;
2422     mEventData->mUpdateInputStyle = true;
2423   }
2424   else if( Controller::NoTextTap::NO_ACTION == action )
2425   {
2426     // Nothing to select. i.e. a white space, out of bounds
2427     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2428
2429     mEventData->mUpdateCursorPosition = true;
2430     mEventData->mUpdateGrabHandlePosition = true;
2431     mEventData->mScrollAfterUpdatePosition = true;
2432     mEventData->mUpdateInputStyle = true;
2433   }
2434 }
2435
2436 void Controller::Impl::SetPopupButtons()
2437 {
2438   /**
2439    *  Sets the Popup buttons to be shown depending on State.
2440    *
2441    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2442    *
2443    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2444    */
2445
2446   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2447
2448   if( EventData::SELECTING == mEventData->mState )
2449   {
2450     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2451
2452     if( !IsClipboardEmpty() )
2453     {
2454       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2455       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2456     }
2457
2458     if( !mEventData->mAllTextSelected )
2459     {
2460       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2461     }
2462   }
2463   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2464   {
2465     if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2466     {
2467       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2468     }
2469
2470     if( !IsClipboardEmpty() )
2471     {
2472       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2473       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2474     }
2475   }
2476   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2477   {
2478     if ( !IsClipboardEmpty() )
2479     {
2480       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2481       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2482     }
2483   }
2484
2485   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2486 }
2487
2488 void Controller::Impl::ChangeState( EventData::State newState )
2489 {
2490   if( NULL == mEventData )
2491   {
2492     // Nothing to do if there is no text input.
2493     return;
2494   }
2495
2496   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2497
2498   if( mEventData->mState != newState )
2499   {
2500     mEventData->mPreviousState = mEventData->mState;
2501     mEventData->mState = newState;
2502
2503     switch( mEventData->mState )
2504     {
2505       case EventData::INACTIVE:
2506       {
2507         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2508         mEventData->mDecorator->StopCursorBlink();
2509         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2510         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2511         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2512         mEventData->mDecorator->SetHighlightActive( false );
2513         mEventData->mDecorator->SetPopupActive( false );
2514         mEventData->mDecoratorUpdated = true;
2515         break;
2516       }
2517       case EventData::INTERRUPTED:
2518       {
2519         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2520         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2521         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2522         mEventData->mDecorator->SetHighlightActive( false );
2523         mEventData->mDecorator->SetPopupActive( false );
2524         mEventData->mDecoratorUpdated = true;
2525         break;
2526       }
2527       case EventData::SELECTING:
2528       {
2529         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2530         mEventData->mDecorator->StopCursorBlink();
2531         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2532         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2533         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2534         mEventData->mDecorator->SetHighlightActive( true );
2535         if( mEventData->mGrabHandlePopupEnabled )
2536         {
2537           SetPopupButtons();
2538           mEventData->mDecorator->SetPopupActive( true );
2539         }
2540         mEventData->mDecoratorUpdated = true;
2541         break;
2542       }
2543       case EventData::EDITING:
2544       {
2545         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2546         if( mEventData->mCursorBlinkEnabled )
2547         {
2548           mEventData->mDecorator->StartCursorBlink();
2549         }
2550         // Grab handle is not shown until a tap is received whilst EDITING
2551         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2552         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2553         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2554         mEventData->mDecorator->SetHighlightActive( false );
2555         if( mEventData->mGrabHandlePopupEnabled )
2556         {
2557           mEventData->mDecorator->SetPopupActive( false );
2558         }
2559         mEventData->mDecoratorUpdated = true;
2560         break;
2561       }
2562       case EventData::EDITING_WITH_POPUP:
2563       {
2564         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2565
2566         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2567         if( mEventData->mCursorBlinkEnabled )
2568         {
2569           mEventData->mDecorator->StartCursorBlink();
2570         }
2571         if( mEventData->mSelectionEnabled )
2572         {
2573           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2574           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2575           mEventData->mDecorator->SetHighlightActive( false );
2576         }
2577         else
2578         {
2579           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2580         }
2581         if( mEventData->mGrabHandlePopupEnabled )
2582         {
2583           SetPopupButtons();
2584           mEventData->mDecorator->SetPopupActive( true );
2585         }
2586         mEventData->mDecoratorUpdated = true;
2587         break;
2588       }
2589       case EventData::EDITING_WITH_GRAB_HANDLE:
2590       {
2591         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2592
2593         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2594         if( mEventData->mCursorBlinkEnabled )
2595         {
2596           mEventData->mDecorator->StartCursorBlink();
2597         }
2598         // Grab handle is not shown until a tap is received whilst EDITING
2599         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2600         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2601         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2602         mEventData->mDecorator->SetHighlightActive( false );
2603         if( mEventData->mGrabHandlePopupEnabled )
2604         {
2605           mEventData->mDecorator->SetPopupActive( false );
2606         }
2607         mEventData->mDecoratorUpdated = true;
2608         break;
2609       }
2610       case EventData::SELECTION_HANDLE_PANNING:
2611       {
2612         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2613         mEventData->mDecorator->StopCursorBlink();
2614         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2615         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2616         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2617         mEventData->mDecorator->SetHighlightActive( true );
2618         if( mEventData->mGrabHandlePopupEnabled )
2619         {
2620           mEventData->mDecorator->SetPopupActive( false );
2621         }
2622         mEventData->mDecoratorUpdated = true;
2623         break;
2624       }
2625       case EventData::GRAB_HANDLE_PANNING:
2626       {
2627         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2628
2629         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2630         if( mEventData->mCursorBlinkEnabled )
2631         {
2632           mEventData->mDecorator->StartCursorBlink();
2633         }
2634         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2635         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2636         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2637         mEventData->mDecorator->SetHighlightActive( false );
2638         if( mEventData->mGrabHandlePopupEnabled )
2639         {
2640           mEventData->mDecorator->SetPopupActive( false );
2641         }
2642         mEventData->mDecoratorUpdated = true;
2643         break;
2644       }
2645       case EventData::EDITING_WITH_PASTE_POPUP:
2646       {
2647         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2648
2649         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2650         if( mEventData->mCursorBlinkEnabled )
2651         {
2652           mEventData->mDecorator->StartCursorBlink();
2653         }
2654
2655         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2656         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2657         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2658         mEventData->mDecorator->SetHighlightActive( false );
2659
2660         if( mEventData->mGrabHandlePopupEnabled )
2661         {
2662           SetPopupButtons();
2663           mEventData->mDecorator->SetPopupActive( true );
2664         }
2665         mEventData->mDecoratorUpdated = true;
2666         break;
2667       }
2668       case EventData::TEXT_PANNING:
2669       {
2670         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2671         mEventData->mDecorator->StopCursorBlink();
2672         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2673         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2674             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2675         {
2676           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2677           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2678           mEventData->mDecorator->SetHighlightActive( true );
2679         }
2680
2681         if( mEventData->mGrabHandlePopupEnabled )
2682         {
2683           mEventData->mDecorator->SetPopupActive( false );
2684         }
2685
2686         mEventData->mDecoratorUpdated = true;
2687         break;
2688       }
2689     }
2690   }
2691 }
2692
2693 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2694                                           CursorInfo& cursorInfo )
2695 {
2696   if( !IsShowingRealText() )
2697   {
2698     // Do not want to use the place-holder text to set the cursor position.
2699
2700     // Use the line's height of the font's family set to set the cursor's size.
2701     // If there is no font's family set, use the default font.
2702     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2703
2704     cursorInfo.lineOffset = 0.f;
2705     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2706     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2707
2708     switch( mModel->mHorizontalAlignment )
2709     {
2710       case Text::HorizontalAlignment::BEGIN :
2711       {
2712         cursorInfo.primaryPosition.x = 0.f;
2713         break;
2714       }
2715       case Text::HorizontalAlignment::CENTER:
2716       {
2717         cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2718         break;
2719       }
2720       case Text::HorizontalAlignment::END:
2721       {
2722         cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2723         break;
2724       }
2725     }
2726
2727     // Nothing else to do.
2728     return;
2729   }
2730
2731   const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2732   GetCursorPositionParameters parameters;
2733   parameters.visualModel = mModel->mVisualModel;
2734   parameters.logicalModel = mModel->mLogicalModel;
2735   parameters.metrics = mMetrics;
2736   parameters.logical = logical;
2737   parameters.isMultiline = isMultiLine;
2738
2739   Text::GetCursorPosition( parameters,
2740                            cursorInfo );
2741
2742   // Adds Outline offset.
2743   const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2744   cursorInfo.primaryPosition.x += outlineWidth;
2745   cursorInfo.primaryPosition.y += outlineWidth;
2746   cursorInfo.secondaryPosition.x += outlineWidth;
2747   cursorInfo.secondaryPosition.y += outlineWidth;
2748
2749   if( isMultiLine )
2750   {
2751     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2752
2753     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2754     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2755
2756     if( 0.f > cursorInfo.primaryPosition.x )
2757     {
2758       cursorInfo.primaryPosition.x = 0.f;
2759     }
2760
2761     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2762     if( cursorInfo.primaryPosition.x > edgeWidth )
2763     {
2764       cursorInfo.primaryPosition.x = edgeWidth;
2765     }
2766   }
2767 }
2768
2769 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2770 {
2771   if( NULL == mEventData )
2772   {
2773     // Nothing to do if there is no text input.
2774     return 0u;
2775   }
2776
2777   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2778
2779   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2780   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2781
2782   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2783   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2784
2785   if( numberOfCharacters > 1u )
2786   {
2787     const Script script = mModel->mLogicalModel->GetScript( index );
2788     if( HasLigatureMustBreak( script ) )
2789     {
2790       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2791       numberOfCharacters = 1u;
2792     }
2793   }
2794   else
2795   {
2796     while( 0u == numberOfCharacters )
2797     {
2798       ++glyphIndex;
2799       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2800     }
2801   }
2802
2803   if( index < mEventData->mPrimaryCursorPosition )
2804   {
2805     cursorIndex -= numberOfCharacters;
2806   }
2807   else
2808   {
2809     cursorIndex += numberOfCharacters;
2810   }
2811
2812   // Will update the cursor hook position.
2813   mEventData->mUpdateCursorHookPosition = true;
2814
2815   return cursorIndex;
2816 }
2817
2818 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2819 {
2820   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2821   if( NULL == mEventData )
2822   {
2823     // Nothing to do if there is no text input.
2824     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2825     return;
2826   }
2827
2828   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2829
2830   mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2831
2832   // Sets the cursor position.
2833   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2834                                        cursorPosition.x,
2835                                        cursorPosition.y,
2836                                        cursorInfo.primaryCursorHeight,
2837                                        cursorInfo.lineHeight );
2838   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2839
2840   if( mEventData->mUpdateGrabHandlePosition )
2841   {
2842     // Sets the grab handle position.
2843     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2844                                          cursorPosition.x,
2845                                          cursorInfo.lineOffset + mModel->mScrollPosition.y,
2846                                          cursorInfo.lineHeight );
2847   }
2848
2849   if( cursorInfo.isSecondaryCursor )
2850   {
2851     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2852                                          cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2853                                          cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2854                                          cursorInfo.secondaryCursorHeight,
2855                                          cursorInfo.lineHeight );
2856     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2857   }
2858
2859   // Set which cursors are active according the state.
2860   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2861   {
2862     if( cursorInfo.isSecondaryCursor )
2863     {
2864       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2865     }
2866     else
2867     {
2868       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2869     }
2870   }
2871   else
2872   {
2873     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2874   }
2875
2876   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2877 }
2878
2879 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2880                                               const CursorInfo& cursorInfo )
2881 {
2882   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2883       ( RIGHT_SELECTION_HANDLE != handleType ) )
2884   {
2885     return;
2886   }
2887
2888   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2889
2890   // Sets the handle's position.
2891   mEventData->mDecorator->SetPosition( handleType,
2892                                        cursorPosition.x,
2893                                        cursorInfo.lineOffset + mModel->mScrollPosition.y,
2894                                        cursorInfo.lineHeight );
2895
2896   // If selection handle at start of the text and other at end of the text then all text is selected.
2897   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2898   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2899   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2900 }
2901
2902 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2903 {
2904   // Clamp between -space & -alignment offset.
2905
2906   if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2907   {
2908     const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2909     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2910     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2911
2912     mEventData->mDecoratorUpdated = true;
2913   }
2914   else
2915   {
2916     mModel->mScrollPosition.x = 0.f;
2917   }
2918 }
2919
2920 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2921 {
2922   if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2923   {
2924     // Nothing to do if the text is single line.
2925     return;
2926   }
2927
2928   // Clamp between -space & 0.
2929   if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2930   {
2931     const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2932     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2933     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2934
2935     mEventData->mDecoratorUpdated = true;
2936   }
2937   else
2938   {
2939     mModel->mScrollPosition.y = 0.f;
2940   }
2941 }
2942
2943 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2944 {
2945   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2946
2947   // position is in actor's coords.
2948   const float positionEndX = position.x + cursorWidth;
2949   const float positionEndY = position.y + lineHeight;
2950
2951   // Transform the position to decorator coords.
2952   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2953   const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2954
2955   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2956   const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2957
2958   if( decoratorPositionBeginX < 0.f )
2959   {
2960     mModel->mScrollPosition.x = -position.x;
2961   }
2962   else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2963   {
2964     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2965   }
2966
2967   if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2968   {
2969     if( decoratorPositionBeginY < 0.f )
2970     {
2971       mModel->mScrollPosition.y = -position.y;
2972     }
2973     else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2974     {
2975       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2976     }
2977   }
2978 }
2979
2980 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2981 {
2982   // Get the current cursor position in decorator coords.
2983   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2984
2985   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition  );
2986
2987
2988
2989   // Calculate the offset to match the cursor position before the character was deleted.
2990   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2991
2992   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2993   if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2994   {
2995     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2996     mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2997   }
2998
2999
3000   ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3001   ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3002
3003   // Makes the new cursor position visible if needed.
3004   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3005 }
3006
3007 void Controller::Impl::RequestRelayout()
3008 {
3009   if( NULL != mControlInterface )
3010   {
3011     mControlInterface->RequestTextRelayout();
3012   }
3013 }
3014
3015 } // namespace Text
3016
3017 } // namespace Toolkit
3018
3019 } // namespace Dali