c3b2346c39423513f0a7d785f513ad3582c0a98f
[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 )
1394   {
1395     mClipboard.HideClipboard();
1396   }
1397 }
1398
1399 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1400 {
1401   //Send string to clipboard
1402   return ( mClipboard && mClipboard.SetItem( source ) );
1403 }
1404
1405 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1406 {
1407   std::string selectedText;
1408   RetrieveSelection( selectedText, deleteAfterSending );
1409   CopyStringToClipboard( selectedText );
1410   ChangeState( EventData::EDITING );
1411 }
1412
1413 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1414 {
1415   if ( mClipboard )
1416   {
1417     retrievedString =  mClipboard.GetItem( itemIndex );
1418   }
1419 }
1420
1421 void Controller::Impl::RepositionSelectionHandles()
1422 {
1423   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1424   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1425
1426   if( selectionStart == selectionEnd )
1427   {
1428     // Nothing to select if handles are in the same place.
1429     return;
1430   }
1431
1432   mEventData->mDecorator->ClearHighlights();
1433
1434   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1435   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1436   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1437   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1438   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1439   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1440   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1441
1442   // TODO: Better algorithm to create the highlight box.
1443   // TODO: Multi-line.
1444
1445   // Get the height of the line.
1446   const Vector<LineRun>& lines = mVisualModel->mLines;
1447   const LineRun& firstLine = *lines.Begin();
1448   const float height = firstLine.ascender + -firstLine.descender;
1449
1450   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1451   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1452   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1453
1454   // Swap the indices if the start is greater than the end.
1455   const bool indicesSwapped = selectionStart > selectionEnd;
1456
1457   // Tell the decorator to flip the selection handles if needed.
1458   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1459
1460   if( indicesSwapped )
1461   {
1462     std::swap( selectionStart, selectionEnd );
1463   }
1464
1465   // Get the indices to the first and last selected glyphs.
1466   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1467   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1468   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1469   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1470
1471   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1472   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1473   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1474
1475   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1476   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1477   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1478
1479   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1480
1481   // Traverse the glyphs.
1482   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1483   {
1484     const GlyphInfo& glyph = *( glyphsBuffer + index );
1485     const Vector2& position = *( positionsBuffer + index );
1486
1487     if( splitStartGlyph )
1488     {
1489       // 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.
1490
1491       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1492       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1493       // Get the direction of the character.
1494       CharacterDirection isCurrentRightToLeft = false;
1495       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1496       {
1497         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1498       }
1499
1500       // The end point could be in the middle of the ligature.
1501       // Calculate the number of characters selected.
1502       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1503
1504       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1505
1506       mEventData->mDecorator->AddHighlight( xPosition,
1507                                             offset.y,
1508                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1509                                             offset.y + height );
1510
1511       splitStartGlyph = false;
1512       continue;
1513     }
1514
1515     if( splitEndGlyph && ( index == glyphEnd ) )
1516     {
1517       // 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.
1518
1519       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1520       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1521       // Get the direction of the character.
1522       CharacterDirection isCurrentRightToLeft = false;
1523       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1524       {
1525         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1526       }
1527
1528       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1529
1530       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1531       mEventData->mDecorator->AddHighlight( xPosition,
1532                                             offset.y,
1533                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1534                                             offset.y + height );
1535
1536       splitEndGlyph = false;
1537       continue;
1538     }
1539
1540     const float xPosition = position.x - glyph.xBearing + offset.x;
1541     mEventData->mDecorator->AddHighlight( xPosition,
1542                                           offset.y,
1543                                           xPosition + glyph.advance,
1544                                           offset.y + height );
1545   }
1546
1547   CursorInfo primaryCursorInfo;
1548   GetCursorPosition( mEventData->mLeftSelectionPosition,
1549                      primaryCursorInfo );
1550
1551   CursorInfo secondaryCursorInfo;
1552   GetCursorPosition( mEventData->mRightSelectionPosition,
1553                      secondaryCursorInfo );
1554
1555   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1556   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1557
1558   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1559                                        primaryPosition.x,
1560                                        primaryCursorInfo.lineOffset + offset.y,
1561                                        primaryCursorInfo.lineHeight );
1562
1563   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1564                                        secondaryPosition.x,
1565                                        secondaryCursorInfo.lineOffset + offset.y,
1566                                        secondaryCursorInfo.lineHeight );
1567
1568   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1569   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1570
1571   // Set the flag to update the decorator.
1572   mEventData->mDecoratorUpdated = true;
1573 }
1574
1575 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1576 {
1577   if( NULL == mEventData )
1578   {
1579     // Nothing to do if there is no text input.
1580     return;
1581   }
1582
1583   if( IsShowingPlaceholderText() )
1584   {
1585     // Nothing to do if there is the place-holder text.
1586     return;
1587   }
1588
1589   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1590   const Length numberOfLines  = mVisualModel->mLines.Count();
1591   if( ( 0 == numberOfGlyphs ) ||
1592       ( 0 == numberOfLines ) )
1593   {
1594     // Nothing to do if there is no text.
1595     return;
1596   }
1597
1598   // Find which word was selected
1599   CharacterIndex selectionStart( 0 );
1600   CharacterIndex selectionEnd( 0 );
1601   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1602   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1603
1604   if( selectionStart == selectionEnd )
1605   {
1606     ChangeState( EventData::EDITING );
1607     // Nothing to select. i.e. a white space, out of bounds
1608     return;
1609   }
1610
1611   mEventData->mLeftSelectionPosition = selectionStart;
1612   mEventData->mRightSelectionPosition = selectionEnd;
1613 }
1614
1615 void Controller::Impl::SetPopupButtons()
1616 {
1617   /**
1618    *  Sets the Popup buttons to be shown depending on State.
1619    *
1620    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1621    *
1622    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1623    */
1624
1625   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1626
1627   if( EventData::SELECTING == mEventData->mState )
1628   {
1629     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1630
1631     if( !IsClipboardEmpty() )
1632     {
1633       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1634       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1635     }
1636
1637     if( !mEventData->mAllTextSelected )
1638     {
1639       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1640     }
1641   }
1642   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1643   {
1644     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1645     {
1646       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1647     }
1648
1649     if( !IsClipboardEmpty() )
1650     {
1651       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1652       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1653     }
1654   }
1655   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1656   {
1657     if ( !IsClipboardEmpty() )
1658     {
1659       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1660       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1661     }
1662   }
1663
1664   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1665 }
1666
1667 void Controller::Impl::ChangeState( EventData::State newState )
1668 {
1669   if( NULL == mEventData )
1670   {
1671     // Nothing to do if there is no text input.
1672     return;
1673   }
1674
1675   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
1676
1677   if( mEventData->mState != newState )
1678   {
1679     mEventData->mState = newState;
1680
1681     if( EventData::INACTIVE == mEventData->mState )
1682     {
1683       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1684       mEventData->mDecorator->StopCursorBlink();
1685       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1686       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1687       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1688       mEventData->mDecorator->SetPopupActive( false );
1689       mEventData->mDecoratorUpdated = true;
1690       HideClipboard();
1691     }
1692     else if( EventData::INTERRUPTED  == mEventData->mState)
1693     {
1694       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1695       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1696       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1697       mEventData->mDecorator->SetPopupActive( false );
1698       mEventData->mDecoratorUpdated = true;
1699       HideClipboard();
1700     }
1701     else if( EventData::SELECTING == mEventData->mState )
1702     {
1703       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1704       mEventData->mDecorator->StopCursorBlink();
1705       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1706       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1707       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1708       if( mEventData->mGrabHandlePopupEnabled )
1709       {
1710         SetPopupButtons();
1711         mEventData->mDecorator->SetPopupActive( true );
1712       }
1713       mEventData->mDecoratorUpdated = true;
1714     }
1715     else if( EventData::EDITING == mEventData->mState )
1716     {
1717       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1718       if( mEventData->mCursorBlinkEnabled )
1719       {
1720         mEventData->mDecorator->StartCursorBlink();
1721       }
1722       // Grab handle is not shown until a tap is received whilst EDITING
1723       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1724       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1725       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1726       if( mEventData->mGrabHandlePopupEnabled )
1727       {
1728         mEventData->mDecorator->SetPopupActive( false );
1729       }
1730       mEventData->mDecoratorUpdated = true;
1731       HideClipboard();
1732     }
1733     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1734     {
1735       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1736
1737       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1738       if( mEventData->mCursorBlinkEnabled )
1739       {
1740         mEventData->mDecorator->StartCursorBlink();
1741       }
1742       if( mEventData->mSelectionEnabled )
1743       {
1744         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1745         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1746       }
1747       else
1748       {
1749         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1750       }
1751       if( mEventData->mGrabHandlePopupEnabled )
1752       {
1753         SetPopupButtons();
1754         mEventData->mDecorator->SetPopupActive( true );
1755       }
1756       HideClipboard();
1757       mEventData->mDecoratorUpdated = true;
1758     }
1759     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1760     {
1761       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1762
1763       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1764       if( mEventData->mCursorBlinkEnabled )
1765       {
1766         mEventData->mDecorator->StartCursorBlink();
1767       }
1768       // Grab handle is not shown until a tap is received whilst EDITING
1769       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1770       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1771       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1772       if( mEventData->mGrabHandlePopupEnabled )
1773       {
1774         mEventData->mDecorator->SetPopupActive( false );
1775       }
1776       mEventData->mDecoratorUpdated = true;
1777       HideClipboard();
1778     }
1779     else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1780     {
1781       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1782       mEventData->mDecorator->StopCursorBlink();
1783       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1784       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1785       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1786       if( mEventData->mGrabHandlePopupEnabled )
1787       {
1788         mEventData->mDecorator->SetPopupActive( false );
1789       }
1790       mEventData->mDecoratorUpdated = true;
1791     }
1792     else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1793     {
1794       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1795
1796       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1797       if( mEventData->mCursorBlinkEnabled )
1798       {
1799         mEventData->mDecorator->StartCursorBlink();
1800       }
1801       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1802       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1803       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1804       if( mEventData->mGrabHandlePopupEnabled )
1805       {
1806         mEventData->mDecorator->SetPopupActive( false );
1807       }
1808       mEventData->mDecoratorUpdated = true;
1809     }
1810     else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1811     {
1812       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1813
1814       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1815       if( mEventData->mCursorBlinkEnabled )
1816       {
1817         mEventData->mDecorator->StartCursorBlink();
1818       }
1819
1820       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1821       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1822       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1823
1824       if( mEventData->mGrabHandlePopupEnabled )
1825       {
1826         SetPopupButtons();
1827         mEventData->mDecorator->SetPopupActive( true );
1828       }
1829       HideClipboard();
1830       mEventData->mDecoratorUpdated = true;
1831     }
1832   }
1833 }
1834
1835 LineIndex Controller::Impl::GetClosestLine( float y ) const
1836 {
1837   float totalHeight = 0.f;
1838   LineIndex lineIndex = 0u;
1839
1840   const Vector<LineRun>& lines = mVisualModel->mLines;
1841   for( LineIndex endLine = lines.Count();
1842        lineIndex < endLine;
1843        ++lineIndex )
1844   {
1845     const LineRun& lineRun = lines[lineIndex];
1846     totalHeight += lineRun.ascender + -lineRun.descender;
1847     if( y < totalHeight )
1848     {
1849       return lineIndex;
1850     }
1851   }
1852
1853   if( lineIndex == 0 )
1854   {
1855     return 0;
1856   }
1857
1858   return lineIndex-1;
1859 }
1860
1861 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1862 {
1863   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1864   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1865
1866   if( mLogicalModel->mText.Count() == 0 )
1867   {
1868     return;  // if model empty
1869   }
1870
1871   if( hitCharacter >= mLogicalModel->mText.Count() )
1872   {
1873     // Closest hit character is the last character.
1874     if( hitCharacter ==  mLogicalModel->mText.Count() )
1875     {
1876       hitCharacter--; //Hit character index set to last character in logical model
1877     }
1878     else
1879     {
1880       // hitCharacter is out of bounds
1881       return;
1882     }
1883   }
1884
1885   startIndex = hitCharacter;
1886   endIndex = hitCharacter;
1887   bool isHitCharacterWhitespace = TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] );
1888
1889   // Find the start and end of the text
1890   for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1891   {
1892     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ startIndex-1 ] ) )
1893     {
1894       break;
1895     }
1896   }
1897   const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1898   for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1899   {
1900     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ endIndex ] ) )
1901     {
1902       break;
1903     }
1904   }
1905 }
1906
1907 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1908                                                         float visualY )
1909 {
1910   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1911
1912   if( NULL == mEventData )
1913   {
1914     // Nothing to do if there is no text input.
1915     return 0u;
1916   }
1917
1918   CharacterIndex logicalIndex = 0u;
1919
1920   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1921   const Length numberOfLines  = mVisualModel->mLines.Count();
1922   if( ( 0 == numberOfGlyphs ) ||
1923       ( 0 == numberOfLines ) )
1924   {
1925     return logicalIndex;
1926   }
1927
1928   // Find which line is closest
1929   const LineIndex lineIndex = GetClosestLine( visualY );
1930   const LineRun& line = mVisualModel->mLines[lineIndex];
1931
1932   // Get the positions of the glyphs.
1933   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1934   const Vector2* const positionsBuffer = positions.Begin();
1935
1936   // Get the character to glyph conversion table.
1937   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1938
1939   // Get the glyphs per character table.
1940   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1941
1942   // Get the glyph's info buffer.
1943   const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
1944
1945   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1946   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1947   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1948
1949   // Whether there is a hit on a glyph.
1950   bool matched = false;
1951
1952   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1953   CharacterIndex visualIndex = startCharacter;
1954   Length numberOfCharacters = 0u;
1955   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1956   {
1957     // The character in logical order.
1958     const CharacterIndex characterLogicalOrderIndex = mLogicalModel->GetLogicalCharacterIndex( visualIndex );
1959
1960     // Get the script of the character.
1961     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1962
1963     // The number of glyphs for that character
1964     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1965     ++numberOfCharacters;
1966
1967
1968     if( 0u != numberOfGlyphs )
1969     {
1970       // Get the first character/glyph of the group of glyphs.
1971       const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfCharacters;
1972       const CharacterIndex firstLogicalCharacterIndex = mLogicalModel->GetLogicalCharacterIndex( firstVisualCharacterIndex );
1973       const GlyphIndex firstLogicalGlyphIndex = *( charactersToGlyphBuffer + firstLogicalCharacterIndex );
1974
1975       // Get the metrics for the group of glyphs.
1976       GlyphMetrics glyphMetrics;
1977       GetGlyphsMetrics( firstLogicalGlyphIndex,
1978                         numberOfGlyphs,
1979                         glyphMetrics,
1980                         glyphInfoBuffer,
1981                         mMetrics );
1982
1983       // Get the position of the first glyph.
1984       const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex );
1985
1986       // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ﻻ.
1987       const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script );
1988       const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u;
1989       const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfBlocks );
1990
1991       GlyphIndex index = 0u;
1992       for( ; !matched && ( index < numberOfBlocks ); ++index )
1993       {
1994         // Find the mid-point of the area containing the glyph
1995         const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1996
1997         if( visualX < glyphCenter )
1998         {
1999           matched = true;
2000           break;
2001         }
2002       }
2003
2004       if( matched )
2005       {
2006         visualIndex = firstVisualCharacterIndex + index;
2007         break;
2008       }
2009
2010       numberOfCharacters = 0u;
2011     }
2012
2013   }
2014
2015   // Return the logical position of the cursor in characters.
2016
2017   if( !matched )
2018   {
2019     visualIndex = endCharacter;
2020   }
2021
2022   logicalIndex = mLogicalModel->GetLogicalCursorIndex( visualIndex );
2023   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
2024
2025   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
2026
2027   return logicalIndex;
2028 }
2029
2030 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2031                                           CursorInfo& cursorInfo )
2032 {
2033   // TODO: Check for multiline with \n, etc...
2034
2035   const Length numberOfCharacters = mLogicalModel->mText.Count();
2036   if( !IsShowingRealText() )
2037   {
2038     // Do not want to use the place-holder text to set the cursor position.
2039
2040     // Use the line's height of the font's family set to set the cursor's size.
2041     // If there is no font's family set, use the default font.
2042     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2043
2044     cursorInfo.lineOffset = 0.f;
2045     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2046     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2047
2048     switch( mLayoutEngine.GetHorizontalAlignment() )
2049     {
2050       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2051       {
2052         cursorInfo.primaryPosition.x = 0.f;
2053         break;
2054       }
2055       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2056       {
2057         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2058         break;
2059       }
2060       case LayoutEngine::HORIZONTAL_ALIGN_END:
2061       {
2062         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2063         break;
2064       }
2065     }
2066
2067     switch( mLayoutEngine.GetVerticalAlignment() )
2068     {
2069       case LayoutEngine::VERTICAL_ALIGN_TOP:
2070       {
2071         cursorInfo.primaryPosition.y = 0.f;
2072         break;
2073       }
2074       case LayoutEngine::VERTICAL_ALIGN_CENTER:
2075       {
2076         cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
2077         break;
2078       }
2079       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
2080       {
2081         cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
2082         break;
2083       }
2084     }
2085
2086     // Nothing else to do.
2087     return;
2088   }
2089
2090   // Check if the logical position is the first or the last one of the text.
2091   const bool isFirstPosition = 0u == logical;
2092   const bool isLastPosition = numberOfCharacters == logical;
2093
2094   // 'logical' is the logical 'cursor' index.
2095   // Get the next and current logical 'character' index.
2096   const CharacterIndex nextCharacterIndex = logical;
2097   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
2098
2099   // Get the direction of the character and the next one.
2100   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
2101
2102   CharacterDirection isCurrentRightToLeft = false;
2103   CharacterDirection isNextRightToLeft = false;
2104   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2105   {
2106     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
2107     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
2108   }
2109
2110   // Get the line where the character is laid-out.
2111   const LineRun* const modelLines = mVisualModel->mLines.Begin();
2112
2113   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
2114   const LineRun& line = *( modelLines + lineIndex );
2115
2116   // Get the paragraph's direction.
2117   const CharacterDirection isRightToLeftParagraph = line.direction;
2118
2119   // Check whether there is an alternative position:
2120
2121   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
2122                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
2123
2124   // Set the line offset and height.
2125   cursorInfo.lineOffset = 0.f;
2126   cursorInfo.lineHeight = line.ascender + -line.descender;
2127
2128   // Calculate the primary cursor.
2129
2130   CharacterIndex index = characterIndex;
2131   if( cursorInfo.isSecondaryCursor )
2132   {
2133     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
2134
2135     if( isLastPosition )
2136     {
2137       // The position of the cursor after the last character needs special
2138       // care depending on its direction and the direction of the paragraph.
2139
2140       // Need to find the first character after the last character with the paragraph's direction.
2141       // i.e l0 l1 l2 r0 r1 should find r0.
2142
2143       // TODO: check for more than one line!
2144       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
2145       index = mLogicalModel->GetLogicalCharacterIndex( index );
2146     }
2147     else
2148     {
2149       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
2150     }
2151   }
2152
2153   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2154   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
2155   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2156   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
2157   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
2158   const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
2159
2160   // Convert the cursor position into the glyph position.
2161   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
2162   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
2163   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
2164
2165   // Get the metrics for the group of glyphs.
2166   GlyphMetrics glyphMetrics;
2167   GetGlyphsMetrics( primaryGlyphIndex,
2168                     primaryNumberOfGlyphs,
2169                     glyphMetrics,
2170                     glyphInfoBuffer,
2171                     mMetrics );
2172
2173   // Whether to add the glyph's advance to the cursor position.
2174   // 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,
2175   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
2176   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
2177   //
2178   // FLCP A
2179   // ------
2180   // 0000 1
2181   // 0001 1
2182   // 0010 0
2183   // 0011 0
2184   // 0100 1
2185   // 0101 0
2186   // 0110 1
2187   // 0111 0
2188   // 1000 0
2189   // 1001 x
2190   // 1010 x
2191   // 1011 1
2192   // 1100 x
2193   // 1101 x
2194   // 1110 x
2195   // 1111 x
2196   //
2197   // Where F -> isFirstPosition
2198   //       L -> isLastPosition
2199   //       C -> isCurrentRightToLeft
2200   //       P -> isRightToLeftParagraph
2201   //       A -> Whether to add the glyph's advance.
2202
2203   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
2204                                  ( isFirstPosition && isRightToLeftParagraph ) ||
2205                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
2206
2207   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
2208
2209   if( !isLastPosition &&
2210       ( primaryNumberOfCharacters > 1u ) )
2211   {
2212     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
2213
2214     bool isCurrentRightToLeft = false;
2215     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2216     {
2217       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
2218     }
2219
2220     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
2221     if( isCurrentRightToLeft )
2222     {
2223       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
2224     }
2225
2226     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
2227   }
2228
2229   // Get the glyph position and x bearing.
2230   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
2231
2232   // Set the primary cursor's height.
2233   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
2234
2235   // Set the primary cursor's position.
2236   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
2237   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
2238
2239   // Calculate the secondary cursor.
2240
2241   if( cursorInfo.isSecondaryCursor )
2242   {
2243     // Set the secondary cursor's height.
2244     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
2245
2246     CharacterIndex index = characterIndex;
2247     if( !isLastPosition )
2248     {
2249       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
2250     }
2251
2252     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
2253     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
2254
2255     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
2256
2257     GetGlyphsMetrics( secondaryGlyphIndex,
2258                       secondaryNumberOfGlyphs,
2259                       glyphMetrics,
2260                       glyphInfoBuffer,
2261                       mMetrics );
2262
2263     // Set the secondary cursor's position.
2264     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
2265     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
2266   }
2267
2268   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2269   {
2270     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2271
2272     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2273     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2274
2275     if( 0.f > cursorInfo.primaryPosition.x )
2276     {
2277       cursorInfo.primaryPosition.x = 0.f;
2278     }
2279
2280     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2281     if( cursorInfo.primaryPosition.x > edgeWidth )
2282     {
2283       cursorInfo.primaryPosition.x = edgeWidth;
2284     }
2285   }
2286 }
2287
2288 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2289 {
2290   if( NULL == mEventData )
2291   {
2292     // Nothing to do if there is no text input.
2293     return 0u;
2294   }
2295
2296   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2297
2298   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2299   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2300
2301   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2302   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2303
2304   if( numberOfCharacters > 1u )
2305   {
2306     const Script script = mLogicalModel->GetScript( index );
2307     if( HasLigatureMustBreak( script ) )
2308     {
2309       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
2310       numberOfCharacters = 1u;
2311     }
2312   }
2313   else
2314   {
2315     while( 0u == numberOfCharacters )
2316     {
2317       ++glyphIndex;
2318       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2319     }
2320   }
2321
2322   if( index < mEventData->mPrimaryCursorPosition )
2323   {
2324     cursorIndex -= numberOfCharacters;
2325   }
2326   else
2327   {
2328     cursorIndex += numberOfCharacters;
2329   }
2330
2331   return cursorIndex;
2332 }
2333
2334 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2335 {
2336   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2337   if( NULL == mEventData )
2338   {
2339     // Nothing to do if there is no text input.
2340     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2341     return;
2342   }
2343
2344   const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
2345   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2346
2347   // Sets the cursor position.
2348   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2349                                        cursorPosition.x,
2350                                        cursorPosition.y,
2351                                        cursorInfo.primaryCursorHeight,
2352                                        cursorInfo.lineHeight );
2353   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2354
2355   // Sets the grab handle position.
2356   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2357                                        cursorPosition.x,
2358                                        cursorInfo.lineOffset + offset.y,
2359                                        cursorInfo.lineHeight );
2360
2361   if( cursorInfo.isSecondaryCursor )
2362   {
2363     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2364                                          cursorInfo.secondaryPosition.x + offset.x,
2365                                          cursorInfo.secondaryPosition.y + offset.y,
2366                                          cursorInfo.secondaryCursorHeight,
2367                                          cursorInfo.lineHeight );
2368     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2369   }
2370
2371   // Set which cursors are active according the state.
2372   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2373   {
2374     if( cursorInfo.isSecondaryCursor )
2375     {
2376       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2377     }
2378     else
2379     {
2380       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2381     }
2382   }
2383   else
2384   {
2385     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2386   }
2387
2388   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2389 }
2390
2391 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2392                                               const CursorInfo& cursorInfo )
2393 {
2394   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2395       ( RIGHT_SELECTION_HANDLE != handleType ) )
2396   {
2397     return;
2398   }
2399
2400   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
2401   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2402
2403   // Sets the handle's position.
2404   mEventData->mDecorator->SetPosition( handleType,
2405                                        cursorPosition.x,
2406                                        cursorInfo.lineOffset + offset.y,
2407                                        cursorInfo.lineHeight );
2408
2409   // If selection handle at start of the text and other at end of the text then all text is selected.
2410   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2411   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2412   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2413 }
2414
2415 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2416 {
2417   // Clamp between -space & 0 (and the text alignment).
2418
2419   if( actualSize.width > mVisualModel->mControlSize.width )
2420   {
2421     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
2422     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
2423     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
2424
2425     mEventData->mDecoratorUpdated = true;
2426   }
2427   else
2428   {
2429     mEventData->mScrollPosition.x = 0.f;
2430   }
2431 }
2432
2433 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2434 {
2435   // Clamp between -space & 0 (and the text alignment).
2436   if( actualSize.height > mVisualModel->mControlSize.height )
2437   {
2438     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
2439     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
2440     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
2441
2442     mEventData->mDecoratorUpdated = true;
2443   }
2444   else
2445   {
2446     mEventData->mScrollPosition.y = 0.f;
2447   }
2448 }
2449
2450 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2451 {
2452   // position is in actor's coords.
2453   const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
2454
2455   // Transform the position to decorator coords.
2456   const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
2457   const float offset = mEventData->mScrollPosition.x + alignment;
2458   const float decoratorPositionBegin = position.x + offset;
2459   const float decoratorPositionEnd = positionEnd + offset;
2460
2461   if( decoratorPositionBegin < 0.f )
2462   {
2463     mEventData->mScrollPosition.x = -position.x - alignment;
2464   }
2465   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2466   {
2467     mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
2468   }
2469 }
2470
2471 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2472 {
2473   // Get the current cursor position in decorator coords.
2474   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2475
2476   // Calculate the offset to match the cursor position before the character was deleted.
2477   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
2478
2479   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2480 }
2481
2482 void Controller::Impl::RequestRelayout()
2483 {
2484   mControlInterface.RequestTextRelayout();
2485 }
2486
2487 } // namespace Text
2488
2489 } // namespace Toolkit
2490
2491 } // namespace Dali