09395d02378faebcd88bad0c7a3b98093274fea8
[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     // Calculates the logical position from the start.
1851     RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
1852                                 0.f - mModel->mScrollPosition.y,
1853                                 Controller::NoTextTap::HIGHLIGHT );
1854
1855     mEventData->mLeftSelectionPosition = 0u;
1856     mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1857   }
1858 }
1859
1860 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1861 {
1862   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1863   {
1864     // Nothing to select if handles are in the same place.
1865     selectedText.clear();
1866     return;
1867   }
1868
1869   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1870
1871   //Get start and end position of selection
1872   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1873   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1874
1875   Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1876   const Length numberOfCharacters = utf32Characters.Count();
1877
1878   // Validate the start and end selection points
1879   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1880   {
1881     //Get text as a UTF8 string
1882     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1883
1884     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1885     {
1886       // Keep a copy of the current input style.
1887       InputStyle currentInputStyle;
1888       currentInputStyle.Copy( mEventData->mInputStyle );
1889
1890       // Set as input style the style of the first deleted character.
1891       mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1892
1893       // Compare if the input style has changed.
1894       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1895
1896       if( hasInputStyleChanged )
1897       {
1898         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1899         // Queue the input style changed signal.
1900         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1901       }
1902
1903       mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1904
1905       // Mark the paragraphs to be updated.
1906       if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1907       {
1908         mTextUpdateInfo.mCharacterIndex = 0;
1909         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1910         mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1911         mTextUpdateInfo.mClearAll = true;
1912       }
1913       else
1914       {
1915         mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1916         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1917       }
1918
1919       // Delete text between handles
1920       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1921       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1922       utf32Characters.Erase( first, last );
1923
1924       // Will show the cursor at the first character of the selection.
1925       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1926     }
1927     else
1928     {
1929       // Will show the cursor at the last character of the selection.
1930       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1931     }
1932
1933     mEventData->mDecoratorUpdated = true;
1934   }
1935 }
1936
1937 void Controller::Impl::ShowClipboard()
1938 {
1939   if( mClipboard )
1940   {
1941     mClipboard.ShowClipboard();
1942   }
1943 }
1944
1945 void Controller::Impl::HideClipboard()
1946 {
1947   if( mClipboard && mClipboardHideEnabled )
1948   {
1949     mClipboard.HideClipboard();
1950   }
1951 }
1952
1953 void Controller::Impl::SetClipboardHideEnable(bool enable)
1954 {
1955   mClipboardHideEnabled = enable;
1956 }
1957
1958 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1959 {
1960   //Send string to clipboard
1961   return ( mClipboard && mClipboard.SetItem( source ) );
1962 }
1963
1964 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1965 {
1966   std::string selectedText;
1967   RetrieveSelection( selectedText, deleteAfterSending );
1968   CopyStringToClipboard( selectedText );
1969   ChangeState( EventData::EDITING );
1970 }
1971
1972 void Controller::Impl::RequestGetTextFromClipboard()
1973 {
1974   if ( mClipboard )
1975   {
1976     mClipboard.RequestItem();
1977   }
1978 }
1979
1980 void Controller::Impl::RepositionSelectionHandles()
1981 {
1982   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1983   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1984
1985   if( selectionStart == selectionEnd )
1986   {
1987     // Nothing to select if handles are in the same place.
1988     // So, deactive Highlight box.
1989     mEventData->mDecorator->SetHighlightActive( false );
1990     return;
1991   }
1992
1993   mEventData->mDecorator->ClearHighlights();
1994
1995   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1996   const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1997   const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1998   const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1999   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2000   const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2001   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2002
2003   const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2004   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2005   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2006
2007   // Swap the indices if the start is greater than the end.
2008   const bool indicesSwapped = selectionStart > selectionEnd;
2009
2010   // Tell the decorator to flip the selection handles if needed.
2011   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2012
2013   if( indicesSwapped )
2014   {
2015     std::swap( selectionStart, selectionEnd );
2016   }
2017
2018   // Get the indices to the first and last selected glyphs.
2019   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2020   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2021   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2022   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2023
2024   // Get the lines where the glyphs are laid-out.
2025   const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2026
2027   LineIndex lineIndex = 0u;
2028   Length numberOfLines = 0u;
2029   mModel->mVisualModel->GetNumberOfLines( glyphStart,
2030                                           1u + glyphEnd - glyphStart,
2031                                           lineIndex,
2032                                           numberOfLines );
2033   const LineIndex firstLineIndex = lineIndex;
2034
2035   // Create the structure to store some selection box info.
2036   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2037   selectionBoxLinesInfo.Resize( numberOfLines );
2038
2039   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2040   selectionBoxInfo->minX = MAX_FLOAT;
2041   selectionBoxInfo->maxX = MIN_FLOAT;
2042
2043   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2044   float minHighlightX = std::numeric_limits<float>::max();
2045   float maxHighlightX = std::numeric_limits<float>::min();
2046   Size highLightSize;
2047   Vector2 highLightPosition; // The highlight position in decorator's coords.
2048
2049   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2050
2051   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2052   selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2053                                                       firstLineIndex );
2054
2055   // Transform to decorator's (control) coords.
2056   selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2057
2058   lineRun += firstLineIndex;
2059
2060   // The line height is the addition of the line ascender and the line descender.
2061   // However, the line descender has a negative value, hence the subtraction.
2062   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2063
2064   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2065
2066   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
2067   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2068   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2069
2070   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
2071   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2072   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2073
2074   // The number of quads of the selection box.
2075   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2076   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2077
2078   // Count the actual number of quads.
2079   unsigned int actualNumberOfQuads = 0u;
2080   Vector4 quad;
2081
2082   // Traverse the glyphs.
2083   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2084   {
2085     const GlyphInfo& glyph = *( glyphsBuffer + index );
2086     const Vector2& position = *( positionsBuffer + index );
2087
2088     if( splitStartGlyph )
2089     {
2090       // 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.
2091
2092       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2093       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2094       // Get the direction of the character.
2095       CharacterDirection isCurrentRightToLeft = false;
2096       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2097       {
2098         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2099       }
2100
2101       // The end point could be in the middle of the ligature.
2102       // Calculate the number of characters selected.
2103       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2104
2105       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2106       quad.y = selectionBoxInfo->lineOffset;
2107       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2108       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2109
2110       // Store the min and max 'x' for each line.
2111       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2112       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2113
2114       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2115       ++actualNumberOfQuads;
2116
2117       splitStartGlyph = false;
2118       continue;
2119     }
2120
2121     if( splitEndGlyph && ( index == glyphEnd ) )
2122     {
2123       // 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.
2124
2125       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2126       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2127       // Get the direction of the character.
2128       CharacterDirection isCurrentRightToLeft = false;
2129       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2130       {
2131         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2132       }
2133
2134       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2135
2136       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2137       quad.y = selectionBoxInfo->lineOffset;
2138       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2139       quad.w = quad.y + selectionBoxInfo->lineHeight;
2140
2141       // Store the min and max 'x' for each line.
2142       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2143       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2144
2145       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2146                                             quad );
2147       ++actualNumberOfQuads;
2148
2149       splitEndGlyph = false;
2150       continue;
2151     }
2152
2153     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2154     quad.y = selectionBoxInfo->lineOffset;
2155     quad.z = quad.x + glyph.advance;
2156     quad.w = quad.y + selectionBoxInfo->lineHeight;
2157
2158     // Store the min and max 'x' for each line.
2159     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2160     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2161
2162     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2163                                           quad );
2164     ++actualNumberOfQuads;
2165
2166     // Whether to retrieve the next line.
2167     if( index == lastGlyphOfLine )
2168     {
2169       ++lineIndex;
2170       if( lineIndex < firstLineIndex + numberOfLines )
2171       {
2172         // Retrieve the next line.
2173         ++lineRun;
2174
2175         // Get the last glyph of the new line.
2176         lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2177
2178         // Keep the offset and height of the current selection box.
2179         const float currentLineOffset = selectionBoxInfo->lineOffset;
2180         const float currentLineHeight = selectionBoxInfo->lineHeight;
2181
2182         // Get the selection box info for the next line.
2183         ++selectionBoxInfo;
2184
2185         selectionBoxInfo->minX = MAX_FLOAT;
2186         selectionBoxInfo->maxX = MIN_FLOAT;
2187
2188         // Update the line's vertical offset.
2189         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2190
2191         // The line height is the addition of the line ascender and the line descender.
2192         // However, the line descender has a negative value, hence the subtraction.
2193         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2194       }
2195     }
2196   }
2197
2198   // Traverses all the lines and updates the min and max 'x' positions and the total height.
2199   // The final width is calculated after 'boxifying' the selection.
2200   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2201          endIt = selectionBoxLinesInfo.End();
2202        it != endIt;
2203        ++it )
2204   {
2205     const SelectionBoxInfo& info = *it;
2206
2207     // Update the size of the highlighted text.
2208     highLightSize.height += info.lineHeight;
2209     minHighlightX = std::min( minHighlightX, info.minX );
2210     maxHighlightX = std::max( maxHighlightX, info.maxX );
2211   }
2212
2213   // Add extra geometry to 'boxify' the selection.
2214
2215   if( 1u < numberOfLines )
2216   {
2217     // Boxify the first line.
2218     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2219     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2220
2221     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2222     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2223
2224     if( boxifyBegin )
2225     {
2226       quad.x = 0.f;
2227       quad.y = firstSelectionBoxLineInfo.lineOffset;
2228       quad.z = firstSelectionBoxLineInfo.minX;
2229       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2230
2231       // Boxify at the beginning of the line.
2232       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2233                                             quad );
2234       ++actualNumberOfQuads;
2235
2236       // Update the size of the highlighted text.
2237       minHighlightX = 0.f;
2238     }
2239
2240     if( boxifyEnd )
2241     {
2242       quad.x = firstSelectionBoxLineInfo.maxX;
2243       quad.y = firstSelectionBoxLineInfo.lineOffset;
2244       quad.z = mModel->mVisualModel->mControlSize.width;
2245       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2246
2247       // Boxify at the end of the line.
2248       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2249                                             quad );
2250       ++actualNumberOfQuads;
2251
2252       // Update the size of the highlighted text.
2253       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2254     }
2255
2256     // Boxify the central lines.
2257     if( 2u < numberOfLines )
2258     {
2259       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2260              endIt = selectionBoxLinesInfo.End() - 1u;
2261            it != endIt;
2262            ++it )
2263       {
2264         const SelectionBoxInfo& info = *it;
2265
2266         quad.x = 0.f;
2267         quad.y = info.lineOffset;
2268         quad.z = info.minX;
2269         quad.w = info.lineOffset + info.lineHeight;
2270
2271         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2272                                               quad );
2273         ++actualNumberOfQuads;
2274
2275         quad.x = info.maxX;
2276         quad.y = info.lineOffset;
2277         quad.z = mModel->mVisualModel->mControlSize.width;
2278         quad.w = info.lineOffset + info.lineHeight;
2279
2280         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2281                                               quad );
2282         ++actualNumberOfQuads;
2283       }
2284
2285       // Update the size of the highlighted text.
2286       minHighlightX = 0.f;
2287       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2288     }
2289
2290     // Boxify the last line.
2291     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2292     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2293
2294     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2295     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2296
2297     if( boxifyBegin )
2298     {
2299       quad.x = 0.f;
2300       quad.y = lastSelectionBoxLineInfo.lineOffset;
2301       quad.z = lastSelectionBoxLineInfo.minX;
2302       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2303
2304       // Boxify at the beginning of the line.
2305       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2306                                             quad );
2307       ++actualNumberOfQuads;
2308
2309       // Update the size of the highlighted text.
2310       minHighlightX = 0.f;
2311     }
2312
2313     if( boxifyEnd )
2314     {
2315       quad.x = lastSelectionBoxLineInfo.maxX;
2316       quad.y = lastSelectionBoxLineInfo.lineOffset;
2317       quad.z = mModel->mVisualModel->mControlSize.width;
2318       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2319
2320       // Boxify at the end of the line.
2321       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2322                                             quad );
2323       ++actualNumberOfQuads;
2324
2325       // Update the size of the highlighted text.
2326       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2327     }
2328   }
2329
2330   // Set the actual number of quads.
2331   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2332
2333   // Sets the highlight's size and position. In decorator's coords.
2334   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2335   highLightSize.width = maxHighlightX - minHighlightX;
2336
2337   highLightPosition.x = minHighlightX;
2338   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2339   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2340
2341   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2342
2343   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2344   {
2345     CursorInfo primaryCursorInfo;
2346     GetCursorPosition( mEventData->mLeftSelectionPosition,
2347                        primaryCursorInfo );
2348
2349     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2350
2351     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2352                                          primaryPosition.x,
2353                                          primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2354                                          primaryCursorInfo.lineHeight );
2355
2356     CursorInfo secondaryCursorInfo;
2357     GetCursorPosition( mEventData->mRightSelectionPosition,
2358                        secondaryCursorInfo );
2359
2360     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2361
2362     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2363                                          secondaryPosition.x,
2364                                          secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2365                                          secondaryCursorInfo.lineHeight );
2366   }
2367
2368   // Set the flag to update the decorator.
2369   mEventData->mDecoratorUpdated = true;
2370 }
2371
2372 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2373 {
2374   if( NULL == mEventData )
2375   {
2376     // Nothing to do if there is no text input.
2377     return;
2378   }
2379
2380   if( IsShowingPlaceholderText() )
2381   {
2382     // Nothing to do if there is the place-holder text.
2383     return;
2384   }
2385
2386   const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2387   const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
2388   if( ( 0 == numberOfGlyphs ) ||
2389       ( 0 == numberOfLines ) )
2390   {
2391     // Nothing to do if there is no text.
2392     return;
2393   }
2394
2395   // Find which word was selected
2396   CharacterIndex selectionStart( 0 );
2397   CharacterIndex selectionEnd( 0 );
2398   CharacterIndex noTextHitIndex( 0 );
2399   const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2400                                                   mModel->mLogicalModel,
2401                                                   mMetrics,
2402                                                   visualX,
2403                                                   visualY,
2404                                                   selectionStart,
2405                                                   selectionEnd,
2406                                                   noTextHitIndex );
2407   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2408
2409   if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2410   {
2411     ChangeState( EventData::SELECTING );
2412
2413     mEventData->mLeftSelectionPosition = selectionStart;
2414     mEventData->mRightSelectionPosition = selectionEnd;
2415
2416     mEventData->mUpdateLeftSelectionPosition = true;
2417     mEventData->mUpdateRightSelectionPosition = true;
2418     mEventData->mUpdateHighlightBox = true;
2419
2420     // It may happen an InputMethodContext commit event arrives before the selection event
2421     // if the InputMethodContext is in pre-edit state. The commit event will set the
2422     // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2423     // to false, the highlight box won't be updated.
2424     mEventData->mUpdateCursorPosition = false;
2425
2426     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2427
2428     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2429     mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2430   }
2431   else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2432   {
2433     // Nothing to select. i.e. a white space, out of bounds
2434     ChangeState( EventData::EDITING_WITH_POPUP );
2435
2436     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2437
2438     mEventData->mUpdateCursorPosition = true;
2439     mEventData->mUpdateGrabHandlePosition = true;
2440     mEventData->mScrollAfterUpdatePosition = true;
2441     mEventData->mUpdateInputStyle = true;
2442   }
2443   else if( Controller::NoTextTap::NO_ACTION == action )
2444   {
2445     // Nothing to select. i.e. a white space, out of bounds
2446     mEventData->mPrimaryCursorPosition = noTextHitIndex;
2447
2448     mEventData->mUpdateCursorPosition = true;
2449     mEventData->mUpdateGrabHandlePosition = true;
2450     mEventData->mScrollAfterUpdatePosition = true;
2451     mEventData->mUpdateInputStyle = true;
2452   }
2453 }
2454
2455 void Controller::Impl::SetPopupButtons()
2456 {
2457   /**
2458    *  Sets the Popup buttons to be shown depending on State.
2459    *
2460    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2461    *
2462    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2463    */
2464
2465   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2466
2467   if( EventData::SELECTING == mEventData->mState )
2468   {
2469     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2470
2471     if( !IsClipboardEmpty() )
2472     {
2473       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2474       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2475     }
2476
2477     if( !mEventData->mAllTextSelected )
2478     {
2479       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2480     }
2481   }
2482   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2483   {
2484     if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2485     {
2486       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2487     }
2488
2489     if( !IsClipboardEmpty() )
2490     {
2491       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2492       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2493     }
2494   }
2495   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2496   {
2497     if ( !IsClipboardEmpty() )
2498     {
2499       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2500       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2501     }
2502   }
2503
2504   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2505 }
2506
2507 void Controller::Impl::ChangeState( EventData::State newState )
2508 {
2509   if( NULL == mEventData )
2510   {
2511     // Nothing to do if there is no text input.
2512     return;
2513   }
2514
2515   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2516
2517   if( mEventData->mState != newState )
2518   {
2519     mEventData->mPreviousState = mEventData->mState;
2520     mEventData->mState = newState;
2521
2522     switch( mEventData->mState )
2523     {
2524       case EventData::INACTIVE:
2525       {
2526         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2527         mEventData->mDecorator->StopCursorBlink();
2528         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2529         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2530         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2531         mEventData->mDecorator->SetHighlightActive( false );
2532         mEventData->mDecorator->SetPopupActive( false );
2533         mEventData->mDecoratorUpdated = true;
2534         break;
2535       }
2536       case EventData::INTERRUPTED:
2537       {
2538         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2539         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2540         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2541         mEventData->mDecorator->SetHighlightActive( false );
2542         mEventData->mDecorator->SetPopupActive( false );
2543         mEventData->mDecoratorUpdated = true;
2544         break;
2545       }
2546       case EventData::SELECTING:
2547       {
2548         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2549         mEventData->mDecorator->StopCursorBlink();
2550         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2551         if ( mEventData->mGrabHandleEnabled )
2552         {
2553           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2554           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2555         }
2556         mEventData->mDecorator->SetHighlightActive( true );
2557         if( mEventData->mGrabHandlePopupEnabled )
2558         {
2559           SetPopupButtons();
2560           mEventData->mDecorator->SetPopupActive( true );
2561         }
2562         mEventData->mDecoratorUpdated = true;
2563         break;
2564       }
2565       case EventData::EDITING:
2566       {
2567         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2568         if( mEventData->mCursorBlinkEnabled )
2569         {
2570           mEventData->mDecorator->StartCursorBlink();
2571         }
2572         // Grab handle is not shown until a tap is received whilst EDITING
2573         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2574         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2575         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2576         mEventData->mDecorator->SetHighlightActive( false );
2577         if( mEventData->mGrabHandlePopupEnabled )
2578         {
2579           mEventData->mDecorator->SetPopupActive( false );
2580         }
2581         mEventData->mDecoratorUpdated = true;
2582         break;
2583       }
2584       case EventData::EDITING_WITH_POPUP:
2585       {
2586         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2587
2588         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2589         if( mEventData->mCursorBlinkEnabled )
2590         {
2591           mEventData->mDecorator->StartCursorBlink();
2592         }
2593         if( mEventData->mSelectionEnabled )
2594         {
2595           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2596           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2597           mEventData->mDecorator->SetHighlightActive( false );
2598         }
2599         else if ( mEventData->mGrabHandleEnabled )
2600         {
2601           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2602         }
2603         if( mEventData->mGrabHandlePopupEnabled )
2604         {
2605           SetPopupButtons();
2606           mEventData->mDecorator->SetPopupActive( true );
2607         }
2608         mEventData->mDecoratorUpdated = true;
2609         break;
2610       }
2611       case EventData::EDITING_WITH_GRAB_HANDLE:
2612       {
2613         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2614
2615         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2616         if( mEventData->mCursorBlinkEnabled )
2617         {
2618           mEventData->mDecorator->StartCursorBlink();
2619         }
2620         // Grab handle is not shown until a tap is received whilst EDITING
2621         if ( mEventData->mGrabHandleEnabled )
2622         {
2623           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2624         }
2625         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2626         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2627         mEventData->mDecorator->SetHighlightActive( false );
2628         if( mEventData->mGrabHandlePopupEnabled )
2629         {
2630           mEventData->mDecorator->SetPopupActive( false );
2631         }
2632         mEventData->mDecoratorUpdated = true;
2633         break;
2634       }
2635       case EventData::SELECTION_HANDLE_PANNING:
2636       {
2637         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2638         mEventData->mDecorator->StopCursorBlink();
2639         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2640         if ( mEventData->mGrabHandleEnabled )
2641         {
2642           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2643           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2644         }
2645         mEventData->mDecorator->SetHighlightActive( true );
2646         if( mEventData->mGrabHandlePopupEnabled )
2647         {
2648           mEventData->mDecorator->SetPopupActive( false );
2649         }
2650         mEventData->mDecoratorUpdated = true;
2651         break;
2652       }
2653       case EventData::GRAB_HANDLE_PANNING:
2654       {
2655         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2656
2657         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2658         if( mEventData->mCursorBlinkEnabled )
2659         {
2660           mEventData->mDecorator->StartCursorBlink();
2661         }
2662         if ( mEventData->mGrabHandleEnabled )
2663         {
2664           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2665         }
2666         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2667         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2668         mEventData->mDecorator->SetHighlightActive( false );
2669         if( mEventData->mGrabHandlePopupEnabled )
2670         {
2671           mEventData->mDecorator->SetPopupActive( false );
2672         }
2673         mEventData->mDecoratorUpdated = true;
2674         break;
2675       }
2676       case EventData::EDITING_WITH_PASTE_POPUP:
2677       {
2678         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2679
2680         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2681         if( mEventData->mCursorBlinkEnabled )
2682         {
2683           mEventData->mDecorator->StartCursorBlink();
2684         }
2685
2686         if ( mEventData->mGrabHandleEnabled )
2687         {
2688           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2689         }
2690         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2691         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2692         mEventData->mDecorator->SetHighlightActive( false );
2693
2694         if( mEventData->mGrabHandlePopupEnabled )
2695         {
2696           SetPopupButtons();
2697           mEventData->mDecorator->SetPopupActive( true );
2698         }
2699         mEventData->mDecoratorUpdated = true;
2700         break;
2701       }
2702       case EventData::TEXT_PANNING:
2703       {
2704         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2705         mEventData->mDecorator->StopCursorBlink();
2706         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2707         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2708             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2709         {
2710           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2711           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2712           mEventData->mDecorator->SetHighlightActive( true );
2713         }
2714
2715         if( mEventData->mGrabHandlePopupEnabled )
2716         {
2717           mEventData->mDecorator->SetPopupActive( false );
2718         }
2719
2720         mEventData->mDecoratorUpdated = true;
2721         break;
2722       }
2723     }
2724   }
2725 }
2726
2727 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2728                                           CursorInfo& cursorInfo )
2729 {
2730   if( !IsShowingRealText() )
2731   {
2732     // Do not want to use the place-holder text to set the cursor position.
2733
2734     // Use the line's height of the font's family set to set the cursor's size.
2735     // If there is no font's family set, use the default font.
2736     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2737
2738     cursorInfo.lineOffset = 0.f;
2739     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2740     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2741
2742     bool isRTL = false;
2743     if( mModel->mMatchSystemLanguageDirection )
2744     {
2745       isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2746     }
2747
2748     switch( mModel->mHorizontalAlignment )
2749     {
2750       case Text::HorizontalAlignment::BEGIN :
2751       {
2752         if( isRTL )
2753         {
2754           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2755         }
2756         else
2757         {
2758           cursorInfo.primaryPosition.x = 0.f;
2759         }
2760         break;
2761       }
2762       case Text::HorizontalAlignment::CENTER:
2763       {
2764         cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2765         break;
2766       }
2767       case Text::HorizontalAlignment::END:
2768       {
2769         if( isRTL )
2770         {
2771           cursorInfo.primaryPosition.x = 0.f;
2772         }
2773         else
2774         {
2775           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2776         }
2777         break;
2778       }
2779     }
2780
2781     // Nothing else to do.
2782     return;
2783   }
2784
2785   const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2786   GetCursorPositionParameters parameters;
2787   parameters.visualModel = mModel->mVisualModel;
2788   parameters.logicalModel = mModel->mLogicalModel;
2789   parameters.metrics = mMetrics;
2790   parameters.logical = logical;
2791   parameters.isMultiline = isMultiLine;
2792
2793   Text::GetCursorPosition( parameters,
2794                            cursorInfo );
2795
2796   // Adds Outline offset.
2797   const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2798   cursorInfo.primaryPosition.x += outlineWidth;
2799   cursorInfo.primaryPosition.y += outlineWidth;
2800   cursorInfo.secondaryPosition.x += outlineWidth;
2801   cursorInfo.secondaryPosition.y += outlineWidth;
2802
2803   if( isMultiLine )
2804   {
2805     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2806
2807     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2808     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2809
2810     if( 0.f > cursorInfo.primaryPosition.x )
2811     {
2812       cursorInfo.primaryPosition.x = 0.f;
2813     }
2814
2815     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2816     if( cursorInfo.primaryPosition.x > edgeWidth )
2817     {
2818       cursorInfo.primaryPosition.x = edgeWidth;
2819     }
2820   }
2821 }
2822
2823 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2824 {
2825   if( NULL == mEventData )
2826   {
2827     // Nothing to do if there is no text input.
2828     return 0u;
2829   }
2830
2831   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2832
2833   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2834   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2835
2836   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2837   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2838
2839   if( numberOfCharacters > 1u )
2840   {
2841     const Script script = mModel->mLogicalModel->GetScript( index );
2842     if( HasLigatureMustBreak( script ) )
2843     {
2844       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2845       numberOfCharacters = 1u;
2846     }
2847   }
2848   else
2849   {
2850     while( 0u == numberOfCharacters )
2851     {
2852       ++glyphIndex;
2853       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2854     }
2855   }
2856
2857   if( index < mEventData->mPrimaryCursorPosition )
2858   {
2859     cursorIndex -= numberOfCharacters;
2860   }
2861   else
2862   {
2863     cursorIndex += numberOfCharacters;
2864   }
2865
2866   // Will update the cursor hook position.
2867   mEventData->mUpdateCursorHookPosition = true;
2868
2869   return cursorIndex;
2870 }
2871
2872 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2873 {
2874   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2875   if( NULL == mEventData )
2876   {
2877     // Nothing to do if there is no text input.
2878     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2879     return;
2880   }
2881
2882   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2883
2884   mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2885
2886   // Sets the cursor position.
2887   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2888                                        cursorPosition.x,
2889                                        cursorPosition.y,
2890                                        cursorInfo.primaryCursorHeight,
2891                                        cursorInfo.lineHeight );
2892   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2893
2894   if( mEventData->mUpdateGrabHandlePosition )
2895   {
2896     // Sets the grab handle position.
2897     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2898                                          cursorPosition.x,
2899                                          cursorInfo.lineOffset + mModel->mScrollPosition.y,
2900                                          cursorInfo.lineHeight );
2901   }
2902
2903   if( cursorInfo.isSecondaryCursor )
2904   {
2905     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2906                                          cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2907                                          cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2908                                          cursorInfo.secondaryCursorHeight,
2909                                          cursorInfo.lineHeight );
2910     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2911   }
2912
2913   // Set which cursors are active according the state.
2914   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2915   {
2916     if( cursorInfo.isSecondaryCursor )
2917     {
2918       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2919     }
2920     else
2921     {
2922       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2923     }
2924   }
2925   else
2926   {
2927     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2928   }
2929
2930   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2931 }
2932
2933 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2934                                               const CursorInfo& cursorInfo )
2935 {
2936   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2937       ( RIGHT_SELECTION_HANDLE != handleType ) )
2938   {
2939     return;
2940   }
2941
2942   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2943
2944   // Sets the handle's position.
2945   mEventData->mDecorator->SetPosition( handleType,
2946                                        cursorPosition.x,
2947                                        cursorInfo.lineOffset + mModel->mScrollPosition.y,
2948                                        cursorInfo.lineHeight );
2949
2950   // If selection handle at start of the text and other at end of the text then all text is selected.
2951   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2952   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2953   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2954 }
2955
2956 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2957 {
2958   // Clamp between -space & -alignment offset.
2959
2960   if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2961   {
2962     const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2963     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2964     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2965
2966     mEventData->mDecoratorUpdated = true;
2967   }
2968   else
2969   {
2970     mModel->mScrollPosition.x = 0.f;
2971   }
2972 }
2973
2974 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2975 {
2976   if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2977   {
2978     // Nothing to do if the text is single line.
2979     return;
2980   }
2981
2982   // Clamp between -space & 0.
2983   if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2984   {
2985     const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2986     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2987     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2988
2989     mEventData->mDecoratorUpdated = true;
2990   }
2991   else
2992   {
2993     mModel->mScrollPosition.y = 0.f;
2994   }
2995 }
2996
2997 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2998 {
2999   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3000
3001   // position is in actor's coords.
3002   const float positionEndX = position.x + cursorWidth;
3003   const float positionEndY = position.y + lineHeight;
3004
3005   // Transform the position to decorator coords.
3006   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3007   const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3008
3009   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3010   const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3011
3012   if( decoratorPositionBeginX < 0.f )
3013   {
3014     mModel->mScrollPosition.x = -position.x;
3015   }
3016   else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3017   {
3018     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3019   }
3020
3021   if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3022   {
3023     if( decoratorPositionBeginY < 0.f )
3024     {
3025       mModel->mScrollPosition.y = -position.y;
3026     }
3027     else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3028     {
3029       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3030     }
3031   }
3032 }
3033
3034 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3035 {
3036   // Get the current cursor position in decorator coords.
3037   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3038
3039   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition  );
3040
3041
3042
3043   // Calculate the offset to match the cursor position before the character was deleted.
3044   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3045
3046   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3047   if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3048   {
3049     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3050     mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3051   }
3052
3053
3054   ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3055   ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3056
3057   // Makes the new cursor position visible if needed.
3058   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3059 }
3060
3061 void Controller::Impl::RequestRelayout()
3062 {
3063   if( NULL != mControlInterface )
3064   {
3065     mControlInterface->RequestTextRelayout();
3066   }
3067 }
3068
3069 } // namespace Text
3070
3071 } // namespace Toolkit
3072
3073 } // namespace Dali