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