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