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