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