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