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