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