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