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