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