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