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