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