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