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