Merge "Remove Geometry::QUAD() usage in Toolkit" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 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-run-container.h>
35
36 namespace
37 {
38
39 /**
40  * @brief Struct used to calculate the selection box.
41  */
42 struct SelectionBoxInfo
43 {
44   float lineOffset;
45   float lineHeight;
46   float minX;
47   float maxX;
48 };
49
50 #if defined(DEBUG_ENABLED)
51   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
52 #endif
53
54 const float MAX_FLOAT = std::numeric_limits<float>::max();
55 const float MIN_FLOAT = std::numeric_limits<float>::min();
56 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
57
58 } // namespace
59
60 namespace Dali
61 {
62
63 namespace Toolkit
64 {
65
66 namespace Text
67 {
68
69 EventData::EventData( DecoratorPtr decorator )
70 : mDecorator( decorator ),
71   mImfManager(),
72   mPlaceholderTextActive(),
73   mPlaceholderTextInactive(),
74   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
75   mEventQueue(),
76   mState( INACTIVE ),
77   mPrimaryCursorPosition( 0u ),
78   mLeftSelectionPosition( 0u ),
79   mRightSelectionPosition( 0u ),
80   mPreEditStartPosition( 0u ),
81   mPreEditLength( 0u ),
82   mIsShowingPlaceholderText( false ),
83   mPreEditFlag( false ),
84   mDecoratorUpdated( false ),
85   mCursorBlinkEnabled( true ),
86   mGrabHandleEnabled( true ),
87   mGrabHandlePopupEnabled( true ),
88   mSelectionEnabled( true ),
89   mHorizontalScrollingEnabled( true ),
90   mVerticalScrollingEnabled( false ),
91   mUpdateCursorPosition( false ),
92   mUpdateLeftSelectionPosition( false ),
93   mUpdateRightSelectionPosition( false ),
94   mScrollAfterUpdatePosition( false ),
95   mScrollAfterDelete( false ),
96   mAllTextSelected( false ),
97   mUpdateInputStyle( false )
98 {
99   mImfManager = ImfManager::Get();
100 }
101
102 EventData::~EventData()
103 {}
104
105 bool Controller::Impl::ProcessInputEvents()
106 {
107   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
108   if( NULL == mEventData )
109   {
110     // Nothing to do if there is no text input.
111     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
112     return false;
113   }
114
115   if( mEventData->mDecorator )
116   {
117     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
118          iter != mEventData->mEventQueue.end();
119          ++iter )
120     {
121       switch( iter->type )
122       {
123         case Event::CURSOR_KEY_EVENT:
124         {
125           OnCursorKeyEvent( *iter );
126           break;
127         }
128         case Event::TAP_EVENT:
129         {
130           OnTapEvent( *iter );
131           break;
132         }
133         case Event::LONG_PRESS_EVENT:
134         {
135           OnLongPressEvent( *iter );
136           break;
137         }
138         case Event::PAN_EVENT:
139         {
140           OnPanEvent( *iter );
141           break;
142         }
143         case Event::GRAB_HANDLE_EVENT:
144         case Event::LEFT_SELECTION_HANDLE_EVENT:
145         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
146         {
147           OnHandleEvent( *iter );
148           break;
149         }
150         case Event::SELECT:
151         {
152           OnSelectEvent( *iter );
153           break;
154         }
155         case Event::SELECT_ALL:
156         {
157           OnSelectAllEvent();
158           break;
159         }
160       }
161     }
162   }
163
164   if( mEventData->mUpdateCursorPosition ||
165       mEventData->mUpdateLeftSelectionPosition ||
166       mEventData->mUpdateRightSelectionPosition )
167   {
168     NotifyImfManager();
169   }
170
171   // The cursor must also be repositioned after inserts into the model
172   if( mEventData->mUpdateCursorPosition )
173   {
174     // Updates the cursor position and scrolls the text to make it visible.
175     CursorInfo cursorInfo;
176     GetCursorPosition( mEventData->mPrimaryCursorPosition,
177                        cursorInfo );
178
179     // Scroll first the text after delete ...
180     if( mEventData->mScrollAfterDelete )
181     {
182       ScrollTextToMatchCursor( cursorInfo );
183     }
184
185     // ... then, text can be scrolled to make the cursor visible.
186     if( mEventData->mScrollAfterUpdatePosition )
187     {
188       ScrollToMakePositionVisible( cursorInfo.primaryPosition );
189     }
190     mEventData->mScrollAfterUpdatePosition = false;
191     mEventData->mScrollAfterDelete = false;
192
193     UpdateCursorPosition( cursorInfo );
194
195     mEventData->mDecoratorUpdated = true;
196     mEventData->mUpdateCursorPosition = false;
197   }
198   else
199   {
200     bool leftScroll = false;
201     bool rightScroll = false;
202
203     CursorInfo leftHandleInfo;
204     CursorInfo rightHandleInfo;
205
206     if( mEventData->mUpdateLeftSelectionPosition )
207     {
208       GetCursorPosition( mEventData->mLeftSelectionPosition,
209                          leftHandleInfo );
210
211       if( mEventData->mScrollAfterUpdatePosition )
212       {
213         ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
214         leftScroll = true;
215       }
216     }
217
218     if( mEventData->mUpdateRightSelectionPosition )
219     {
220       GetCursorPosition( mEventData->mRightSelectionPosition,
221                          rightHandleInfo );
222
223       if( mEventData->mScrollAfterUpdatePosition )
224       {
225         ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
226         rightScroll = true;
227       }
228     }
229
230     if( mEventData->mUpdateLeftSelectionPosition )
231     {
232       UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
233                              leftHandleInfo );
234
235       SetPopupButtons();
236       mEventData->mDecoratorUpdated = true;
237     }
238
239     if( mEventData->mUpdateRightSelectionPosition )
240     {
241       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
242                              rightHandleInfo );
243
244       SetPopupButtons();
245       mEventData->mDecoratorUpdated = true;
246     }
247
248     if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
249     {
250       RepositionSelectionHandles();
251
252       mEventData->mUpdateLeftSelectionPosition = false;
253       mEventData->mUpdateRightSelectionPosition = false;
254     }
255
256     if( leftScroll || rightScroll )
257     {
258       mEventData->mScrollAfterUpdatePosition = false;
259     }
260   }
261
262   if( mEventData->mUpdateInputStyle )
263   {
264     // Set the default style first.
265     RetrieveDefaultInputStyle( mEventData->mInputStyle );
266
267     // Get the character index from the cursor index.
268     const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
269
270     // Retrieve the style from the style runs stored in the logical model.
271     mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
272
273     mEventData->mUpdateInputStyle = false;
274   }
275
276   mEventData->mEventQueue.clear();
277
278   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
279
280   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
281   mEventData->mDecoratorUpdated = false;
282
283   return decoratorUpdated;
284 }
285
286 void Controller::Impl::NotifyImfManager()
287 {
288   if( mEventData && mEventData->mImfManager )
289   {
290     CharacterIndex cursorPosition = GetLogicalCursorPosition();
291
292     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
293
294     // Update the cursor position by removing the initial white spaces.
295     if( cursorPosition < numberOfWhiteSpaces )
296     {
297       cursorPosition = 0u;
298     }
299     else
300     {
301       cursorPosition -= numberOfWhiteSpaces;
302     }
303
304     mEventData->mImfManager.SetCursorPosition( cursorPosition );
305     mEventData->mImfManager.NotifyCursorPosition();
306   }
307 }
308
309 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
310 {
311   CharacterIndex cursorPosition = 0u;
312
313   if( mEventData )
314   {
315     if( ( EventData::SELECTING == mEventData->mState ) ||
316         ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
317     {
318       cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
319     }
320     else
321     {
322       cursorPosition = mEventData->mPrimaryCursorPosition;
323     }
324   }
325
326   return cursorPosition;
327 }
328
329 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
330 {
331   Length numberOfWhiteSpaces = 0u;
332
333   // Get the buffer to the text.
334   Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
335
336   const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
337   for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
338   {
339     if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
340     {
341       break;
342     }
343   }
344
345   return numberOfWhiteSpaces;
346 }
347
348 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
349 {
350   // Get the total number of characters.
351   Length numberOfCharacters = mLogicalModel->mText.Count();
352
353   // Retrieve the text.
354   if( 0u != numberOfCharacters )
355   {
356     Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
357   }
358 }
359
360 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
361 {
362   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
363   mTextUpdateInfo.mStartGlyphIndex = 0u;
364   mTextUpdateInfo.mStartLineIndex = 0u;
365   numberOfCharacters = 0u;
366
367   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
368   if( 0u == numberOfParagraphs )
369   {
370     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
371     numberOfCharacters = 0u;
372
373     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
374
375     // Nothing else to do if there are no paragraphs.
376     return;
377   }
378
379   // Find the paragraphs to be updated.
380   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
381   if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
382   {
383     // Text is being added at the end of the current text.
384     if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
385     {
386       // Text is being added in a new paragraph after the last character of the text.
387       mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
388       numberOfCharacters = 0u;
389       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
390
391       mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
392       mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
393
394       // Nothing else to do;
395       return;
396     }
397
398     paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
399   }
400   else
401   {
402     Length numberOfCharactersToUpdate = 0u;
403     if( mTextUpdateInfo.mFullRelayoutNeeded )
404     {
405       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
406     }
407     else
408     {
409       numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
410     }
411     mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
412                                    numberOfCharactersToUpdate,
413                                    paragraphsToBeUpdated );
414   }
415
416   if( 0u != paragraphsToBeUpdated.Count() )
417   {
418     const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
419     const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
420     mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
421
422     ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
423     const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
424
425     if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) &&                                            // Some character are removed.
426         ( lastParagraphIndex < numberOfParagraphs - 1u ) &&                                                // There is a next paragraph.
427         ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
428           ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
429     {
430       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
431       const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
432
433       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
434     }
435     else
436     {
437       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
438     }
439   }
440
441   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
442   mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
443 }
444
445 void Controller::Impl::ClearFullModelData( OperationsMask operations )
446 {
447   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
448   {
449     mLogicalModel->mLineBreakInfo.Clear();
450     mLogicalModel->mParagraphInfo.Clear();
451   }
452
453   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
454   {
455     mLogicalModel->mLineBreakInfo.Clear();
456   }
457
458   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
459   {
460     mLogicalModel->mScriptRuns.Clear();
461   }
462
463   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
464   {
465     mLogicalModel->mFontRuns.Clear();
466   }
467
468   if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
469   {
470     if( NO_OPERATION != ( BIDI_INFO & operations ) )
471     {
472       mLogicalModel->mBidirectionalParagraphInfo.Clear();
473       mLogicalModel->mCharacterDirections.Clear();
474     }
475
476     if( NO_OPERATION != ( REORDER & operations ) )
477     {
478       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
479       for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
480              endIt = mLogicalModel->mBidirectionalLineInfo.End();
481            it != endIt;
482            ++it )
483       {
484         BidirectionalLineInfoRun& bidiLineInfo = *it;
485
486         free( bidiLineInfo.visualToLogicalMap );
487         bidiLineInfo.visualToLogicalMap = NULL;
488       }
489       mLogicalModel->mBidirectionalLineInfo.Clear();
490     }
491   }
492
493   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
494   {
495     mVisualModel->mGlyphs.Clear();
496     mVisualModel->mGlyphsToCharacters.Clear();
497     mVisualModel->mCharactersToGlyph.Clear();
498     mVisualModel->mCharactersPerGlyph.Clear();
499     mVisualModel->mGlyphsPerCharacter.Clear();
500     mVisualModel->mGlyphPositions.Clear();
501   }
502
503   if( NO_OPERATION != ( LAYOUT & operations ) )
504   {
505     mVisualModel->mLines.Clear();
506   }
507
508   if( NO_OPERATION != ( COLOR & operations ) )
509   {
510     mVisualModel->mColorIndices.Clear();
511   }
512 }
513
514 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
515 {
516   const CharacterIndex endIndexPlusOne = endIndex + 1u;
517
518   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
519   {
520     // Clear the line break info.
521     LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
522
523     mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
524                                          lineBreakInfoBuffer + endIndexPlusOne );
525
526     // Clear the paragraphs.
527     ClearCharacterRuns( startIndex,
528                         endIndex,
529                         mLogicalModel->mParagraphInfo );
530   }
531
532   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
533   {
534     // Clear the word break info.
535     WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
536
537     mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
538                                          wordBreakInfoBuffer + endIndexPlusOne );
539   }
540
541   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
542   {
543     // Clear the scripts.
544     ClearCharacterRuns( startIndex,
545                         endIndex,
546                         mLogicalModel->mScriptRuns );
547   }
548
549   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
550   {
551     // Clear the fonts.
552     ClearCharacterRuns( startIndex,
553                         endIndex,
554                         mLogicalModel->mFontRuns );
555   }
556
557   if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
558   {
559     if( NO_OPERATION != ( BIDI_INFO & operations ) )
560     {
561       // Clear the bidirectional paragraph info.
562       ClearCharacterRuns( startIndex,
563                           endIndex,
564                           mLogicalModel->mBidirectionalParagraphInfo );
565
566       // Clear the character's directions.
567       CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
568
569       mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
570                                                  characterDirectionsBuffer + endIndexPlusOne );
571     }
572
573     if( NO_OPERATION != ( REORDER & operations ) )
574     {
575       uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
576       uint32_t endRemoveIndex = startRemoveIndex;
577       ClearCharacterRuns( startIndex,
578                           endIndex,
579                           mLogicalModel->mBidirectionalLineInfo,
580                           startRemoveIndex,
581                           endRemoveIndex );
582
583       BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
584
585       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
586       for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
587              endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
588            it != endIt;
589            ++it )
590       {
591         BidirectionalLineInfoRun& bidiLineInfo = *it;
592
593         free( bidiLineInfo.visualToLogicalMap );
594         bidiLineInfo.visualToLogicalMap = NULL;
595       }
596
597       mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
598                                                    bidirectionalLineInfoBuffer + endRemoveIndex );
599     }
600   }
601 }
602
603 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
604 {
605   const CharacterIndex endIndexPlusOne = endIndex + 1u;
606   const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
607
608   // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
609   GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
610   Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
611
612   const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
613   const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
614
615   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
616   {
617     // Update the character to glyph indices.
618     for( Vector<GlyphIndex>::Iterator it =  charactersToGlyphBuffer + endIndexPlusOne,
619            endIt =  charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
620          it != endIt;
621          ++it )
622     {
623       CharacterIndex& index = *it;
624       index -= numberOfGlyphsRemoved;
625     }
626
627     // Clear the character to glyph conversion table.
628     mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
629                                             charactersToGlyphBuffer + endIndexPlusOne );
630
631     // Clear the glyphs per character table.
632     mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
633                                              glyphsPerCharacterBuffer + endIndexPlusOne );
634
635     // Clear the glyphs buffer.
636     GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
637     mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
638                                  glyphsBuffer + endGlyphIndexPlusOne );
639
640     CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
641
642     // Update the glyph to character indices.
643     for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
644            endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
645          it != endIt;
646          ++it )
647     {
648       CharacterIndex& index = *it;
649       index -= numberOfCharactersRemoved;
650     }
651
652     // Clear the glyphs to characters buffer.
653     mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
654                                              glyphsToCharactersBuffer  + endGlyphIndexPlusOne );
655
656     // Clear the characters per glyph buffer.
657     Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
658     mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
659                                              charactersPerGlyphBuffer + endGlyphIndexPlusOne );
660
661     // Clear the positions buffer.
662     Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
663     mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
664                                          positionsBuffer + endGlyphIndexPlusOne );
665   }
666
667   if( NO_OPERATION != ( LAYOUT & operations ) )
668   {
669     // Clear the lines.
670     uint32_t startRemoveIndex = mVisualModel->mLines.Count();
671     uint32_t endRemoveIndex = startRemoveIndex;
672     ClearCharacterRuns( startIndex,
673                         endIndex,
674                         mVisualModel->mLines,
675                         startRemoveIndex,
676                         endRemoveIndex );
677
678     // Will update the glyph runs.
679     startRemoveIndex = mVisualModel->mLines.Count();
680     endRemoveIndex = startRemoveIndex;
681     ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
682                     endGlyphIndexPlusOne - 1u,
683                     mVisualModel->mLines,
684                     startRemoveIndex,
685                     endRemoveIndex );
686
687     // Set the line index from where to insert the new laid-out lines.
688     mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
689
690     LineRun* linesBuffer = mVisualModel->mLines.Begin();
691     mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
692                                 linesBuffer + endRemoveIndex );
693   }
694
695   if( NO_OPERATION != ( COLOR & operations ) )
696   {
697     if( 0u != mVisualModel->mColorIndices.Count() )
698     {
699       ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
700       mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
701                                          colorIndexBuffer + endGlyphIndexPlusOne );
702     }
703   }
704 }
705
706 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
707 {
708   if( mTextUpdateInfo.mClearAll ||
709       ( ( 0u == startIndex ) &&
710         ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
711   {
712     ClearFullModelData( operations );
713   }
714   else
715   {
716     // Clear the model data related with characters.
717     ClearCharacterModelData( startIndex, endIndex, operations );
718
719     // Clear the model data related with glyphs.
720     ClearGlyphModelData( startIndex, endIndex, operations );
721   }
722
723   // The estimated number of lines. Used to avoid reallocations when layouting.
724   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
725
726   mVisualModel->ClearCaches();
727 }
728
729 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
730 {
731   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
732
733   // Calculate the operations to be done.
734   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
735
736   if( NO_OPERATION == operations )
737   {
738     // Nothing to do if no operations are pending and required.
739     return false;
740   }
741
742   Vector<Character>& utf32Characters = mLogicalModel->mText;
743
744   const Length numberOfCharacters = utf32Characters.Count();
745
746   // Index to the first character of the first paragraph to be updated.
747   CharacterIndex startIndex = 0u;
748   // Number of characters of the paragraphs to be removed.
749   Length paragraphCharacters = 0u;
750
751   CalculateTextUpdateIndices( paragraphCharacters );
752   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
753
754   if( mTextUpdateInfo.mClearAll ||
755       ( 0u != paragraphCharacters ) )
756   {
757     ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
758   }
759
760   mTextUpdateInfo.mClearAll = false;
761
762   // Whether the model is updated.
763   bool updated = false;
764
765   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
766   const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
767
768   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
769   {
770     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
771     // calculate the bidirectional info for each 'paragraph'.
772     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
773     // is not shaped together).
774     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
775
776     SetLineBreakInfo( utf32Characters,
777                       startIndex,
778                       requestedNumberOfCharacters,
779                       lineBreakInfo );
780
781     // Create the paragraph info.
782     mLogicalModel->CreateParagraphInfo( startIndex,
783                                         requestedNumberOfCharacters );
784     updated = true;
785   }
786
787   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
788   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
789   {
790     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
791     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
792
793     SetWordBreakInfo( utf32Characters,
794                       startIndex,
795                       requestedNumberOfCharacters,
796                       wordBreakInfo );
797     updated = true;
798   }
799
800   const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
801   const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
802
803   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
804   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
805
806   if( getScripts || validateFonts )
807   {
808     // Validates the fonts assigned by the application or assigns default ones.
809     // It makes sure all the characters are going to be rendered by the correct font.
810     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
811
812     if( getScripts )
813     {
814       // Retrieves the scripts used in the text.
815       multilanguageSupport.SetScripts( utf32Characters,
816                                        startIndex,
817                                        requestedNumberOfCharacters,
818                                        scripts );
819     }
820
821     if( validateFonts )
822     {
823       // Validate the fonts set through the mark-up string.
824       Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
825
826       // Get the default font id.
827       const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
828
829       // Validates the fonts. If there is a character with no assigned font it sets a default one.
830       // After this call, fonts are validated.
831       multilanguageSupport.ValidateFonts( utf32Characters,
832                                           scripts,
833                                           fontDescriptionRuns,
834                                           defaultFontId,
835                                           startIndex,
836                                           requestedNumberOfCharacters,
837                                           validFonts );
838     }
839     updated = true;
840   }
841
842   Vector<Character> mirroredUtf32Characters;
843   bool textMirrored = false;
844   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
845   if( NO_OPERATION != ( BIDI_INFO & operations ) )
846   {
847     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
848     bidirectionalInfo.Reserve( numberOfParagraphs );
849
850     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
851     SetBidirectionalInfo( utf32Characters,
852                           scripts,
853                           lineBreakInfo,
854                           startIndex,
855                           requestedNumberOfCharacters,
856                           bidirectionalInfo );
857
858     if( 0u != bidirectionalInfo.Count() )
859     {
860       // Only set the character directions if there is right to left characters.
861       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
862       GetCharactersDirection( bidirectionalInfo,
863                               numberOfCharacters,
864                               startIndex,
865                               requestedNumberOfCharacters,
866                               directions );
867
868       // This paragraph has right to left text. Some characters may need to be mirrored.
869       // TODO: consider if the mirrored string can be stored as well.
870
871       textMirrored = GetMirroredText( utf32Characters,
872                                       directions,
873                                       bidirectionalInfo,
874                                       startIndex,
875                                       requestedNumberOfCharacters,
876                                       mirroredUtf32Characters );
877     }
878     else
879     {
880       // There is no right to left characters. Clear the directions vector.
881       mLogicalModel->mCharacterDirections.Clear();
882     }
883     updated = true;
884   }
885
886   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
887   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
888   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
889   Vector<GlyphIndex> newParagraphGlyphs;
890   newParagraphGlyphs.Reserve( numberOfParagraphs );
891
892   const Length currentNumberOfGlyphs = glyphs.Count();
893   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
894   {
895     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
896     // Shapes the text.
897     ShapeText( textToShape,
898                lineBreakInfo,
899                scripts,
900                validFonts,
901                startIndex,
902                mTextUpdateInfo.mStartGlyphIndex,
903                requestedNumberOfCharacters,
904                glyphs,
905                glyphsToCharactersMap,
906                charactersPerGlyph,
907                newParagraphGlyphs );
908
909     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
910     mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
911     mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
912     updated = true;
913   }
914
915   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
916
917   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
918   {
919     GlyphInfo* glyphsBuffer = glyphs.Begin();
920     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
921
922     // Update the width and advance of all new paragraph characters.
923     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
924     {
925       const GlyphIndex index = *it;
926       GlyphInfo& glyph = *( glyphsBuffer + index );
927
928       glyph.xBearing = 0.f;
929       glyph.width = 0.f;
930       glyph.advance = 0.f;
931     }
932     updated = true;
933   }
934
935   if( NO_OPERATION != ( COLOR & operations ) )
936   {
937     // Set the color runs in glyphs.
938     SetColorSegmentationInfo( mLogicalModel->mColorRuns,
939                               mVisualModel->mCharactersToGlyph,
940                               mVisualModel->mGlyphsPerCharacter,
941                               startIndex,
942                               mTextUpdateInfo.mStartGlyphIndex,
943                               requestedNumberOfCharacters,
944                               mVisualModel->mColors,
945                               mVisualModel->mColorIndices );
946
947     updated = true;
948   }
949
950   if( ( NULL != mEventData ) &&
951       mEventData->mPreEditFlag &&
952       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
953   {
954     // Add the underline for the pre-edit text.
955     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
956     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
957
958     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
959     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
960     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
961     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
962
963     GlyphRun underlineRun;
964     underlineRun.glyphIndex = glyphStart;
965     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
966
967     // TODO: At the moment the underline runs are only for pre-edit.
968     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
969   }
970
971   // The estimated number of lines. Used to avoid reallocations when layouting.
972   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
973
974   // Set the previous number of characters for the next time the text is updated.
975   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
976
977   return updated;
978 }
979
980 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
981 {
982   // Sets the default text's color.
983   inputStyle.textColor = mTextColor;
984   inputStyle.isDefaultColor = true;
985
986   inputStyle.familyName.clear();
987   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
988   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
989   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
990   inputStyle.size = 0.f;
991
992   inputStyle.familyDefined = false;
993   inputStyle.weightDefined = false;
994   inputStyle.widthDefined = false;
995   inputStyle.slantDefined = false;
996   inputStyle.sizeDefined = false;
997
998   // Sets the default font's family name, weight, width, slant and size.
999   if( mFontDefaults )
1000   {
1001     if( mFontDefaults->familyDefined )
1002     {
1003       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1004       inputStyle.familyDefined = true;
1005     }
1006
1007     if( mFontDefaults->weightDefined )
1008     {
1009       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1010       inputStyle.weightDefined = true;
1011     }
1012
1013     if( mFontDefaults->widthDefined )
1014     {
1015       inputStyle.width = mFontDefaults->mFontDescription.width;
1016       inputStyle.widthDefined = true;
1017     }
1018
1019     if( mFontDefaults->slantDefined )
1020     {
1021       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1022       inputStyle.slantDefined = true;
1023     }
1024
1025     if( mFontDefaults->sizeDefined )
1026     {
1027       inputStyle.size = mFontDefaults->mDefaultPointSize;
1028       inputStyle.sizeDefined = true;
1029     }
1030   }
1031 }
1032
1033 float Controller::Impl::GetDefaultFontLineHeight()
1034 {
1035   FontId defaultFontId = 0u;
1036   if( NULL == mFontDefaults )
1037   {
1038     TextAbstraction::FontDescription fontDescription;
1039     defaultFontId = mFontClient.GetFontId( fontDescription );
1040   }
1041   else
1042   {
1043     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1044   }
1045
1046   Text::FontMetrics fontMetrics;
1047   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1048
1049   return( fontMetrics.ascender - fontMetrics.descender );
1050 }
1051
1052 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1053 {
1054   if( NULL == mEventData )
1055   {
1056     // Nothing to do if there is no text input.
1057     return;
1058   }
1059
1060   int keyCode = event.p1.mInt;
1061
1062   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1063   {
1064     if( mEventData->mPrimaryCursorPosition > 0u )
1065     {
1066       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1067     }
1068   }
1069   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1070   {
1071     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1072     {
1073       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1074     }
1075   }
1076   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1077   {
1078     // TODO
1079   }
1080   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1081   {
1082     // TODO
1083   }
1084
1085   mEventData->mUpdateCursorPosition = true;
1086   mEventData->mUpdateInputStyle = true;
1087   mEventData->mScrollAfterUpdatePosition = true;
1088 }
1089
1090 void Controller::Impl::OnTapEvent( const Event& event )
1091 {
1092   if( NULL != mEventData )
1093   {
1094     const unsigned int tapCount = event.p1.mUint;
1095
1096     if( 1u == tapCount )
1097     {
1098       if( IsShowingRealText() )
1099       {
1100         // Convert from control's coords to text's coords.
1101         const float xPosition = event.p2.mFloat - mScrollPosition.x;
1102         const float yPosition = event.p3.mFloat - mScrollPosition.y;
1103
1104         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1105                                                                           mLogicalModel,
1106                                                                           mMetrics,
1107                                                                           xPosition,
1108                                                                           yPosition );
1109
1110         // When the cursor position is changing, delay cursor blinking
1111         mEventData->mDecorator->DelayCursorBlink();
1112       }
1113       else
1114       {
1115         mEventData->mPrimaryCursorPosition = 0u;
1116       }
1117
1118       mEventData->mUpdateCursorPosition = true;
1119       mEventData->mScrollAfterUpdatePosition = true;
1120       mEventData->mUpdateInputStyle = true;
1121
1122       // Notify the cursor position to the imf manager.
1123       if( mEventData->mImfManager )
1124       {
1125         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1126         mEventData->mImfManager.NotifyCursorPosition();
1127       }
1128     }
1129   }
1130 }
1131
1132 void Controller::Impl::OnPanEvent( const Event& event )
1133 {
1134   if( NULL == mEventData )
1135   {
1136     // Nothing to do if there is no text input.
1137     return;
1138   }
1139
1140   int state = event.p1.mInt;
1141
1142   if( ( Gesture::Started == state ) ||
1143       ( Gesture::Continuing == state ) )
1144   {
1145     const Vector2& actualSize = mVisualModel->GetLayoutSize();
1146     const Vector2 currentScroll = mScrollPosition;
1147
1148     if( mEventData->mHorizontalScrollingEnabled )
1149     {
1150       const float displacementX = event.p2.mFloat;
1151       mScrollPosition.x += displacementX;
1152
1153       ClampHorizontalScroll( actualSize );
1154     }
1155
1156     if( mEventData->mVerticalScrollingEnabled )
1157     {
1158       const float displacementY = event.p3.mFloat;
1159       mScrollPosition.y += displacementY;
1160
1161       ClampVerticalScroll( actualSize );
1162     }
1163
1164     if( mEventData->mDecorator )
1165     {
1166       mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1167     }
1168   }
1169 }
1170
1171 void Controller::Impl::OnLongPressEvent( const Event& event )
1172 {
1173   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1174
1175   if( EventData::EDITING == mEventData->mState )
1176   {
1177     ChangeState ( EventData::EDITING_WITH_POPUP );
1178     mEventData->mDecoratorUpdated = true;
1179   }
1180 }
1181
1182 void Controller::Impl::OnHandleEvent( const Event& event )
1183 {
1184   if( NULL == mEventData )
1185   {
1186     // Nothing to do if there is no text input.
1187     return;
1188   }
1189
1190   const unsigned int state = event.p1.mUint;
1191   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1192
1193   if( HANDLE_PRESSED == state )
1194   {
1195     // Convert from decorator's coords to text's coords.
1196     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1197     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1198
1199     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1200                                                                           mLogicalModel,
1201                                                                           mMetrics,
1202                                                                           xPosition,
1203                                                                           yPosition );
1204
1205     if( Event::GRAB_HANDLE_EVENT == event.type )
1206     {
1207       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1208
1209       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1210       {
1211         mEventData->mPrimaryCursorPosition = handleNewPosition;
1212         mEventData->mUpdateCursorPosition = true;
1213       }
1214     }
1215     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1216     {
1217       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1218
1219       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1220           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1221       {
1222         mEventData->mLeftSelectionPosition = handleNewPosition;
1223
1224         mEventData->mUpdateLeftSelectionPosition = true;
1225       }
1226     }
1227     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1228     {
1229       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1230
1231       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1232           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1233       {
1234         mEventData->mRightSelectionPosition = handleNewPosition;
1235
1236         mEventData->mUpdateRightSelectionPosition = true;
1237       }
1238     }
1239   } // end ( HANDLE_PRESSED == state )
1240   else if( ( HANDLE_RELEASED == state ) ||
1241            handleStopScrolling )
1242   {
1243     CharacterIndex handlePosition = 0u;
1244     if( handleStopScrolling )
1245     {
1246       // Convert from decorator's coords to text's coords.
1247       const float xPosition = event.p2.mFloat - mScrollPosition.x;
1248       const float yPosition = event.p3.mFloat - mScrollPosition.y;
1249
1250       handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1251                                                     mLogicalModel,
1252                                                     mMetrics,
1253                                                     xPosition,
1254                                                     yPosition );
1255     }
1256
1257     if( Event::GRAB_HANDLE_EVENT == event.type )
1258     {
1259       mEventData->mUpdateCursorPosition = true;
1260       mEventData->mUpdateInputStyle = true;
1261
1262       if( !IsClipboardEmpty() )
1263       {
1264         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1265       }
1266
1267       if( handleStopScrolling )
1268       {
1269         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1270         mEventData->mPrimaryCursorPosition = handlePosition;
1271       }
1272     }
1273     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1274     {
1275       ChangeState( EventData::SELECTING );
1276
1277       if( handleStopScrolling )
1278       {
1279         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1280         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1281
1282         if( mEventData->mUpdateLeftSelectionPosition )
1283         {
1284           mEventData->mLeftSelectionPosition = handlePosition;
1285         }
1286       }
1287     }
1288     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1289     {
1290       ChangeState( EventData::SELECTING );
1291
1292       if( handleStopScrolling )
1293       {
1294         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1295         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1296         if( mEventData->mUpdateRightSelectionPosition )
1297         {
1298           mEventData->mRightSelectionPosition = handlePosition;
1299         }
1300       }
1301     }
1302
1303     mEventData->mDecoratorUpdated = true;
1304   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1305   else if( HANDLE_SCROLLING == state )
1306   {
1307     const float xSpeed = event.p2.mFloat;
1308     const Vector2& actualSize = mVisualModel->GetLayoutSize();
1309     const Vector2 currentScrollPosition = mScrollPosition;
1310
1311     mScrollPosition.x += xSpeed;
1312
1313     ClampHorizontalScroll( actualSize );
1314
1315     bool endOfScroll = false;
1316     if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1317     {
1318       // Notify the decorator there is no more text to scroll.
1319       // The decorator won't send more scroll events.
1320       mEventData->mDecorator->NotifyEndOfScroll();
1321       // Still need to set the position of the handle.
1322       endOfScroll = true;
1323     }
1324
1325     // Set the position of the handle.
1326     const bool scrollRightDirection = xSpeed > 0.f;
1327     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1328     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1329
1330     if( Event::GRAB_HANDLE_EVENT == event.type )
1331     {
1332       ChangeState( EventData::GRAB_HANDLE_PANNING );
1333
1334       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1335
1336       // Position the grag handle close to either the left or right edge.
1337       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1338
1339       // Get the new handle position.
1340       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1341       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1342                                                                          mLogicalModel,
1343                                                                          mMetrics,
1344                                                                          position.x - mScrollPosition.x,
1345                                                                          position.y - mScrollPosition.y );
1346
1347       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1348       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1349       mEventData->mPrimaryCursorPosition = handlePosition;
1350       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1351     }
1352     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1353     {
1354       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1355       //       Think if something can be done to save power.
1356
1357       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1358
1359       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1360
1361       // Position the selection handle close to either the left or right edge.
1362       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1363
1364       // Get the new handle position.
1365       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1366       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1367                                                                          mLogicalModel,
1368                                                                          mMetrics,
1369                                                                          position.x - mScrollPosition.x,
1370                                                                          position.y - mScrollPosition.y );
1371
1372       if( leftSelectionHandleEvent )
1373       {
1374         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1375         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1376         if( differentHandles )
1377         {
1378           mEventData->mLeftSelectionPosition = handlePosition;
1379         }
1380       }
1381       else
1382       {
1383         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1384         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1385         if( differentHandles )
1386         {
1387           mEventData->mRightSelectionPosition = handlePosition;
1388         }
1389       }
1390
1391       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1392       {
1393         RepositionSelectionHandles();
1394
1395         mEventData->mScrollAfterUpdatePosition = true;
1396       }
1397     }
1398     mEventData->mDecoratorUpdated = true;
1399   } // end ( HANDLE_SCROLLING == state )
1400 }
1401
1402 void Controller::Impl::OnSelectEvent( const Event& event )
1403 {
1404   if( NULL == mEventData )
1405   {
1406     // Nothing to do if there is no text.
1407     return;
1408   }
1409
1410   if( mEventData->mSelectionEnabled )
1411   {
1412     // Convert from control's coords to text's coords.
1413     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1414     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1415
1416     // Calculates the logical position from the x,y coords.
1417     RepositionSelectionHandles( xPosition,
1418                                 yPosition );
1419
1420     mEventData->mUpdateLeftSelectionPosition = true;
1421     mEventData->mUpdateRightSelectionPosition = true;
1422     mEventData->mUpdateCursorPosition = false;
1423
1424     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1425   }
1426 }
1427
1428 void Controller::Impl::OnSelectAllEvent()
1429 {
1430   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1431
1432   if( NULL == mEventData )
1433   {
1434     // Nothing to do if there is no text.
1435     return;
1436   }
1437
1438   if( mEventData->mSelectionEnabled )
1439   {
1440     mEventData->mLeftSelectionPosition = 0u;
1441     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1442
1443     mEventData->mScrollAfterUpdatePosition = true;
1444     mEventData->mUpdateLeftSelectionPosition = true;
1445     mEventData->mUpdateRightSelectionPosition = true;
1446   }
1447 }
1448
1449 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1450 {
1451   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1452   {
1453     // Nothing to select if handles are in the same place.
1454     selectedText.clear();
1455     return;
1456   }
1457
1458   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1459
1460   //Get start and end position of selection
1461   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1462   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1463
1464   Vector<Character>& utf32Characters = mLogicalModel->mText;
1465   const Length numberOfCharacters = utf32Characters.Count();
1466
1467   // Validate the start and end selection points
1468   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1469   {
1470     //Get text as a UTF8 string
1471     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1472
1473     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1474     {
1475       // Set as input style the style of the first deleted character.
1476       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1477
1478       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1479
1480       // Mark the paragraphs to be updated.
1481       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1482       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1483
1484       // Delete text between handles
1485       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1486       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1487       utf32Characters.Erase( first, last );
1488
1489       // Will show the cursor at the first character of the selection.
1490       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1491     }
1492     else
1493     {
1494       // Will show the cursor at the last character of the selection.
1495       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1496     }
1497
1498     mEventData->mDecoratorUpdated = true;
1499   }
1500 }
1501
1502 void Controller::Impl::ShowClipboard()
1503 {
1504   if( mClipboard )
1505   {
1506     mClipboard.ShowClipboard();
1507   }
1508 }
1509
1510 void Controller::Impl::HideClipboard()
1511 {
1512   if( mClipboard && mClipboardHideEnabled )
1513   {
1514     mClipboard.HideClipboard();
1515   }
1516 }
1517
1518 void Controller::Impl::SetClipboardHideEnable(bool enable)
1519 {
1520   mClipboardHideEnabled = enable;
1521 }
1522
1523 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1524 {
1525   //Send string to clipboard
1526   return ( mClipboard && mClipboard.SetItem( source ) );
1527 }
1528
1529 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1530 {
1531   std::string selectedText;
1532   RetrieveSelection( selectedText, deleteAfterSending );
1533   CopyStringToClipboard( selectedText );
1534   ChangeState( EventData::EDITING );
1535 }
1536
1537 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1538 {
1539   if ( mClipboard )
1540   {
1541     retrievedString =  mClipboard.GetItem( itemIndex );
1542   }
1543 }
1544
1545 void Controller::Impl::RepositionSelectionHandles()
1546 {
1547   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1548   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1549
1550   if( selectionStart == selectionEnd )
1551   {
1552     // Nothing to select if handles are in the same place.
1553     return;
1554   }
1555
1556   mEventData->mDecorator->ClearHighlights();
1557
1558   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1559   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1560   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1561   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1562   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1563   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1564   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1565
1566   // TODO: Better algorithm to create the highlight box.
1567
1568   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1569   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1570   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1571
1572   // Swap the indices if the start is greater than the end.
1573   const bool indicesSwapped = selectionStart > selectionEnd;
1574
1575   // Tell the decorator to flip the selection handles if needed.
1576   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1577
1578   if( indicesSwapped )
1579   {
1580     std::swap( selectionStart, selectionEnd );
1581   }
1582
1583   // Get the indices to the first and last selected glyphs.
1584   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1585   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1586   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1587   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1588
1589   // Get the lines where the glyphs are laid-out.
1590   const LineRun* lineRun = mVisualModel->mLines.Begin();
1591
1592   LineIndex lineIndex = 0u;
1593   Length numberOfLines = 0u;
1594   mVisualModel->GetNumberOfLines( glyphStart,
1595                                   1u + glyphEnd - glyphStart,
1596                                   lineIndex,
1597                                   numberOfLines );
1598   const LineIndex firstLineIndex = lineIndex;
1599
1600   // Create the structure to store some selection box info.
1601   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1602   selectionBoxLinesInfo.Resize( numberOfLines );
1603
1604   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1605   selectionBoxInfo->minX = MAX_FLOAT;
1606   selectionBoxInfo->maxX = MIN_FLOAT;
1607
1608   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1609
1610   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1611   selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1612                                                       firstLineIndex );
1613   lineRun += firstLineIndex;
1614
1615   // The line height is the addition of the line ascender and the line descender.
1616   // However, the line descender has a negative value, hence the subtraction.
1617   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1618
1619   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1620
1621   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1622   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1623   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1624
1625   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1626   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1627   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1628
1629   // Traverse the glyphs.
1630   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1631   {
1632     const GlyphInfo& glyph = *( glyphsBuffer + index );
1633     const Vector2& position = *( positionsBuffer + index );
1634
1635     if( splitStartGlyph )
1636     {
1637       // 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.
1638
1639       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1640       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1641       // Get the direction of the character.
1642       CharacterDirection isCurrentRightToLeft = false;
1643       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1644       {
1645         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1646       }
1647
1648       // The end point could be in the middle of the ligature.
1649       // Calculate the number of characters selected.
1650       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1651
1652       const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1653       const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1654       const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1655
1656       // Store the min and max 'x' for each line.
1657       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1658       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1659
1660       mEventData->mDecorator->AddHighlight( xPosition,
1661                                             yPosition,
1662                                             xPositionAdvance,
1663                                             yPosition + selectionBoxInfo->lineHeight );
1664
1665       splitStartGlyph = false;
1666       continue;
1667     }
1668
1669     if( splitEndGlyph && ( index == glyphEnd ) )
1670     {
1671       // 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.
1672
1673       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1674       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1675       // Get the direction of the character.
1676       CharacterDirection isCurrentRightToLeft = false;
1677       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1678       {
1679         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1680       }
1681
1682       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1683
1684       const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1685       const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1686       const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1687
1688       // Store the min and max 'x' for each line.
1689       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1690       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1691
1692       mEventData->mDecorator->AddHighlight( xPosition,
1693                                             yPosition,
1694                                             xPositionAdvance,
1695                                             yPosition + selectionBoxInfo->lineHeight );
1696
1697       splitEndGlyph = false;
1698       continue;
1699     }
1700
1701     const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1702     const float xPositionAdvance = xPosition + glyph.advance;
1703     const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1704
1705     // Store the min and max 'x' for each line.
1706     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1707     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1708
1709     mEventData->mDecorator->AddHighlight( xPosition,
1710                                           yPosition,
1711                                           xPositionAdvance,
1712                                           yPosition + selectionBoxInfo->lineHeight );
1713
1714     // Whether to retrieve the next line.
1715     if( index == lastGlyphOfLine )
1716     {
1717       // Retrieve the next line.
1718       ++lineRun;
1719
1720       // Get the last glyph of the new line.
1721       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1722
1723       ++lineIndex;
1724       if( lineIndex < firstLineIndex + numberOfLines )
1725       {
1726         // Get the selection box info for the next line.
1727         const float currentLineOffset = selectionBoxInfo->lineOffset;
1728         ++selectionBoxInfo;
1729
1730         selectionBoxInfo->minX = MAX_FLOAT;
1731         selectionBoxInfo->maxX = MIN_FLOAT;
1732
1733         // The line height is the addition of the line ascender and the line descender.
1734         // However, the line descender has a negative value, hence the subtraction.
1735         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1736
1737         // Update the line's vertical offset.
1738         selectionBoxInfo->lineOffset = currentLineOffset + selectionBoxInfo->lineHeight;
1739       }
1740     }
1741   }
1742
1743   // Add extra geometry to 'boxify' the selection.
1744
1745   if( 1u < numberOfLines )
1746   {
1747     // Boxify the first line.
1748     lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1749     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1750
1751     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1752     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1753
1754     if( boxifyBegin )
1755     {
1756       // Boxify at the beginning of the line.
1757       mEventData->mDecorator->AddHighlight( 0.f,
1758                                             firstSelectionBoxLineInfo.lineOffset,
1759                                             firstSelectionBoxLineInfo.minX,
1760                                             firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1761     }
1762
1763     if( boxifyEnd )
1764     {
1765       // Boxify at the end of the line.
1766       mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1767                                             firstSelectionBoxLineInfo.lineOffset,
1768                                             mVisualModel->mControlSize.width,
1769                                             firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1770     }
1771
1772     // Boxify the central lines.
1773     if( 2u < numberOfLines )
1774     {
1775       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1776              endIt = selectionBoxLinesInfo.End() - 1u;
1777            it != endIt;
1778            ++it )
1779       {
1780         const SelectionBoxInfo& info = *it;
1781
1782         mEventData->mDecorator->AddHighlight( 0.f,
1783                                               info.lineOffset,
1784                                               info.minX,
1785                                               info.lineOffset + info.lineHeight );
1786
1787         mEventData->mDecorator->AddHighlight( info.maxX,
1788                                               info.lineOffset,
1789                                               mVisualModel->mControlSize.width,
1790                                               info.lineOffset + info.lineHeight );
1791       }
1792     }
1793
1794     // Boxify the last line.
1795     lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1796     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1797
1798     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1799     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1800
1801     if( boxifyBegin )
1802     {
1803       // Boxify at the beginning of the line.
1804       mEventData->mDecorator->AddHighlight( 0.f,
1805                                             lastSelectionBoxLineInfo.lineOffset,
1806                                             lastSelectionBoxLineInfo.minX,
1807                                             lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1808     }
1809
1810     if( boxifyEnd )
1811     {
1812       // Boxify at the end of the line.
1813       mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1814                                             lastSelectionBoxLineInfo.lineOffset,
1815                                             mVisualModel->mControlSize.width,
1816                                             lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1817     }
1818   }
1819
1820
1821   CursorInfo primaryCursorInfo;
1822   GetCursorPosition( mEventData->mLeftSelectionPosition,
1823                      primaryCursorInfo );
1824
1825   CursorInfo secondaryCursorInfo;
1826   GetCursorPosition( mEventData->mRightSelectionPosition,
1827                      secondaryCursorInfo );
1828
1829   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1830   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1831
1832   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1833                                        primaryPosition.x,
1834                                        primaryCursorInfo.lineOffset + mScrollPosition.y,
1835                                        primaryCursorInfo.lineHeight );
1836
1837   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1838                                        secondaryPosition.x,
1839                                        secondaryCursorInfo.lineOffset + mScrollPosition.y,
1840                                        secondaryCursorInfo.lineHeight );
1841
1842   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1843   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1844
1845   // Set the flag to update the decorator.
1846   mEventData->mDecoratorUpdated = true;
1847 }
1848
1849 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1850 {
1851   if( NULL == mEventData )
1852   {
1853     // Nothing to do if there is no text input.
1854     return;
1855   }
1856
1857   if( IsShowingPlaceholderText() )
1858   {
1859     // Nothing to do if there is the place-holder text.
1860     return;
1861   }
1862
1863   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1864   const Length numberOfLines  = mVisualModel->mLines.Count();
1865   if( ( 0 == numberOfGlyphs ) ||
1866       ( 0 == numberOfLines ) )
1867   {
1868     // Nothing to do if there is no text.
1869     return;
1870   }
1871
1872   // Find which word was selected
1873   CharacterIndex selectionStart( 0 );
1874   CharacterIndex selectionEnd( 0 );
1875   FindSelectionIndices( mVisualModel,
1876                         mLogicalModel,
1877                         mMetrics,
1878                         visualX,
1879                         visualY,
1880                         selectionStart,
1881                         selectionEnd );
1882   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1883
1884   if( selectionStart == selectionEnd )
1885   {
1886     ChangeState( EventData::EDITING );
1887     // Nothing to select. i.e. a white space, out of bounds
1888     return;
1889   }
1890
1891   mEventData->mLeftSelectionPosition = selectionStart;
1892   mEventData->mRightSelectionPosition = selectionEnd;
1893 }
1894
1895 void Controller::Impl::SetPopupButtons()
1896 {
1897   /**
1898    *  Sets the Popup buttons to be shown depending on State.
1899    *
1900    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1901    *
1902    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1903    */
1904
1905   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1906
1907   if( EventData::SELECTING == mEventData->mState )
1908   {
1909     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1910
1911     if( !IsClipboardEmpty() )
1912     {
1913       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1914       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1915     }
1916
1917     if( !mEventData->mAllTextSelected )
1918     {
1919       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1920     }
1921   }
1922   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1923   {
1924     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1925     {
1926       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1927     }
1928
1929     if( !IsClipboardEmpty() )
1930     {
1931       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1932       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1933     }
1934   }
1935   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1936   {
1937     if ( !IsClipboardEmpty() )
1938     {
1939       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1940       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1941     }
1942   }
1943
1944   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1945 }
1946
1947 void Controller::Impl::ChangeState( EventData::State newState )
1948 {
1949   if( NULL == mEventData )
1950   {
1951     // Nothing to do if there is no text input.
1952     return;
1953   }
1954
1955   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
1956
1957   if( mEventData->mState != newState )
1958   {
1959     mEventData->mState = newState;
1960
1961     if( EventData::INACTIVE == mEventData->mState )
1962     {
1963       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1964       mEventData->mDecorator->StopCursorBlink();
1965       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1966       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1967       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1968       mEventData->mDecorator->SetPopupActive( false );
1969       mEventData->mDecoratorUpdated = true;
1970       HideClipboard();
1971     }
1972     else if( EventData::INTERRUPTED  == mEventData->mState)
1973     {
1974       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1975       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1976       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1977       mEventData->mDecorator->SetPopupActive( false );
1978       mEventData->mDecoratorUpdated = true;
1979       HideClipboard();
1980     }
1981     else if( EventData::SELECTING == mEventData->mState )
1982     {
1983       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1984       mEventData->mDecorator->StopCursorBlink();
1985       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1986       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1987       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1988       if( mEventData->mGrabHandlePopupEnabled )
1989       {
1990         SetPopupButtons();
1991         mEventData->mDecorator->SetPopupActive( true );
1992       }
1993       mEventData->mDecoratorUpdated = true;
1994     }
1995     else if( EventData::EDITING == mEventData->mState )
1996     {
1997       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1998       if( mEventData->mCursorBlinkEnabled )
1999       {
2000         mEventData->mDecorator->StartCursorBlink();
2001       }
2002       // Grab handle is not shown until a tap is received whilst EDITING
2003       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2004       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2005       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2006       if( mEventData->mGrabHandlePopupEnabled )
2007       {
2008         mEventData->mDecorator->SetPopupActive( false );
2009       }
2010       mEventData->mDecoratorUpdated = true;
2011       HideClipboard();
2012     }
2013     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2014     {
2015       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2016
2017       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2018       if( mEventData->mCursorBlinkEnabled )
2019       {
2020         mEventData->mDecorator->StartCursorBlink();
2021       }
2022       if( mEventData->mSelectionEnabled )
2023       {
2024         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2025         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2026       }
2027       else
2028       {
2029         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2030       }
2031       if( mEventData->mGrabHandlePopupEnabled )
2032       {
2033         SetPopupButtons();
2034         mEventData->mDecorator->SetPopupActive( true );
2035       }
2036       HideClipboard();
2037       mEventData->mDecoratorUpdated = true;
2038     }
2039     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
2040     {
2041       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2042
2043       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2044       if( mEventData->mCursorBlinkEnabled )
2045       {
2046         mEventData->mDecorator->StartCursorBlink();
2047       }
2048       // Grab handle is not shown until a tap is received whilst EDITING
2049       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2050       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2051       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2052       if( mEventData->mGrabHandlePopupEnabled )
2053       {
2054         mEventData->mDecorator->SetPopupActive( false );
2055       }
2056       mEventData->mDecoratorUpdated = true;
2057       HideClipboard();
2058     }
2059     else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
2060     {
2061       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2062       mEventData->mDecorator->StopCursorBlink();
2063       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2064       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2065       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2066       if( mEventData->mGrabHandlePopupEnabled )
2067       {
2068         mEventData->mDecorator->SetPopupActive( false );
2069       }
2070       mEventData->mDecoratorUpdated = true;
2071     }
2072     else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
2073     {
2074       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2075
2076       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2077       if( mEventData->mCursorBlinkEnabled )
2078       {
2079         mEventData->mDecorator->StartCursorBlink();
2080       }
2081       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2082       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2083       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2084       if( mEventData->mGrabHandlePopupEnabled )
2085       {
2086         mEventData->mDecorator->SetPopupActive( false );
2087       }
2088       mEventData->mDecoratorUpdated = true;
2089     }
2090     else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2091     {
2092       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2093
2094       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2095       if( mEventData->mCursorBlinkEnabled )
2096       {
2097         mEventData->mDecorator->StartCursorBlink();
2098       }
2099
2100       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2101       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2102       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2103
2104       if( mEventData->mGrabHandlePopupEnabled )
2105       {
2106         SetPopupButtons();
2107         mEventData->mDecorator->SetPopupActive( true );
2108       }
2109       HideClipboard();
2110       mEventData->mDecoratorUpdated = true;
2111     }
2112   }
2113 }
2114
2115 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2116                                           CursorInfo& cursorInfo )
2117 {
2118   if( !IsShowingRealText() )
2119   {
2120     // Do not want to use the place-holder text to set the cursor position.
2121
2122     // Use the line's height of the font's family set to set the cursor's size.
2123     // If there is no font's family set, use the default font.
2124     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2125
2126     cursorInfo.lineOffset = 0.f;
2127     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2128     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2129
2130     switch( mLayoutEngine.GetHorizontalAlignment() )
2131     {
2132       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2133       {
2134         cursorInfo.primaryPosition.x = 0.f;
2135         break;
2136       }
2137       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2138       {
2139         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2140         break;
2141       }
2142       case LayoutEngine::HORIZONTAL_ALIGN_END:
2143       {
2144         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2145         break;
2146       }
2147     }
2148
2149     // Nothing else to do.
2150     return;
2151   }
2152
2153   Text::GetCursorPosition( mVisualModel,
2154                            mLogicalModel,
2155                            mMetrics,
2156                            logical,
2157                            cursorInfo );
2158
2159   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2160   {
2161     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2162
2163     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2164     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2165
2166     if( 0.f > cursorInfo.primaryPosition.x )
2167     {
2168       cursorInfo.primaryPosition.x = 0.f;
2169     }
2170
2171     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2172     if( cursorInfo.primaryPosition.x > edgeWidth )
2173     {
2174       cursorInfo.primaryPosition.x = edgeWidth;
2175     }
2176   }
2177 }
2178
2179 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2180 {
2181   if( NULL == mEventData )
2182   {
2183     // Nothing to do if there is no text input.
2184     return 0u;
2185   }
2186
2187   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2188
2189   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2190   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2191
2192   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2193   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2194
2195   if( numberOfCharacters > 1u )
2196   {
2197     const Script script = mLogicalModel->GetScript( index );
2198     if( HasLigatureMustBreak( script ) )
2199     {
2200       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
2201       numberOfCharacters = 1u;
2202     }
2203   }
2204   else
2205   {
2206     while( 0u == numberOfCharacters )
2207     {
2208       ++glyphIndex;
2209       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2210     }
2211   }
2212
2213   if( index < mEventData->mPrimaryCursorPosition )
2214   {
2215     cursorIndex -= numberOfCharacters;
2216   }
2217   else
2218   {
2219     cursorIndex += numberOfCharacters;
2220   }
2221
2222   return cursorIndex;
2223 }
2224
2225 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2226 {
2227   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2228   if( NULL == mEventData )
2229   {
2230     // Nothing to do if there is no text input.
2231     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2232     return;
2233   }
2234
2235   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2236
2237   // Sets the cursor position.
2238   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2239                                        cursorPosition.x,
2240                                        cursorPosition.y,
2241                                        cursorInfo.primaryCursorHeight,
2242                                        cursorInfo.lineHeight );
2243   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2244
2245   // Sets the grab handle position.
2246   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2247                                        cursorPosition.x,
2248                                        cursorInfo.lineOffset + mScrollPosition.y,
2249                                        cursorInfo.lineHeight );
2250
2251   if( cursorInfo.isSecondaryCursor )
2252   {
2253     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2254                                          cursorInfo.secondaryPosition.x + mScrollPosition.x,
2255                                          cursorInfo.secondaryPosition.y + mScrollPosition.y,
2256                                          cursorInfo.secondaryCursorHeight,
2257                                          cursorInfo.lineHeight );
2258     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2259   }
2260
2261   // Set which cursors are active according the state.
2262   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2263   {
2264     if( cursorInfo.isSecondaryCursor )
2265     {
2266       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2267     }
2268     else
2269     {
2270       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2271     }
2272   }
2273   else
2274   {
2275     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2276   }
2277
2278   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2279 }
2280
2281 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2282                                               const CursorInfo& cursorInfo )
2283 {
2284   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2285       ( RIGHT_SELECTION_HANDLE != handleType ) )
2286   {
2287     return;
2288   }
2289
2290   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2291
2292   // Sets the handle's position.
2293   mEventData->mDecorator->SetPosition( handleType,
2294                                        cursorPosition.x,
2295                                        cursorInfo.lineOffset + mScrollPosition.y,
2296                                        cursorInfo.lineHeight );
2297
2298   // If selection handle at start of the text and other at end of the text then all text is selected.
2299   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2300   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2301   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2302 }
2303
2304 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2305 {
2306   // Clamp between -space & 0.
2307
2308   if( actualSize.width > mVisualModel->mControlSize.width )
2309   {
2310     const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2311     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2312     mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2313
2314     mEventData->mDecoratorUpdated = true;
2315   }
2316   else
2317   {
2318     mScrollPosition.x = 0.f;
2319   }
2320 }
2321
2322 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2323 {
2324   // Clamp between -space & 0.
2325   if( actualSize.height > mVisualModel->mControlSize.height )
2326   {
2327     const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2328     mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2329     mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2330
2331     mEventData->mDecoratorUpdated = true;
2332   }
2333   else
2334   {
2335     mScrollPosition.y = 0.f;
2336   }
2337 }
2338
2339 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2340 {
2341   const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2342
2343   // position is in actor's coords.
2344   const float positionEnd = position.x + cursorWidth;
2345
2346   // Transform the position to decorator coords.
2347   const float decoratorPositionBegin = position.x + mScrollPosition.x;
2348   const float decoratorPositionEnd = positionEnd + mScrollPosition.x;
2349
2350   if( decoratorPositionBegin < 0.f )
2351   {
2352     mScrollPosition.x = -position.x;
2353   }
2354   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2355   {
2356     mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd;
2357   }
2358 }
2359
2360 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2361 {
2362   // Get the current cursor position in decorator coords.
2363   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2364
2365   // Calculate the offset to match the cursor position before the character was deleted.
2366   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2367
2368   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2369
2370   // Makes the new cursor position visible if needed.
2371   ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2372 }
2373
2374 void Controller::Impl::RequestRelayout()
2375 {
2376   mControlInterface.RequestTextRelayout();
2377 }
2378
2379 } // namespace Text
2380
2381 } // namespace Toolkit
2382
2383 } // namespace Dali