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