TextModel - Update only the edited paragraph when the style changes.
[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   inputStyle.familyName.clear();
900   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
901   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
902   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
903   inputStyle.size = 0.f;
904
905   inputStyle.familyDefined = false;
906   inputStyle.weightDefined = false;
907   inputStyle.widthDefined = false;
908   inputStyle.slantDefined = false;
909   inputStyle.sizeDefined = false;
910
911   // Sets the default font's family name, weight, width, slant and size.
912   if( mFontDefaults )
913   {
914     if( mFontDefaults->familyDefined )
915     {
916       inputStyle.familyName = mFontDefaults->mFontDescription.family;
917       inputStyle.familyDefined = true;
918     }
919
920     if( mFontDefaults->weightDefined )
921     {
922       inputStyle.weight = mFontDefaults->mFontDescription.weight;
923       inputStyle.weightDefined = true;
924     }
925
926     if( mFontDefaults->widthDefined )
927     {
928       inputStyle.width = mFontDefaults->mFontDescription.width;
929       inputStyle.widthDefined = true;
930     }
931
932     if( mFontDefaults->slantDefined )
933     {
934       inputStyle.slant = mFontDefaults->mFontDescription.slant;
935       inputStyle.slantDefined = true;
936     }
937
938     if( mFontDefaults->sizeDefined )
939     {
940       inputStyle.size = mFontDefaults->mDefaultPointSize;
941       inputStyle.sizeDefined = true;
942     }
943   }
944 }
945
946 float Controller::Impl::GetDefaultFontLineHeight()
947 {
948   FontId defaultFontId = 0u;
949   if( NULL == mFontDefaults )
950   {
951     TextAbstraction::FontDescription fontDescription;
952     defaultFontId = mFontClient.GetFontId( fontDescription );
953   }
954   else
955   {
956     defaultFontId = mFontDefaults->GetFontId( mFontClient );
957   }
958
959   Text::FontMetrics fontMetrics;
960   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
961
962   return( fontMetrics.ascender - fontMetrics.descender );
963 }
964
965 void Controller::Impl::OnCursorKeyEvent( const Event& event )
966 {
967   if( NULL == mEventData )
968   {
969     // Nothing to do if there is no text input.
970     return;
971   }
972
973   int keyCode = event.p1.mInt;
974
975   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
976   {
977     if( mEventData->mPrimaryCursorPosition > 0u )
978     {
979       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
980     }
981   }
982   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
983   {
984     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
985     {
986       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
987     }
988   }
989   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
990   {
991     // TODO
992   }
993   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
994   {
995     // TODO
996   }
997
998   mEventData->mUpdateCursorPosition = true;
999   mEventData->mUpdateInputStyle = true;
1000   mEventData->mScrollAfterUpdatePosition = true;
1001 }
1002
1003 void Controller::Impl::OnTapEvent( const Event& event )
1004 {
1005   if( NULL != mEventData )
1006   {
1007     const unsigned int tapCount = event.p1.mUint;
1008
1009     if( 1u == tapCount )
1010     {
1011       if( IsShowingRealText() )
1012       {
1013         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1014         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1015
1016         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
1017                                                                     yPosition );
1018
1019         // When the cursor position is changing, delay cursor blinking
1020         mEventData->mDecorator->DelayCursorBlink();
1021       }
1022       else
1023       {
1024         mEventData->mPrimaryCursorPosition = 0u;
1025       }
1026
1027       mEventData->mUpdateCursorPosition = true;
1028       mEventData->mScrollAfterUpdatePosition = true;
1029       mEventData->mUpdateInputStyle = true;
1030
1031       // Notify the cursor position to the imf manager.
1032       if( mEventData->mImfManager )
1033       {
1034         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1035         mEventData->mImfManager.NotifyCursorPosition();
1036       }
1037     }
1038   }
1039 }
1040
1041 void Controller::Impl::OnPanEvent( const Event& event )
1042 {
1043   if( NULL == mEventData )
1044   {
1045     // Nothing to do if there is no text input.
1046     return;
1047   }
1048
1049   int state = event.p1.mInt;
1050
1051   if( Gesture::Started    == state ||
1052       Gesture::Continuing == state )
1053   {
1054     const Vector2& actualSize = mVisualModel->GetLayoutSize();
1055     const Vector2 currentScroll = mEventData->mScrollPosition;
1056
1057     if( mEventData->mHorizontalScrollingEnabled )
1058     {
1059       const float displacementX = event.p2.mFloat;
1060       mEventData->mScrollPosition.x += displacementX;
1061
1062       ClampHorizontalScroll( actualSize );
1063     }
1064
1065     if( mEventData->mVerticalScrollingEnabled )
1066     {
1067       const float displacementY = event.p3.mFloat;
1068       mEventData->mScrollPosition.y += displacementY;
1069
1070       ClampVerticalScroll( actualSize );
1071     }
1072
1073     if( mEventData->mDecorator )
1074     {
1075       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
1076     }
1077   }
1078 }
1079
1080 void Controller::Impl::OnLongPressEvent( const Event& event )
1081 {
1082   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1083
1084   if( EventData::EDITING == mEventData->mState )
1085   {
1086     ChangeState ( EventData::EDITING_WITH_POPUP );
1087     mEventData->mDecoratorUpdated = true;
1088   }
1089 }
1090
1091 void Controller::Impl::OnHandleEvent( const Event& event )
1092 {
1093   if( NULL == mEventData )
1094   {
1095     // Nothing to do if there is no text input.
1096     return;
1097   }
1098
1099   const unsigned int state = event.p1.mUint;
1100   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1101
1102   if( HANDLE_PRESSED == state )
1103   {
1104     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1105     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1106     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1107
1108     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
1109
1110     if( Event::GRAB_HANDLE_EVENT == event.type )
1111     {
1112       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1113
1114       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1115       {
1116         mEventData->mPrimaryCursorPosition = handleNewPosition;
1117         mEventData->mUpdateCursorPosition = true;
1118       }
1119     }
1120     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1121     {
1122       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1123
1124       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1125           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1126       {
1127         mEventData->mLeftSelectionPosition = handleNewPosition;
1128
1129         mEventData->mUpdateLeftSelectionPosition = true;
1130       }
1131     }
1132     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1133     {
1134       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1135
1136       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1137           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1138       {
1139         mEventData->mRightSelectionPosition = handleNewPosition;
1140
1141         mEventData->mUpdateRightSelectionPosition = true;
1142       }
1143     }
1144   } // end ( HANDLE_PRESSED == state )
1145   else if( ( HANDLE_RELEASED == state ) ||
1146            handleStopScrolling )
1147   {
1148     CharacterIndex handlePosition = 0u;
1149     if( handleStopScrolling )
1150     {
1151       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1152       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1153       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1154
1155       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
1156     }
1157
1158     if( Event::GRAB_HANDLE_EVENT == event.type )
1159     {
1160       mEventData->mUpdateCursorPosition = true;
1161       mEventData->mUpdateInputStyle = true;
1162
1163       if( !IsClipboardEmpty() )
1164       {
1165         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1166       }
1167
1168       if( handleStopScrolling )
1169       {
1170         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1171         mEventData->mPrimaryCursorPosition = handlePosition;
1172       }
1173     }
1174     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1175     {
1176       ChangeState( EventData::SELECTING );
1177
1178       if( handleStopScrolling )
1179       {
1180         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1181         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1182
1183         if( mEventData->mUpdateLeftSelectionPosition )
1184         {
1185           mEventData->mLeftSelectionPosition = handlePosition;
1186         }
1187       }
1188     }
1189     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1190     {
1191       ChangeState( EventData::SELECTING );
1192
1193       if( handleStopScrolling )
1194       {
1195         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1196         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1197         if( mEventData->mUpdateRightSelectionPosition )
1198         {
1199           mEventData->mRightSelectionPosition = handlePosition;
1200         }
1201       }
1202     }
1203
1204     mEventData->mDecoratorUpdated = true;
1205   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1206   else if( HANDLE_SCROLLING == state )
1207   {
1208     const float xSpeed = event.p2.mFloat;
1209     const Vector2& actualSize = mVisualModel->GetLayoutSize();
1210     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
1211
1212     mEventData->mScrollPosition.x += xSpeed;
1213
1214     ClampHorizontalScroll( actualSize );
1215
1216     bool endOfScroll = false;
1217     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
1218     {
1219       // Notify the decorator there is no more text to scroll.
1220       // The decorator won't send more scroll events.
1221       mEventData->mDecorator->NotifyEndOfScroll();
1222       // Still need to set the position of the handle.
1223       endOfScroll = true;
1224     }
1225
1226     // Set the position of the handle.
1227     const bool scrollRightDirection = xSpeed > 0.f;
1228     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1229     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1230
1231     if( Event::GRAB_HANDLE_EVENT == event.type )
1232     {
1233       ChangeState( EventData::GRAB_HANDLE_PANNING );
1234
1235       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1236
1237       // Position the grag handle close to either the left or right edge.
1238       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1239
1240       // Get the new handle position.
1241       // The grab handle's position is in decorator coords. Need to transforms to text coords.
1242       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1243                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
1244
1245       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1246       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1247       mEventData->mPrimaryCursorPosition = handlePosition;
1248       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1249     }
1250     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1251     {
1252       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1253       //       Think if something can be done to save power.
1254
1255       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1256
1257       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1258
1259       // Position the selection handle close to either the left or right edge.
1260       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1261
1262       // Get the new handle position.
1263       // The selection handle's position is in decorator coords. Need to transforms to text coords.
1264       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1265                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
1266
1267       if( leftSelectionHandleEvent )
1268       {
1269         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1270         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1271         if( differentHandles )
1272         {
1273           mEventData->mLeftSelectionPosition = handlePosition;
1274         }
1275       }
1276       else
1277       {
1278         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1279         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1280         if( differentHandles )
1281         {
1282           mEventData->mRightSelectionPosition = handlePosition;
1283         }
1284       }
1285
1286       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1287       {
1288         RepositionSelectionHandles();
1289
1290         mEventData->mScrollAfterUpdatePosition = true;
1291       }
1292     }
1293     mEventData->mDecoratorUpdated = true;
1294   } // end ( HANDLE_SCROLLING == state )
1295 }
1296
1297 void Controller::Impl::OnSelectEvent( const Event& event )
1298 {
1299   if( NULL == mEventData )
1300   {
1301     // Nothing to do if there is no text.
1302     return;
1303   }
1304
1305   if( mEventData->mSelectionEnabled )
1306   {
1307     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1308     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1309     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1310
1311     // Calculates the logical position from the x,y coords.
1312     RepositionSelectionHandles( xPosition,
1313                                 yPosition );
1314
1315     mEventData->mUpdateLeftSelectionPosition = true;
1316     mEventData->mUpdateRightSelectionPosition = true;
1317
1318     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1319   }
1320 }
1321
1322 void Controller::Impl::OnSelectAllEvent()
1323 {
1324   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1325
1326   if( NULL == mEventData )
1327   {
1328     // Nothing to do if there is no text.
1329     return;
1330   }
1331
1332   if( mEventData->mSelectionEnabled )
1333   {
1334     mEventData->mLeftSelectionPosition = 0u;
1335     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1336
1337     mEventData->mScrollAfterUpdatePosition = true;
1338     mEventData->mUpdateLeftSelectionPosition = true;
1339     mEventData->mUpdateRightSelectionPosition = true;
1340   }
1341 }
1342
1343 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1344 {
1345   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1346   {
1347     // Nothing to select if handles are in the same place.
1348     selectedText.clear();
1349     return;
1350   }
1351
1352   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1353
1354   //Get start and end position of selection
1355   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1356   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1357
1358   Vector<Character>& utf32Characters = mLogicalModel->mText;
1359   const Length numberOfCharacters = utf32Characters.Count();
1360
1361   // Validate the start and end selection points
1362   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1363   {
1364     //Get text as a UTF8 string
1365     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1366
1367     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1368     {
1369       // Set as input style the style of the first deleted character.
1370       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1371
1372       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1373
1374       // Mark the paragraphs to be updated.
1375       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1376       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1377
1378       // Delete text between handles
1379       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1380       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1381       utf32Characters.Erase( first, last );
1382
1383       // Scroll after delete.
1384       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1385       mEventData->mScrollAfterDelete = true;
1386     }
1387     // Udpade the cursor position and the decorator.
1388     // Scroll after the position is updated if is not scrolling after delete.
1389     mEventData->mUpdateCursorPosition = true;
1390     mEventData->mScrollAfterUpdatePosition = !mEventData->mScrollAfterDelete;
1391     mEventData->mDecoratorUpdated = true;
1392   }
1393 }
1394
1395 void Controller::Impl::ShowClipboard()
1396 {
1397   if( mClipboard )
1398   {
1399     mClipboard.ShowClipboard();
1400   }
1401 }
1402
1403 void Controller::Impl::HideClipboard()
1404 {
1405   if( mClipboard )
1406   {
1407     mClipboard.HideClipboard();
1408   }
1409 }
1410
1411 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1412 {
1413   //Send string to clipboard
1414   return ( mClipboard && mClipboard.SetItem( source ) );
1415 }
1416
1417 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1418 {
1419   std::string selectedText;
1420   RetrieveSelection( selectedText, deleteAfterSending );
1421   CopyStringToClipboard( selectedText );
1422   ChangeState( EventData::EDITING );
1423 }
1424
1425 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1426 {
1427   if ( mClipboard )
1428   {
1429     retrievedString =  mClipboard.GetItem( itemIndex );
1430   }
1431 }
1432
1433 void Controller::Impl::RepositionSelectionHandles()
1434 {
1435   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1436   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1437
1438   if( selectionStart == selectionEnd )
1439   {
1440     // Nothing to select if handles are in the same place.
1441     return;
1442   }
1443
1444   mEventData->mDecorator->ClearHighlights();
1445
1446   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1447   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1448   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1449   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1450   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1451   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1452   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1453
1454   // TODO: Better algorithm to create the highlight box.
1455   // TODO: Multi-line.
1456
1457   // Get the height of the line.
1458   const Vector<LineRun>& lines = mVisualModel->mLines;
1459   const LineRun& firstLine = *lines.Begin();
1460   const float height = firstLine.ascender + -firstLine.descender;
1461
1462   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1463   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1464   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1465
1466   // Swap the indices if the start is greater than the end.
1467   const bool indicesSwapped = selectionStart > selectionEnd;
1468
1469   // Tell the decorator to flip the selection handles if needed.
1470   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1471
1472   if( indicesSwapped )
1473   {
1474     std::swap( selectionStart, selectionEnd );
1475   }
1476
1477   // Get the indices to the first and last selected glyphs.
1478   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1479   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1480   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1481   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1482
1483   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1484   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1485   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1486
1487   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1488   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1489   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1490
1491   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1492
1493   // Traverse the glyphs.
1494   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1495   {
1496     const GlyphInfo& glyph = *( glyphsBuffer + index );
1497     const Vector2& position = *( positionsBuffer + index );
1498
1499     if( splitStartGlyph )
1500     {
1501       // 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.
1502
1503       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1504       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1505       // Get the direction of the character.
1506       CharacterDirection isCurrentRightToLeft = false;
1507       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1508       {
1509         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1510       }
1511
1512       // The end point could be in the middle of the ligature.
1513       // Calculate the number of characters selected.
1514       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1515
1516       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1517
1518       mEventData->mDecorator->AddHighlight( xPosition,
1519                                             offset.y,
1520                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1521                                             offset.y + height );
1522
1523       splitStartGlyph = false;
1524       continue;
1525     }
1526
1527     if( splitEndGlyph && ( index == glyphEnd ) )
1528     {
1529       // 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.
1530
1531       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1532       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1533       // Get the direction of the character.
1534       CharacterDirection isCurrentRightToLeft = false;
1535       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1536       {
1537         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1538       }
1539
1540       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1541
1542       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1543       mEventData->mDecorator->AddHighlight( xPosition,
1544                                             offset.y,
1545                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1546                                             offset.y + height );
1547
1548       splitEndGlyph = false;
1549       continue;
1550     }
1551
1552     const float xPosition = position.x - glyph.xBearing + offset.x;
1553     mEventData->mDecorator->AddHighlight( xPosition,
1554                                           offset.y,
1555                                           xPosition + glyph.advance,
1556                                           offset.y + height );
1557   }
1558
1559   CursorInfo primaryCursorInfo;
1560   GetCursorPosition( mEventData->mLeftSelectionPosition,
1561                      primaryCursorInfo );
1562
1563   CursorInfo secondaryCursorInfo;
1564   GetCursorPosition( mEventData->mRightSelectionPosition,
1565                      secondaryCursorInfo );
1566
1567   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1568   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1569
1570   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1571                                        primaryPosition.x,
1572                                        primaryCursorInfo.lineOffset + offset.y,
1573                                        primaryCursorInfo.lineHeight );
1574
1575   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1576                                        secondaryPosition.x,
1577                                        secondaryCursorInfo.lineOffset + offset.y,
1578                                        secondaryCursorInfo.lineHeight );
1579
1580   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1581   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1582
1583   // Set the flag to update the decorator.
1584   mEventData->mDecoratorUpdated = true;
1585 }
1586
1587 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1588 {
1589   if( NULL == mEventData )
1590   {
1591     // Nothing to do if there is no text input.
1592     return;
1593   }
1594
1595   if( IsShowingPlaceholderText() )
1596   {
1597     // Nothing to do if there is the place-holder text.
1598     return;
1599   }
1600
1601   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1602   const Length numberOfLines  = mVisualModel->mLines.Count();
1603   if( ( 0 == numberOfGlyphs ) ||
1604       ( 0 == numberOfLines ) )
1605   {
1606     // Nothing to do if there is no text.
1607     return;
1608   }
1609
1610   // Find which word was selected
1611   CharacterIndex selectionStart( 0 );
1612   CharacterIndex selectionEnd( 0 );
1613   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1614   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1615
1616   if( selectionStart == selectionEnd )
1617   {
1618     ChangeState( EventData::EDITING );
1619     // Nothing to select. i.e. a white space, out of bounds
1620     return;
1621   }
1622
1623   mEventData->mLeftSelectionPosition = selectionStart;
1624   mEventData->mRightSelectionPosition = selectionEnd;
1625 }
1626
1627 void Controller::Impl::SetPopupButtons()
1628 {
1629   /**
1630    *  Sets the Popup buttons to be shown depending on State.
1631    *
1632    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1633    *
1634    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1635    */
1636
1637   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1638
1639   if( EventData::SELECTING == mEventData->mState )
1640   {
1641     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1642
1643     if( !IsClipboardEmpty() )
1644     {
1645       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1646       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1647     }
1648
1649     if( !mEventData->mAllTextSelected )
1650     {
1651       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1652     }
1653   }
1654   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1655   {
1656     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1657     {
1658       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1659     }
1660
1661     if( !IsClipboardEmpty() )
1662     {
1663       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1664       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1665     }
1666   }
1667   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1668   {
1669     if ( !IsClipboardEmpty() )
1670     {
1671       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1672       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1673     }
1674   }
1675
1676   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1677 }
1678
1679 void Controller::Impl::ChangeState( EventData::State newState )
1680 {
1681   if( NULL == mEventData )
1682   {
1683     // Nothing to do if there is no text input.
1684     return;
1685   }
1686
1687   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
1688
1689   if( mEventData->mState != newState )
1690   {
1691     mEventData->mState = newState;
1692
1693     if( EventData::INACTIVE == mEventData->mState )
1694     {
1695       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1696       mEventData->mDecorator->StopCursorBlink();
1697       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1698       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1699       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1700       mEventData->mDecorator->SetPopupActive( false );
1701       mEventData->mDecoratorUpdated = true;
1702       HideClipboard();
1703     }
1704     else if( EventData::INTERRUPTED  == mEventData->mState)
1705     {
1706       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1707       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1708       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1709       mEventData->mDecorator->SetPopupActive( false );
1710       mEventData->mDecoratorUpdated = true;
1711       HideClipboard();
1712     }
1713     else if( EventData::SELECTING == mEventData->mState )
1714     {
1715       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1716       mEventData->mDecorator->StopCursorBlink();
1717       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1718       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1719       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1720       if( mEventData->mGrabHandlePopupEnabled )
1721       {
1722         SetPopupButtons();
1723         mEventData->mDecorator->SetPopupActive( true );
1724       }
1725       mEventData->mDecoratorUpdated = true;
1726     }
1727     else if( EventData::EDITING == mEventData->mState )
1728     {
1729       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1730       if( mEventData->mCursorBlinkEnabled )
1731       {
1732         mEventData->mDecorator->StartCursorBlink();
1733       }
1734       // Grab handle is not shown until a tap is received whilst EDITING
1735       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1736       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1737       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1738       if( mEventData->mGrabHandlePopupEnabled )
1739       {
1740         mEventData->mDecorator->SetPopupActive( false );
1741       }
1742       mEventData->mDecoratorUpdated = true;
1743       HideClipboard();
1744     }
1745     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1746     {
1747       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1748
1749       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1750       if( mEventData->mCursorBlinkEnabled )
1751       {
1752         mEventData->mDecorator->StartCursorBlink();
1753       }
1754       if( mEventData->mSelectionEnabled )
1755       {
1756         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1757         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1758       }
1759       else
1760       {
1761         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1762       }
1763       if( mEventData->mGrabHandlePopupEnabled )
1764       {
1765         SetPopupButtons();
1766         mEventData->mDecorator->SetPopupActive( true );
1767       }
1768       HideClipboard();
1769       mEventData->mDecoratorUpdated = true;
1770     }
1771     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1772     {
1773       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1774
1775       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1776       if( mEventData->mCursorBlinkEnabled )
1777       {
1778         mEventData->mDecorator->StartCursorBlink();
1779       }
1780       // Grab handle is not shown until a tap is received whilst EDITING
1781       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1782       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1783       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1784       if( mEventData->mGrabHandlePopupEnabled )
1785       {
1786         mEventData->mDecorator->SetPopupActive( false );
1787       }
1788       mEventData->mDecoratorUpdated = true;
1789       HideClipboard();
1790     }
1791     else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1792     {
1793       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1794       mEventData->mDecorator->StopCursorBlink();
1795       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1796       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1797       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1798       if( mEventData->mGrabHandlePopupEnabled )
1799       {
1800         mEventData->mDecorator->SetPopupActive( false );
1801       }
1802       mEventData->mDecoratorUpdated = true;
1803     }
1804     else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1805     {
1806       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1807
1808       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1809       if( mEventData->mCursorBlinkEnabled )
1810       {
1811         mEventData->mDecorator->StartCursorBlink();
1812       }
1813       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1814       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1815       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1816       if( mEventData->mGrabHandlePopupEnabled )
1817       {
1818         mEventData->mDecorator->SetPopupActive( false );
1819       }
1820       mEventData->mDecoratorUpdated = true;
1821     }
1822     else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1823     {
1824       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1825
1826       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1827       if( mEventData->mCursorBlinkEnabled )
1828       {
1829         mEventData->mDecorator->StartCursorBlink();
1830       }
1831
1832       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1833       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1834       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1835
1836       if( mEventData->mGrabHandlePopupEnabled )
1837       {
1838         SetPopupButtons();
1839         mEventData->mDecorator->SetPopupActive( true );
1840       }
1841       HideClipboard();
1842       mEventData->mDecoratorUpdated = true;
1843     }
1844   }
1845 }
1846
1847 LineIndex Controller::Impl::GetClosestLine( float y ) const
1848 {
1849   float totalHeight = 0.f;
1850   LineIndex lineIndex = 0u;
1851
1852   const Vector<LineRun>& lines = mVisualModel->mLines;
1853   for( LineIndex endLine = lines.Count();
1854        lineIndex < endLine;
1855        ++lineIndex )
1856   {
1857     const LineRun& lineRun = lines[lineIndex];
1858     totalHeight += lineRun.ascender + -lineRun.descender;
1859     if( y < totalHeight )
1860     {
1861       return lineIndex;
1862     }
1863   }
1864
1865   if( lineIndex == 0 )
1866   {
1867     return 0;
1868   }
1869
1870   return lineIndex-1;
1871 }
1872
1873 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1874 {
1875   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1876   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1877
1878   if( mLogicalModel->mText.Count() == 0 )
1879   {
1880     return;  // if model empty
1881   }
1882
1883   if( hitCharacter >= mLogicalModel->mText.Count() )
1884   {
1885     // Closest hit character is the last character.
1886     if( hitCharacter ==  mLogicalModel->mText.Count() )
1887     {
1888       hitCharacter--; //Hit character index set to last character in logical model
1889     }
1890     else
1891     {
1892       // hitCharacter is out of bounds
1893       return;
1894     }
1895   }
1896
1897   startIndex = hitCharacter;
1898   endIndex = hitCharacter;
1899   bool isHitCharacterWhitespace = TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] );
1900
1901   // Find the start and end of the text
1902   for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1903   {
1904     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ startIndex-1 ] ) )
1905     {
1906       break;
1907     }
1908   }
1909   const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1910   for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1911   {
1912     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ endIndex ] ) )
1913     {
1914       break;
1915     }
1916   }
1917 }
1918
1919 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1920                                                         float visualY )
1921 {
1922   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1923
1924   if( NULL == mEventData )
1925   {
1926     // Nothing to do if there is no text input.
1927     return 0u;
1928   }
1929
1930   CharacterIndex logicalIndex = 0u;
1931
1932   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1933   const Length numberOfLines  = mVisualModel->mLines.Count();
1934   if( ( 0 == numberOfGlyphs ) ||
1935       ( 0 == numberOfLines ) )
1936   {
1937     return logicalIndex;
1938   }
1939
1940   // Find which line is closest
1941   const LineIndex lineIndex = GetClosestLine( visualY );
1942   const LineRun& line = mVisualModel->mLines[lineIndex];
1943
1944   // Get the positions of the glyphs.
1945   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1946   const Vector2* const positionsBuffer = positions.Begin();
1947
1948   // Get the visual to logical conversion tables.
1949   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1950   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1951
1952   // Get the character to glyph conversion table.
1953   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1954
1955   // Get the glyphs per character table.
1956   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1957
1958   // Get the glyph's info buffer.
1959   const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
1960
1961   // If the vector is void, there is no right to left characters.
1962   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1963
1964   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1965   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1966   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1967
1968   // Whether there is a hit on a glyph.
1969   bool matched = false;
1970
1971   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1972   CharacterIndex visualIndex = startCharacter;
1973   Length numberOfCharacters = 0u;
1974   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1975   {
1976     // The character in logical order.
1977     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1978
1979     // Get the script of the character.
1980     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1981
1982     // The number of glyphs for that character
1983     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1984     ++numberOfCharacters;
1985
1986
1987     if( 0u != numberOfGlyphs )
1988     {
1989       // Get the first character/glyph of the group of glyphs.
1990       const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfCharacters;
1991       const CharacterIndex firstLogicalCharacterIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + firstVisualCharacterIndex ) : firstVisualCharacterIndex;
1992       const GlyphIndex firstLogicalGlyphIndex = *( charactersToGlyphBuffer + firstLogicalCharacterIndex );
1993
1994       // Get the metrics for the group of glyphs.
1995       GlyphMetrics glyphMetrics;
1996       GetGlyphsMetrics( firstLogicalGlyphIndex,
1997                         numberOfGlyphs,
1998                         glyphMetrics,
1999                         glyphInfoBuffer,
2000                         mMetrics );
2001
2002       // Get the position of the first glyph.
2003       const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex );
2004
2005       // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ﻻ.
2006       const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script );
2007       const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u;
2008       const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfBlocks );
2009
2010       GlyphIndex index = 0u;
2011       for( ; !matched && ( index < numberOfBlocks ); ++index )
2012       {
2013         // Find the mid-point of the area containing the glyph
2014         const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
2015
2016         if( visualX < glyphCenter )
2017         {
2018           matched = true;
2019           break;
2020         }
2021       }
2022
2023       if( matched )
2024       {
2025         visualIndex = firstVisualCharacterIndex + index;
2026         break;
2027       }
2028
2029       numberOfCharacters = 0u;
2030     }
2031
2032   }
2033
2034
2035   // Return the logical position of the cursor in characters.
2036
2037   if( !matched )
2038   {
2039     visualIndex = endCharacter;
2040   }
2041
2042   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
2043   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
2044
2045   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
2046
2047   return logicalIndex;
2048 }
2049
2050 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2051                                           CursorInfo& cursorInfo )
2052 {
2053   // TODO: Check for multiline with \n, etc...
2054
2055   const Length numberOfCharacters = mLogicalModel->mText.Count();
2056   if( !IsShowingRealText() )
2057   {
2058     // Do not want to use the place-holder text to set the cursor position.
2059
2060     // Use the line's height of the font's family set to set the cursor's size.
2061     // If there is no font's family set, use the default font.
2062     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2063
2064     cursorInfo.lineOffset = 0.f;
2065     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2066     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2067
2068     switch( mLayoutEngine.GetHorizontalAlignment() )
2069     {
2070       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2071       {
2072         cursorInfo.primaryPosition.x = 0.f;
2073         break;
2074       }
2075       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2076       {
2077         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2078         break;
2079       }
2080       case LayoutEngine::HORIZONTAL_ALIGN_END:
2081       {
2082         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2083         break;
2084       }
2085     }
2086
2087     switch( mLayoutEngine.GetVerticalAlignment() )
2088     {
2089       case LayoutEngine::VERTICAL_ALIGN_TOP:
2090       {
2091         cursorInfo.primaryPosition.y = 0.f;
2092         break;
2093       }
2094       case LayoutEngine::VERTICAL_ALIGN_CENTER:
2095       {
2096         cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
2097         break;
2098       }
2099       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
2100       {
2101         cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
2102         break;
2103       }
2104     }
2105
2106     // Nothing else to do.
2107     return;
2108   }
2109
2110   // Check if the logical position is the first or the last one of the text.
2111   const bool isFirstPosition = 0u == logical;
2112   const bool isLastPosition = numberOfCharacters == logical;
2113
2114   // 'logical' is the logical 'cursor' index.
2115   // Get the next and current logical 'character' index.
2116   const CharacterIndex nextCharacterIndex = logical;
2117   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
2118
2119   // Get the direction of the character and the next one.
2120   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
2121
2122   CharacterDirection isCurrentRightToLeft = false;
2123   CharacterDirection isNextRightToLeft = false;
2124   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2125   {
2126     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
2127     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
2128   }
2129
2130   // Get the line where the character is laid-out.
2131   const LineRun* const modelLines = mVisualModel->mLines.Begin();
2132
2133   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
2134   const LineRun& line = *( modelLines + lineIndex );
2135
2136   // Get the paragraph's direction.
2137   const CharacterDirection isRightToLeftParagraph = line.direction;
2138
2139   // Check whether there is an alternative position:
2140
2141   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
2142                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
2143
2144   // Set the line offset and height.
2145   cursorInfo.lineOffset = 0.f;
2146   cursorInfo.lineHeight = line.ascender + -line.descender;
2147
2148   // Calculate the primary cursor.
2149
2150   CharacterIndex index = characterIndex;
2151   if( cursorInfo.isSecondaryCursor )
2152   {
2153     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
2154
2155     if( isLastPosition )
2156     {
2157       // The position of the cursor after the last character needs special
2158       // care depending on its direction and the direction of the paragraph.
2159
2160       // Need to find the first character after the last character with the paragraph's direction.
2161       // i.e l0 l1 l2 r0 r1 should find r0.
2162
2163       // TODO: check for more than one line!
2164       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
2165       index = mLogicalModel->GetLogicalCharacterIndex( index );
2166     }
2167     else
2168     {
2169       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
2170     }
2171   }
2172
2173   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2174   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
2175   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2176   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
2177   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
2178   const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
2179
2180   // Convert the cursor position into the glyph position.
2181   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
2182   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
2183   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
2184
2185   // Get the metrics for the group of glyphs.
2186   GlyphMetrics glyphMetrics;
2187   GetGlyphsMetrics( primaryGlyphIndex,
2188                     primaryNumberOfGlyphs,
2189                     glyphMetrics,
2190                     glyphInfoBuffer,
2191                     mMetrics );
2192
2193   // Whether to add the glyph's advance to the cursor position.
2194   // 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,
2195   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
2196   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
2197   //
2198   // FLCP A
2199   // ------
2200   // 0000 1
2201   // 0001 1
2202   // 0010 0
2203   // 0011 0
2204   // 0100 1
2205   // 0101 0
2206   // 0110 1
2207   // 0111 0
2208   // 1000 0
2209   // 1001 x
2210   // 1010 x
2211   // 1011 1
2212   // 1100 x
2213   // 1101 x
2214   // 1110 x
2215   // 1111 x
2216   //
2217   // Where F -> isFirstPosition
2218   //       L -> isLastPosition
2219   //       C -> isCurrentRightToLeft
2220   //       P -> isRightToLeftParagraph
2221   //       A -> Whether to add the glyph's advance.
2222
2223   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
2224                                  ( isFirstPosition && isRightToLeftParagraph ) ||
2225                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
2226
2227   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
2228
2229   if( !isLastPosition &&
2230       ( primaryNumberOfCharacters > 1u ) )
2231   {
2232     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
2233
2234     bool isCurrentRightToLeft = false;
2235     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2236     {
2237       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
2238     }
2239
2240     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
2241     if( isCurrentRightToLeft )
2242     {
2243       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
2244     }
2245
2246     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
2247   }
2248
2249   // Get the glyph position and x bearing.
2250   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
2251
2252   // Set the primary cursor's height.
2253   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
2254
2255   // Set the primary cursor's position.
2256   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
2257   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
2258
2259   // Calculate the secondary cursor.
2260
2261   if( cursorInfo.isSecondaryCursor )
2262   {
2263     // Set the secondary cursor's height.
2264     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
2265
2266     CharacterIndex index = characterIndex;
2267     if( !isLastPosition )
2268     {
2269       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
2270     }
2271
2272     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
2273     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
2274
2275     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
2276
2277     GetGlyphsMetrics( secondaryGlyphIndex,
2278                       secondaryNumberOfGlyphs,
2279                       glyphMetrics,
2280                       glyphInfoBuffer,
2281                       mMetrics );
2282
2283     // Set the secondary cursor's position.
2284     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
2285     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
2286   }
2287
2288   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2289   {
2290     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2291
2292     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2293     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2294
2295     if( 0.f > cursorInfo.primaryPosition.x )
2296     {
2297       cursorInfo.primaryPosition.x = 0.f;
2298     }
2299
2300     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2301     if( cursorInfo.primaryPosition.x > edgeWidth )
2302     {
2303       cursorInfo.primaryPosition.x = edgeWidth;
2304     }
2305   }
2306 }
2307
2308 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2309 {
2310   if( NULL == mEventData )
2311   {
2312     // Nothing to do if there is no text input.
2313     return 0u;
2314   }
2315
2316   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2317
2318   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2319   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2320
2321   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2322   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2323
2324   if( numberOfCharacters > 1u )
2325   {
2326     const Script script = mLogicalModel->GetScript( index );
2327     if( HasLigatureMustBreak( script ) )
2328     {
2329       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
2330       numberOfCharacters = 1u;
2331     }
2332   }
2333   else
2334   {
2335     while( 0u == numberOfCharacters )
2336     {
2337       ++glyphIndex;
2338       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2339     }
2340   }
2341
2342   if( index < mEventData->mPrimaryCursorPosition )
2343   {
2344     cursorIndex -= numberOfCharacters;
2345   }
2346   else
2347   {
2348     cursorIndex += numberOfCharacters;
2349   }
2350
2351   return cursorIndex;
2352 }
2353
2354 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2355 {
2356   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2357   if( NULL == mEventData )
2358   {
2359     // Nothing to do if there is no text input.
2360     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2361     return;
2362   }
2363
2364   const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
2365   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2366
2367   // Sets the cursor position.
2368   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2369                                        cursorPosition.x,
2370                                        cursorPosition.y,
2371                                        cursorInfo.primaryCursorHeight,
2372                                        cursorInfo.lineHeight );
2373   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2374
2375   // Sets the grab handle position.
2376   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2377                                        cursorPosition.x,
2378                                        cursorInfo.lineOffset + offset.y,
2379                                        cursorInfo.lineHeight );
2380
2381   if( cursorInfo.isSecondaryCursor )
2382   {
2383     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2384                                          cursorInfo.secondaryPosition.x + offset.x,
2385                                          cursorInfo.secondaryPosition.y + offset.y,
2386                                          cursorInfo.secondaryCursorHeight,
2387                                          cursorInfo.lineHeight );
2388     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2389   }
2390
2391   // Set which cursors are active according the state.
2392   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2393   {
2394     if( cursorInfo.isSecondaryCursor )
2395     {
2396       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2397     }
2398     else
2399     {
2400       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2401     }
2402   }
2403   else
2404   {
2405     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2406   }
2407
2408   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2409 }
2410
2411 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2412                                               const CursorInfo& cursorInfo )
2413 {
2414   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2415       ( RIGHT_SELECTION_HANDLE != handleType ) )
2416   {
2417     return;
2418   }
2419
2420   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
2421   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2422
2423   // Sets the handle's position.
2424   mEventData->mDecorator->SetPosition( handleType,
2425                                        cursorPosition.x,
2426                                        cursorInfo.lineOffset + offset.y,
2427                                        cursorInfo.lineHeight );
2428
2429   // If selection handle at start of the text and other at end of the text then all text is selected.
2430   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2431   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2432   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2433 }
2434
2435 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2436 {
2437   // Clamp between -space & 0 (and the text alignment).
2438
2439   if( actualSize.width > mVisualModel->mControlSize.width )
2440   {
2441     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
2442     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
2443     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
2444
2445     mEventData->mDecoratorUpdated = true;
2446   }
2447   else
2448   {
2449     mEventData->mScrollPosition.x = 0.f;
2450   }
2451 }
2452
2453 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2454 {
2455   // Clamp between -space & 0 (and the text alignment).
2456   if( actualSize.height > mVisualModel->mControlSize.height )
2457   {
2458     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
2459     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
2460     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
2461
2462     mEventData->mDecoratorUpdated = true;
2463   }
2464   else
2465   {
2466     mEventData->mScrollPosition.y = 0.f;
2467   }
2468 }
2469
2470 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2471 {
2472   // position is in actor's coords.
2473   const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
2474
2475   // Transform the position to decorator coords.
2476   const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
2477   const float offset = mEventData->mScrollPosition.x + alignment;
2478   const float decoratorPositionBegin = position.x + offset;
2479   const float decoratorPositionEnd = positionEnd + offset;
2480
2481   if( decoratorPositionBegin < 0.f )
2482   {
2483     mEventData->mScrollPosition.x = -position.x - alignment;
2484   }
2485   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2486   {
2487     mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
2488   }
2489 }
2490
2491 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2492 {
2493   // Get the current cursor position in decorator coords.
2494   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2495
2496   // Calculate the offset to match the cursor position before the character was deleted.
2497   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
2498
2499   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2500 }
2501
2502 void Controller::Impl::RequestRelayout()
2503 {
2504   mControlInterface.RequestTextRelayout();
2505 }
2506
2507 } // namespace Text
2508
2509 } // namespace Toolkit
2510
2511 } // namespace Dali