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