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