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