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