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