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