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