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