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