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