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