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