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