Fix for multi-language support.
[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's description.
828       TextAbstraction::FontDescription defaultFontDescription;
829       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
830       if( NULL != mFontDefaults )
831       {
832         defaultFontDescription = mFontDefaults->mFontDescription;
833         defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
834       }
835
836       // Validates the fonts. If there is a character with no assigned font it sets a default one.
837       // After this call, fonts are validated.
838       multilanguageSupport.ValidateFonts( utf32Characters,
839                                           scripts,
840                                           fontDescriptionRuns,
841                                           defaultFontDescription,
842                                           defaultPointSize,
843                                           startIndex,
844                                           requestedNumberOfCharacters,
845                                           validFonts );
846     }
847     updated = true;
848   }
849
850   Vector<Character> mirroredUtf32Characters;
851   bool textMirrored = false;
852   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
853   if( NO_OPERATION != ( BIDI_INFO & operations ) )
854   {
855     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
856     bidirectionalInfo.Reserve( numberOfParagraphs );
857
858     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
859     SetBidirectionalInfo( utf32Characters,
860                           scripts,
861                           lineBreakInfo,
862                           startIndex,
863                           requestedNumberOfCharacters,
864                           bidirectionalInfo );
865
866     if( 0u != bidirectionalInfo.Count() )
867     {
868       // Only set the character directions if there is right to left characters.
869       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
870       GetCharactersDirection( bidirectionalInfo,
871                               numberOfCharacters,
872                               startIndex,
873                               requestedNumberOfCharacters,
874                               directions );
875
876       // This paragraph has right to left text. Some characters may need to be mirrored.
877       // TODO: consider if the mirrored string can be stored as well.
878
879       textMirrored = GetMirroredText( utf32Characters,
880                                       directions,
881                                       bidirectionalInfo,
882                                       startIndex,
883                                       requestedNumberOfCharacters,
884                                       mirroredUtf32Characters );
885     }
886     else
887     {
888       // There is no right to left characters. Clear the directions vector.
889       mLogicalModel->mCharacterDirections.Clear();
890     }
891     updated = true;
892   }
893
894   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
895   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
896   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
897   Vector<GlyphIndex> newParagraphGlyphs;
898   newParagraphGlyphs.Reserve( numberOfParagraphs );
899
900   const Length currentNumberOfGlyphs = glyphs.Count();
901   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
902   {
903     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
904     // Shapes the text.
905     ShapeText( textToShape,
906                lineBreakInfo,
907                scripts,
908                validFonts,
909                startIndex,
910                mTextUpdateInfo.mStartGlyphIndex,
911                requestedNumberOfCharacters,
912                glyphs,
913                glyphsToCharactersMap,
914                charactersPerGlyph,
915                newParagraphGlyphs );
916
917     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
918     mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
919     mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
920     updated = true;
921   }
922
923   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
924
925   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
926   {
927     GlyphInfo* glyphsBuffer = glyphs.Begin();
928     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
929
930     // Update the width and advance of all new paragraph characters.
931     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
932     {
933       const GlyphIndex index = *it;
934       GlyphInfo& glyph = *( glyphsBuffer + index );
935
936       glyph.xBearing = 0.f;
937       glyph.width = 0.f;
938       glyph.advance = 0.f;
939     }
940     updated = true;
941   }
942
943   if( NO_OPERATION != ( COLOR & operations ) )
944   {
945     // Set the color runs in glyphs.
946     SetColorSegmentationInfo( mLogicalModel->mColorRuns,
947                               mVisualModel->mCharactersToGlyph,
948                               mVisualModel->mGlyphsPerCharacter,
949                               startIndex,
950                               mTextUpdateInfo.mStartGlyphIndex,
951                               requestedNumberOfCharacters,
952                               mVisualModel->mColors,
953                               mVisualModel->mColorIndices );
954
955     updated = true;
956   }
957
958   if( ( NULL != mEventData ) &&
959       mEventData->mPreEditFlag &&
960       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
961   {
962     // Add the underline for the pre-edit text.
963     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
964     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
965
966     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
967     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
968     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
969     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
970
971     GlyphRun underlineRun;
972     underlineRun.glyphIndex = glyphStart;
973     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
974
975     // TODO: At the moment the underline runs are only for pre-edit.
976     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
977   }
978
979   // The estimated number of lines. Used to avoid reallocations when layouting.
980   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
981
982   // Set the previous number of characters for the next time the text is updated.
983   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
984
985   return updated;
986 }
987
988 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
989 {
990   // Sets the default text's color.
991   inputStyle.textColor = mTextColor;
992   inputStyle.isDefaultColor = true;
993
994   inputStyle.familyName.clear();
995   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
996   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
997   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
998   inputStyle.size = 0.f;
999
1000   inputStyle.familyDefined = false;
1001   inputStyle.weightDefined = false;
1002   inputStyle.widthDefined = false;
1003   inputStyle.slantDefined = false;
1004   inputStyle.sizeDefined = false;
1005
1006   // Sets the default font's family name, weight, width, slant and size.
1007   if( mFontDefaults )
1008   {
1009     if( mFontDefaults->familyDefined )
1010     {
1011       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1012       inputStyle.familyDefined = true;
1013     }
1014
1015     if( mFontDefaults->weightDefined )
1016     {
1017       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1018       inputStyle.weightDefined = true;
1019     }
1020
1021     if( mFontDefaults->widthDefined )
1022     {
1023       inputStyle.width = mFontDefaults->mFontDescription.width;
1024       inputStyle.widthDefined = true;
1025     }
1026
1027     if( mFontDefaults->slantDefined )
1028     {
1029       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1030       inputStyle.slantDefined = true;
1031     }
1032
1033     if( mFontDefaults->sizeDefined )
1034     {
1035       inputStyle.size = mFontDefaults->mDefaultPointSize;
1036       inputStyle.sizeDefined = true;
1037     }
1038   }
1039 }
1040
1041 float Controller::Impl::GetDefaultFontLineHeight()
1042 {
1043   FontId defaultFontId = 0u;
1044   if( NULL == mFontDefaults )
1045   {
1046     TextAbstraction::FontDescription fontDescription;
1047     defaultFontId = mFontClient.GetFontId( fontDescription );
1048   }
1049   else
1050   {
1051     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1052   }
1053
1054   Text::FontMetrics fontMetrics;
1055   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1056
1057   return( fontMetrics.ascender - fontMetrics.descender );
1058 }
1059
1060 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1061 {
1062   if( NULL == mEventData )
1063   {
1064     // Nothing to do if there is no text input.
1065     return;
1066   }
1067
1068   int keyCode = event.p1.mInt;
1069
1070   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1071   {
1072     if( mEventData->mPrimaryCursorPosition > 0u )
1073     {
1074       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1075     }
1076   }
1077   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1078   {
1079     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1080     {
1081       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1082     }
1083   }
1084   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1085   {
1086     // Get first the line index of the current cursor position index.
1087     CharacterIndex characterIndex = 0u;
1088
1089     if( mEventData->mPrimaryCursorPosition > 0u )
1090     {
1091       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1092     }
1093
1094     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1095
1096     if( lineIndex > 0u )
1097     {
1098       // Retrieve the cursor position info.
1099       CursorInfo cursorInfo;
1100       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1101                          cursorInfo );
1102
1103       // Get the line above.
1104       const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1105
1106       // Get the next hit 'y' point.
1107       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1108
1109       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1110       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1111                                                                         mLogicalModel,
1112                                                                         mMetrics,
1113                                                                         mEventData->mCursorHookPositionX,
1114                                                                         hitPointY );
1115     }
1116   }
1117   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1118   {
1119     // Get first the line index of the current cursor position index.
1120     CharacterIndex characterIndex = 0u;
1121
1122     if( mEventData->mPrimaryCursorPosition > 0u )
1123     {
1124       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1125     }
1126
1127     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1128
1129     if( lineIndex + 1u < mVisualModel->mLines.Count() )
1130     {
1131       // Retrieve the cursor position info.
1132       CursorInfo cursorInfo;
1133       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1134                          cursorInfo );
1135
1136       // Get the line below.
1137       const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1138
1139       // Get the next hit 'y' point.
1140       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1141
1142       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1143       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1144                                                                         mLogicalModel,
1145                                                                         mMetrics,
1146                                                                         mEventData->mCursorHookPositionX,
1147                                                                         hitPointY );
1148     }
1149   }
1150
1151   mEventData->mUpdateCursorPosition = true;
1152   mEventData->mUpdateInputStyle = true;
1153   mEventData->mScrollAfterUpdatePosition = true;
1154 }
1155
1156 void Controller::Impl::OnTapEvent( const Event& event )
1157 {
1158   if( NULL != mEventData )
1159   {
1160     const unsigned int tapCount = event.p1.mUint;
1161
1162     if( 1u == tapCount )
1163     {
1164       if( IsShowingRealText() )
1165       {
1166         // Convert from control's coords to text's coords.
1167         const float xPosition = event.p2.mFloat - mScrollPosition.x;
1168         const float yPosition = event.p3.mFloat - mScrollPosition.y;
1169
1170         // Keep the tap 'x' position. Used to move the cursor.
1171         mEventData->mCursorHookPositionX = xPosition;
1172
1173         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1174                                                                           mLogicalModel,
1175                                                                           mMetrics,
1176                                                                           xPosition,
1177                                                                           yPosition );
1178
1179         // When the cursor position is changing, delay cursor blinking
1180         mEventData->mDecorator->DelayCursorBlink();
1181       }
1182       else
1183       {
1184         mEventData->mPrimaryCursorPosition = 0u;
1185       }
1186
1187       mEventData->mUpdateCursorPosition = true;
1188       mEventData->mUpdateGrabHandlePosition = true;
1189       mEventData->mScrollAfterUpdatePosition = true;
1190       mEventData->mUpdateInputStyle = true;
1191
1192       // Notify the cursor position to the imf manager.
1193       if( mEventData->mImfManager )
1194       {
1195         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1196         mEventData->mImfManager.NotifyCursorPosition();
1197       }
1198     }
1199   }
1200 }
1201
1202 void Controller::Impl::OnPanEvent( const Event& event )
1203 {
1204   if( NULL == mEventData )
1205   {
1206     // Nothing to do if there is no text input.
1207     return;
1208   }
1209
1210   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1211   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1212
1213   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1214   {
1215     // Nothing to do if scrolling is not enabled.
1216     return;
1217   }
1218
1219   const int state = event.p1.mInt;
1220
1221   switch( state )
1222   {
1223     case Gesture::Started:
1224     {
1225       // Will remove the cursor, handles or text's popup, ...
1226       ChangeState( EventData::TEXT_PANNING );
1227       break;
1228     }
1229     case Gesture::Continuing:
1230     {
1231       const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1232       const Vector2 currentScroll = mScrollPosition;
1233
1234       if( isHorizontalScrollEnabled )
1235       {
1236         const float displacementX = event.p2.mFloat;
1237         mScrollPosition.x += displacementX;
1238
1239         ClampHorizontalScroll( layoutSize );
1240       }
1241
1242       if( isVerticalScrollEnabled )
1243       {
1244         const float displacementY = event.p3.mFloat;
1245         mScrollPosition.y += displacementY;
1246
1247         ClampVerticalScroll( layoutSize );
1248       }
1249
1250       mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1251       break;
1252     }
1253     case Gesture::Finished:
1254     case Gesture::Cancelled: // FALLTHROUGH
1255     {
1256       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1257       ChangeState( mEventData->mPreviousState );
1258       break;
1259     }
1260     default:
1261       break;
1262   }
1263 }
1264
1265 void Controller::Impl::OnLongPressEvent( const Event& event )
1266 {
1267   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1268
1269   if( EventData::EDITING == mEventData->mState )
1270   {
1271     ChangeState ( EventData::EDITING_WITH_POPUP );
1272     mEventData->mDecoratorUpdated = true;
1273   }
1274 }
1275
1276 void Controller::Impl::OnHandleEvent( const Event& event )
1277 {
1278   if( NULL == mEventData )
1279   {
1280     // Nothing to do if there is no text input.
1281     return;
1282   }
1283
1284   const unsigned int state = event.p1.mUint;
1285   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1286   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1287
1288   if( HANDLE_PRESSED == state )
1289   {
1290     // Convert from decorator's coords to text's coords.
1291     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1292     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1293
1294     // Need to calculate the handle's new position.
1295     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1296                                                                           mLogicalModel,
1297                                                                           mMetrics,
1298                                                                           xPosition,
1299                                                                           yPosition );
1300
1301     if( Event::GRAB_HANDLE_EVENT == event.type )
1302     {
1303       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1304
1305       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1306       {
1307         // Updates the cursor position if the handle's new position is different than the current one.
1308         mEventData->mUpdateCursorPosition = true;
1309         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1310         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1311         mEventData->mPrimaryCursorPosition = handleNewPosition;
1312       }
1313
1314       // 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.
1315       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1316     }
1317     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1318     {
1319       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1320
1321       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1322           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1323       {
1324         // Updates the highlight box if the handle's new position is different than the current one.
1325         mEventData->mUpdateHighlightBox = true;
1326         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1327         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1328         mEventData->mLeftSelectionPosition = handleNewPosition;
1329       }
1330
1331       // 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.
1332       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1333
1334       // Will define the order to scroll the text to match the handle position.
1335       mEventData->mIsLeftHandleSelected = true;
1336     }
1337     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1338     {
1339       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1340
1341       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1342           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1343       {
1344         // Updates the highlight box if the handle's new position is different than the current one.
1345         mEventData->mUpdateHighlightBox = true;
1346         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1347         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1348         mEventData->mRightSelectionPosition = handleNewPosition;
1349       }
1350
1351       // 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.
1352       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1353
1354       // Will define the order to scroll the text to match the handle position.
1355       mEventData->mIsLeftHandleSelected = false;
1356     }
1357   } // end ( HANDLE_PRESSED == state )
1358   else if( ( HANDLE_RELEASED == state ) ||
1359            handleStopScrolling )
1360   {
1361     CharacterIndex handlePosition = 0u;
1362     if( handleStopScrolling || isSmoothHandlePanEnabled )
1363     {
1364       // Convert from decorator's coords to text's coords.
1365       const float xPosition = event.p2.mFloat - mScrollPosition.x;
1366       const float yPosition = event.p3.mFloat - mScrollPosition.y;
1367
1368       handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1369                                                     mLogicalModel,
1370                                                     mMetrics,
1371                                                     xPosition,
1372                                                     yPosition );
1373     }
1374
1375     if( Event::GRAB_HANDLE_EVENT == event.type )
1376     {
1377       mEventData->mUpdateCursorPosition = true;
1378       mEventData->mUpdateGrabHandlePosition = true;
1379       mEventData->mUpdateInputStyle = true;
1380
1381       if( !IsClipboardEmpty() )
1382       {
1383         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1384       }
1385
1386       if( handleStopScrolling || isSmoothHandlePanEnabled )
1387       {
1388         mEventData->mScrollAfterUpdatePosition = true;
1389         mEventData->mPrimaryCursorPosition = handlePosition;
1390       }
1391     }
1392     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1393     {
1394       ChangeState( EventData::SELECTING );
1395
1396       mEventData->mUpdateHighlightBox = true;
1397       mEventData->mUpdateLeftSelectionPosition = true;
1398       mEventData->mUpdateRightSelectionPosition = true;
1399
1400       if( handleStopScrolling || isSmoothHandlePanEnabled )
1401       {
1402         mEventData->mScrollAfterUpdatePosition = true;
1403
1404         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1405             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1406         {
1407           mEventData->mLeftSelectionPosition = handlePosition;
1408         }
1409       }
1410     }
1411     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1412     {
1413       ChangeState( EventData::SELECTING );
1414
1415       mEventData->mUpdateHighlightBox = true;
1416       mEventData->mUpdateRightSelectionPosition = true;
1417       mEventData->mUpdateLeftSelectionPosition = true;
1418
1419       if( handleStopScrolling || isSmoothHandlePanEnabled )
1420       {
1421         mEventData->mScrollAfterUpdatePosition = true;
1422         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1423             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1424         {
1425           mEventData->mRightSelectionPosition = handlePosition;
1426         }
1427       }
1428     }
1429
1430     mEventData->mDecoratorUpdated = true;
1431   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1432   else if( HANDLE_SCROLLING == state )
1433   {
1434     const float xSpeed = event.p2.mFloat;
1435     const float ySpeed = event.p3.mFloat;
1436     const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1437     const Vector2 currentScrollPosition = mScrollPosition;
1438
1439     mScrollPosition.x += xSpeed;
1440     mScrollPosition.y += ySpeed;
1441
1442     ClampHorizontalScroll( layoutSize );
1443     ClampVerticalScroll( layoutSize );
1444
1445     bool endOfScroll = false;
1446     if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1447     {
1448       // Notify the decorator there is no more text to scroll.
1449       // The decorator won't send more scroll events.
1450       mEventData->mDecorator->NotifyEndOfScroll();
1451       // Still need to set the position of the handle.
1452       endOfScroll = true;
1453     }
1454
1455     // Set the position of the handle.
1456     const bool scrollRightDirection = xSpeed > 0.f;
1457     const bool scrollBottomDirection = ySpeed > 0.f;
1458     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1459     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1460
1461     if( Event::GRAB_HANDLE_EVENT == event.type )
1462     {
1463       ChangeState( EventData::GRAB_HANDLE_PANNING );
1464
1465       // Get the grab handle position in decorator coords.
1466       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1467
1468       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1469       {
1470         // Position the grag handle close to either the left or right edge.
1471         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1472       }
1473
1474       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1475       {
1476         position.x = mEventData->mCursorHookPositionX;
1477
1478         // Position the grag handle close to either the top or bottom edge.
1479         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1480       }
1481
1482       // Get the new handle position.
1483       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1484       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1485                                                                          mLogicalModel,
1486                                                                          mMetrics,
1487                                                                          position.x - mScrollPosition.x,
1488                                                                          position.y - mScrollPosition.y );
1489
1490       if( mEventData->mPrimaryCursorPosition != handlePosition )
1491       {
1492         mEventData->mUpdateCursorPosition = true;
1493         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1494         mEventData->mScrollAfterUpdatePosition = true;
1495         mEventData->mPrimaryCursorPosition = handlePosition;
1496       }
1497       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1498
1499       // Updates the decorator if the soft handle panning is enabled.
1500       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1501     }
1502     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1503     {
1504       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1505
1506       // Get the selection handle position in decorator coords.
1507       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1508
1509       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1510       {
1511         // Position the selection handle close to either the left or right edge.
1512         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1513       }
1514
1515       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1516       {
1517         position.x = mEventData->mCursorHookPositionX;
1518
1519         // Position the grag handle close to either the top or bottom edge.
1520         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1521       }
1522
1523       // Get the new handle position.
1524       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1525       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1526                                                                          mLogicalModel,
1527                                                                          mMetrics,
1528                                                                          position.x - mScrollPosition.x,
1529                                                                          position.y - mScrollPosition.y );
1530
1531       if( leftSelectionHandleEvent )
1532       {
1533         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1534
1535         if( differentHandles || endOfScroll )
1536         {
1537           mEventData->mUpdateHighlightBox = true;
1538           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1539           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1540           mEventData->mLeftSelectionPosition = handlePosition;
1541         }
1542       }
1543       else
1544       {
1545         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1546         if( differentHandles || endOfScroll )
1547         {
1548           mEventData->mUpdateHighlightBox = true;
1549           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1550           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1551           mEventData->mRightSelectionPosition = handlePosition;
1552         }
1553       }
1554
1555       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1556       {
1557         RepositionSelectionHandles();
1558
1559         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1560       }
1561     }
1562     mEventData->mDecoratorUpdated = true;
1563   } // end ( HANDLE_SCROLLING == state )
1564 }
1565
1566 void Controller::Impl::OnSelectEvent( const Event& event )
1567 {
1568   if( NULL == mEventData )
1569   {
1570     // Nothing to do if there is no text.
1571     return;
1572   }
1573
1574   if( mEventData->mSelectionEnabled )
1575   {
1576     // Convert from control's coords to text's coords.
1577     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1578     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1579
1580     // Calculates the logical position from the x,y coords.
1581     RepositionSelectionHandles( xPosition,
1582                                 yPosition );
1583   }
1584 }
1585
1586 void Controller::Impl::OnSelectAllEvent()
1587 {
1588   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1589
1590   if( NULL == mEventData )
1591   {
1592     // Nothing to do if there is no text.
1593     return;
1594   }
1595
1596   if( mEventData->mSelectionEnabled )
1597   {
1598     ChangeState( EventData::SELECTING );
1599
1600     mEventData->mLeftSelectionPosition = 0u;
1601     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1602
1603     mEventData->mScrollAfterUpdatePosition = true;
1604     mEventData->mUpdateLeftSelectionPosition = true;
1605     mEventData->mUpdateRightSelectionPosition = true;
1606     mEventData->mUpdateHighlightBox = true;
1607   }
1608 }
1609
1610 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1611 {
1612   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1613   {
1614     // Nothing to select if handles are in the same place.
1615     selectedText.clear();
1616     return;
1617   }
1618
1619   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1620
1621   //Get start and end position of selection
1622   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1623   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1624
1625   Vector<Character>& utf32Characters = mLogicalModel->mText;
1626   const Length numberOfCharacters = utf32Characters.Count();
1627
1628   // Validate the start and end selection points
1629   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1630   {
1631     //Get text as a UTF8 string
1632     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1633
1634     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1635     {
1636       // Set as input style the style of the first deleted character.
1637       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1638
1639       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1640
1641       // Mark the paragraphs to be updated.
1642       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1643       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1644
1645       // Delete text between handles
1646       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1647       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1648       utf32Characters.Erase( first, last );
1649
1650       // Will show the cursor at the first character of the selection.
1651       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1652     }
1653     else
1654     {
1655       // Will show the cursor at the last character of the selection.
1656       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1657     }
1658
1659     mEventData->mDecoratorUpdated = true;
1660   }
1661 }
1662
1663 void Controller::Impl::ShowClipboard()
1664 {
1665   if( mClipboard )
1666   {
1667     mClipboard.ShowClipboard();
1668   }
1669 }
1670
1671 void Controller::Impl::HideClipboard()
1672 {
1673   if( mClipboard && mClipboardHideEnabled )
1674   {
1675     mClipboard.HideClipboard();
1676   }
1677 }
1678
1679 void Controller::Impl::SetClipboardHideEnable(bool enable)
1680 {
1681   mClipboardHideEnabled = enable;
1682 }
1683
1684 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1685 {
1686   //Send string to clipboard
1687   return ( mClipboard && mClipboard.SetItem( source ) );
1688 }
1689
1690 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1691 {
1692   std::string selectedText;
1693   RetrieveSelection( selectedText, deleteAfterSending );
1694   CopyStringToClipboard( selectedText );
1695   ChangeState( EventData::EDITING );
1696 }
1697
1698 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1699 {
1700   if ( mClipboard )
1701   {
1702     retrievedString =  mClipboard.GetItem( itemIndex );
1703   }
1704 }
1705
1706 void Controller::Impl::RepositionSelectionHandles()
1707 {
1708   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1709   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1710
1711   if( selectionStart == selectionEnd )
1712   {
1713     // Nothing to select if handles are in the same place.
1714     return;
1715   }
1716
1717   mEventData->mDecorator->ClearHighlights();
1718
1719   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1720   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1721   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1722   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1723   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1724   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1725   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1726
1727   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1728   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1729   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1730
1731   // Swap the indices if the start is greater than the end.
1732   const bool indicesSwapped = selectionStart > selectionEnd;
1733
1734   // Tell the decorator to flip the selection handles if needed.
1735   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1736
1737   if( indicesSwapped )
1738   {
1739     std::swap( selectionStart, selectionEnd );
1740   }
1741
1742   // Get the indices to the first and last selected glyphs.
1743   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1744   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1745   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1746   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1747
1748   // Get the lines where the glyphs are laid-out.
1749   const LineRun* lineRun = mVisualModel->mLines.Begin();
1750
1751   LineIndex lineIndex = 0u;
1752   Length numberOfLines = 0u;
1753   mVisualModel->GetNumberOfLines( glyphStart,
1754                                   1u + glyphEnd - glyphStart,
1755                                   lineIndex,
1756                                   numberOfLines );
1757   const LineIndex firstLineIndex = lineIndex;
1758
1759   // Create the structure to store some selection box info.
1760   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1761   selectionBoxLinesInfo.Resize( numberOfLines );
1762
1763   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1764   selectionBoxInfo->minX = MAX_FLOAT;
1765   selectionBoxInfo->maxX = MIN_FLOAT;
1766
1767   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1768   float minHighlightX = std::numeric_limits<float>::max();
1769   float maxHighlightX = std::numeric_limits<float>::min();
1770   Size highLightSize;
1771   Vector2 highLightPosition; // The highlight position in decorator's coords.
1772
1773   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1774
1775   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1776   selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1777                                                       firstLineIndex );
1778
1779   // Transform to decorator's (control) coords.
1780   selectionBoxInfo->lineOffset += mScrollPosition.y;
1781
1782   lineRun += firstLineIndex;
1783
1784   // The line height is the addition of the line ascender and the line descender.
1785   // However, the line descender has a negative value, hence the subtraction.
1786   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1787
1788   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1789
1790   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1791   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1792   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1793
1794   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1795   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1796   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1797
1798   // The number of quads of the selection box.
1799   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1800   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1801
1802   // Count the actual number of quads.
1803   unsigned int actualNumberOfQuads = 0u;
1804   Vector4 quad;
1805
1806   // Traverse the glyphs.
1807   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1808   {
1809     const GlyphInfo& glyph = *( glyphsBuffer + index );
1810     const Vector2& position = *( positionsBuffer + index );
1811
1812     if( splitStartGlyph )
1813     {
1814       // 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.
1815
1816       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1817       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1818       // Get the direction of the character.
1819       CharacterDirection isCurrentRightToLeft = false;
1820       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1821       {
1822         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1823       }
1824
1825       // The end point could be in the middle of the ligature.
1826       // Calculate the number of characters selected.
1827       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1828
1829       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1830       quad.y = selectionBoxInfo->lineOffset;
1831       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1832       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1833
1834       // Store the min and max 'x' for each line.
1835       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1836       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1837
1838       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1839       ++actualNumberOfQuads;
1840
1841       splitStartGlyph = false;
1842       continue;
1843     }
1844
1845     if( splitEndGlyph && ( index == glyphEnd ) )
1846     {
1847       // 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.
1848
1849       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1850       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1851       // Get the direction of the character.
1852       CharacterDirection isCurrentRightToLeft = false;
1853       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1854       {
1855         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1856       }
1857
1858       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1859
1860       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1861       quad.y = selectionBoxInfo->lineOffset;
1862       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1863       quad.w = quad.y + selectionBoxInfo->lineHeight;
1864
1865       // Store the min and max 'x' for each line.
1866       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1867       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1868
1869       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1870                                             quad );
1871       ++actualNumberOfQuads;
1872
1873       splitEndGlyph = false;
1874       continue;
1875     }
1876
1877     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1878     quad.y = selectionBoxInfo->lineOffset;
1879     quad.z = quad.x + glyph.advance;
1880     quad.w = quad.y + selectionBoxInfo->lineHeight;
1881
1882     // Store the min and max 'x' for each line.
1883     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1884     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1885
1886     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1887                                           quad );
1888     ++actualNumberOfQuads;
1889
1890     // Whether to retrieve the next line.
1891     if( index == lastGlyphOfLine )
1892     {
1893       // Retrieve the next line.
1894       ++lineRun;
1895
1896       // Get the last glyph of the new line.
1897       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1898
1899       ++lineIndex;
1900       if( lineIndex < firstLineIndex + numberOfLines )
1901       {
1902         // Keep the offset and height of the current selection box.
1903         const float currentLineOffset = selectionBoxInfo->lineOffset;
1904         const float currentLineHeight = selectionBoxInfo->lineHeight;
1905
1906         // Get the selection box info for the next line.
1907         ++selectionBoxInfo;
1908
1909         selectionBoxInfo->minX = MAX_FLOAT;
1910         selectionBoxInfo->maxX = MIN_FLOAT;
1911
1912         // Update the line's vertical offset.
1913         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1914
1915         // The line height is the addition of the line ascender and the line descender.
1916         // However, the line descender has a negative value, hence the subtraction.
1917         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1918       }
1919     }
1920   }
1921
1922   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1923   // The final width is calculated after 'boxifying' the selection.
1924   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1925          endIt = selectionBoxLinesInfo.End();
1926        it != endIt;
1927        ++it )
1928   {
1929     const SelectionBoxInfo& info = *it;
1930
1931     // Update the size of the highlighted text.
1932     highLightSize.height += info.lineHeight;
1933     minHighlightX = std::min( minHighlightX, info.minX );
1934     maxHighlightX = std::max( maxHighlightX, info.maxX );
1935   }
1936
1937   // Add extra geometry to 'boxify' the selection.
1938
1939   if( 1u < numberOfLines )
1940   {
1941     // Boxify the first line.
1942     lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1943     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1944
1945     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1946     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1947
1948     if( boxifyBegin )
1949     {
1950       quad.x = 0.f;
1951       quad.y = firstSelectionBoxLineInfo.lineOffset;
1952       quad.z = firstSelectionBoxLineInfo.minX;
1953       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1954
1955       // Boxify at the beginning of the line.
1956       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1957                                             quad );
1958       ++actualNumberOfQuads;
1959
1960       // Update the size of the highlighted text.
1961       minHighlightX = 0.f;
1962     }
1963
1964     if( boxifyEnd )
1965     {
1966       quad.x = firstSelectionBoxLineInfo.maxX;
1967       quad.y = firstSelectionBoxLineInfo.lineOffset;
1968       quad.z = mVisualModel->mControlSize.width;
1969       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1970
1971       // Boxify at the end of the line.
1972       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1973                                             quad );
1974       ++actualNumberOfQuads;
1975
1976       // Update the size of the highlighted text.
1977       maxHighlightX = mVisualModel->mControlSize.width;
1978     }
1979
1980     // Boxify the central lines.
1981     if( 2u < numberOfLines )
1982     {
1983       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1984              endIt = selectionBoxLinesInfo.End() - 1u;
1985            it != endIt;
1986            ++it )
1987       {
1988         const SelectionBoxInfo& info = *it;
1989
1990         quad.x = 0.f;
1991         quad.y = info.lineOffset;
1992         quad.z = info.minX;
1993         quad.w = info.lineOffset + info.lineHeight;
1994
1995         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1996                                               quad );
1997         ++actualNumberOfQuads;
1998
1999         quad.x = info.maxX;
2000         quad.y = info.lineOffset;
2001         quad.z = mVisualModel->mControlSize.width;
2002         quad.w = info.lineOffset + info.lineHeight;
2003
2004         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2005                                               quad );
2006         ++actualNumberOfQuads;
2007       }
2008
2009       // Update the size of the highlighted text.
2010       minHighlightX = 0.f;
2011       maxHighlightX = mVisualModel->mControlSize.width;
2012     }
2013
2014     // Boxify the last line.
2015     lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2016     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2017
2018     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2019     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2020
2021     if( boxifyBegin )
2022     {
2023       quad.x = 0.f;
2024       quad.y = lastSelectionBoxLineInfo.lineOffset;
2025       quad.z = lastSelectionBoxLineInfo.minX;
2026       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2027
2028       // Boxify at the beginning of the line.
2029       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2030                                             quad );
2031       ++actualNumberOfQuads;
2032
2033       // Update the size of the highlighted text.
2034       minHighlightX = 0.f;
2035     }
2036
2037     if( boxifyEnd )
2038     {
2039       quad.x = lastSelectionBoxLineInfo.maxX;
2040       quad.y = lastSelectionBoxLineInfo.lineOffset;
2041       quad.z = mVisualModel->mControlSize.width;
2042       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2043
2044       // Boxify at the end of the line.
2045       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2046                                             quad );
2047       ++actualNumberOfQuads;
2048
2049       // Update the size of the highlighted text.
2050       maxHighlightX = mVisualModel->mControlSize.width;
2051     }
2052   }
2053
2054   // Set the actual number of quads.
2055   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2056
2057   // Sets the highlight's size and position. In decorator's coords.
2058   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2059   highLightSize.width = maxHighlightX - minHighlightX;
2060
2061   highLightPosition.x = minHighlightX;
2062   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2063   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2064
2065   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2066
2067   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2068   {
2069     CursorInfo primaryCursorInfo;
2070     GetCursorPosition( mEventData->mLeftSelectionPosition,
2071                        primaryCursorInfo );
2072
2073     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2074
2075     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2076                                          primaryPosition.x,
2077                                          primaryCursorInfo.lineOffset + mScrollPosition.y,
2078                                          primaryCursorInfo.lineHeight );
2079
2080     CursorInfo secondaryCursorInfo;
2081     GetCursorPosition( mEventData->mRightSelectionPosition,
2082                        secondaryCursorInfo );
2083
2084     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2085
2086     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2087                                          secondaryPosition.x,
2088                                          secondaryCursorInfo.lineOffset + mScrollPosition.y,
2089                                          secondaryCursorInfo.lineHeight );
2090   }
2091
2092   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2093   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2094
2095   // Set the flag to update the decorator.
2096   mEventData->mDecoratorUpdated = true;
2097 }
2098
2099 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2100 {
2101   if( NULL == mEventData )
2102   {
2103     // Nothing to do if there is no text input.
2104     return;
2105   }
2106
2107   if( IsShowingPlaceholderText() )
2108   {
2109     // Nothing to do if there is the place-holder text.
2110     return;
2111   }
2112
2113   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2114   const Length numberOfLines  = mVisualModel->mLines.Count();
2115   if( ( 0 == numberOfGlyphs ) ||
2116       ( 0 == numberOfLines ) )
2117   {
2118     // Nothing to do if there is no text.
2119     return;
2120   }
2121
2122   // Find which word was selected
2123   CharacterIndex selectionStart( 0 );
2124   CharacterIndex selectionEnd( 0 );
2125   const bool indicesFound = FindSelectionIndices( mVisualModel,
2126                                                   mLogicalModel,
2127                                                   mMetrics,
2128                                                   visualX,
2129                                                   visualY,
2130                                                   selectionStart,
2131                                                   selectionEnd );
2132   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2133
2134   if( indicesFound )
2135   {
2136     ChangeState( EventData::SELECTING );
2137
2138     mEventData->mLeftSelectionPosition = selectionStart;
2139     mEventData->mRightSelectionPosition = selectionEnd;
2140
2141     mEventData->mUpdateLeftSelectionPosition = true;
2142     mEventData->mUpdateRightSelectionPosition = true;
2143     mEventData->mUpdateHighlightBox = true;
2144
2145     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2146   }
2147   else
2148   {
2149     // Nothing to select. i.e. a white space, out of bounds
2150     ChangeState( EventData::EDITING );
2151
2152     mEventData->mPrimaryCursorPosition = selectionEnd;
2153
2154     mEventData->mUpdateCursorPosition = true;
2155     mEventData->mUpdateGrabHandlePosition = true;
2156     mEventData->mScrollAfterUpdatePosition = true;
2157     mEventData->mUpdateInputStyle = true;
2158   }
2159 }
2160
2161 void Controller::Impl::SetPopupButtons()
2162 {
2163   /**
2164    *  Sets the Popup buttons to be shown depending on State.
2165    *
2166    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2167    *
2168    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2169    */
2170
2171   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2172
2173   if( EventData::SELECTING == mEventData->mState )
2174   {
2175     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2176
2177     if( !IsClipboardEmpty() )
2178     {
2179       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2180       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2181     }
2182
2183     if( !mEventData->mAllTextSelected )
2184     {
2185       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2186     }
2187   }
2188   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2189   {
2190     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2191     {
2192       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2193     }
2194
2195     if( !IsClipboardEmpty() )
2196     {
2197       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2198       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2199     }
2200   }
2201   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2202   {
2203     if ( !IsClipboardEmpty() )
2204     {
2205       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2206       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2207     }
2208   }
2209
2210   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2211 }
2212
2213 void Controller::Impl::ChangeState( EventData::State newState )
2214 {
2215   if( NULL == mEventData )
2216   {
2217     // Nothing to do if there is no text input.
2218     return;
2219   }
2220
2221   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2222
2223   if( mEventData->mState != newState )
2224   {
2225     mEventData->mPreviousState = mEventData->mState;
2226     mEventData->mState = newState;
2227
2228     switch( mEventData->mState )
2229     {
2230       case EventData::INACTIVE:
2231       {
2232         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2233         mEventData->mDecorator->StopCursorBlink();
2234         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2235         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2236         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2237         mEventData->mDecorator->SetHighlightActive( false );
2238         mEventData->mDecorator->SetPopupActive( false );
2239         mEventData->mDecoratorUpdated = true;
2240         HideClipboard();
2241         break;
2242       }
2243       case EventData::INTERRUPTED:
2244       {
2245         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2246         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2247         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2248         mEventData->mDecorator->SetHighlightActive( false );
2249         mEventData->mDecorator->SetPopupActive( false );
2250         mEventData->mDecoratorUpdated = true;
2251         HideClipboard();
2252         break;
2253       }
2254       case EventData::SELECTING:
2255       {
2256         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2257         mEventData->mDecorator->StopCursorBlink();
2258         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2259         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2260         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2261         mEventData->mDecorator->SetHighlightActive( true );
2262         if( mEventData->mGrabHandlePopupEnabled )
2263         {
2264           SetPopupButtons();
2265           mEventData->mDecorator->SetPopupActive( true );
2266         }
2267         mEventData->mDecoratorUpdated = true;
2268         break;
2269       }
2270       case EventData::EDITING:
2271       {
2272         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2273         if( mEventData->mCursorBlinkEnabled )
2274         {
2275           mEventData->mDecorator->StartCursorBlink();
2276         }
2277         // Grab handle is not shown until a tap is received whilst EDITING
2278         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2279         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2280         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2281         mEventData->mDecorator->SetHighlightActive( false );
2282         if( mEventData->mGrabHandlePopupEnabled )
2283         {
2284           mEventData->mDecorator->SetPopupActive( false );
2285         }
2286         mEventData->mDecoratorUpdated = true;
2287         HideClipboard();
2288         break;
2289       }
2290       case EventData::EDITING_WITH_POPUP:
2291       {
2292         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2293
2294         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2295         if( mEventData->mCursorBlinkEnabled )
2296         {
2297           mEventData->mDecorator->StartCursorBlink();
2298         }
2299         if( mEventData->mSelectionEnabled )
2300         {
2301           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2302           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2303           mEventData->mDecorator->SetHighlightActive( false );
2304         }
2305         else
2306         {
2307           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2308         }
2309         if( mEventData->mGrabHandlePopupEnabled )
2310         {
2311           SetPopupButtons();
2312           mEventData->mDecorator->SetPopupActive( true );
2313         }
2314         HideClipboard();
2315         mEventData->mDecoratorUpdated = true;
2316         break;
2317       }
2318       case EventData::EDITING_WITH_GRAB_HANDLE:
2319       {
2320         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2321
2322         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2323         if( mEventData->mCursorBlinkEnabled )
2324         {
2325           mEventData->mDecorator->StartCursorBlink();
2326         }
2327         // Grab handle is not shown until a tap is received whilst EDITING
2328         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2329         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2330         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2331         mEventData->mDecorator->SetHighlightActive( false );
2332         if( mEventData->mGrabHandlePopupEnabled )
2333         {
2334           mEventData->mDecorator->SetPopupActive( false );
2335         }
2336         mEventData->mDecoratorUpdated = true;
2337         HideClipboard();
2338         break;
2339       }
2340       case EventData::SELECTION_HANDLE_PANNING:
2341       {
2342         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2343         mEventData->mDecorator->StopCursorBlink();
2344         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2345         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2346         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2347         mEventData->mDecorator->SetHighlightActive( true );
2348         if( mEventData->mGrabHandlePopupEnabled )
2349         {
2350           mEventData->mDecorator->SetPopupActive( false );
2351         }
2352         mEventData->mDecoratorUpdated = true;
2353         break;
2354       }
2355       case EventData::GRAB_HANDLE_PANNING:
2356       {
2357         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2358
2359         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2360         if( mEventData->mCursorBlinkEnabled )
2361         {
2362           mEventData->mDecorator->StartCursorBlink();
2363         }
2364         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2365         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2366         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2367         mEventData->mDecorator->SetHighlightActive( false );
2368         if( mEventData->mGrabHandlePopupEnabled )
2369         {
2370           mEventData->mDecorator->SetPopupActive( false );
2371         }
2372         mEventData->mDecoratorUpdated = true;
2373         break;
2374       }
2375       case EventData::EDITING_WITH_PASTE_POPUP:
2376       {
2377         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2378
2379         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2380         if( mEventData->mCursorBlinkEnabled )
2381         {
2382           mEventData->mDecorator->StartCursorBlink();
2383         }
2384
2385         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2386         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2387         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2388         mEventData->mDecorator->SetHighlightActive( false );
2389
2390         if( mEventData->mGrabHandlePopupEnabled )
2391         {
2392           SetPopupButtons();
2393           mEventData->mDecorator->SetPopupActive( true );
2394         }
2395         HideClipboard();
2396         mEventData->mDecoratorUpdated = true;
2397         break;
2398       }
2399       case EventData::TEXT_PANNING:
2400       {
2401         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2402         mEventData->mDecorator->StopCursorBlink();
2403         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2404         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2405             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2406         {
2407           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2408           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2409           mEventData->mDecorator->SetHighlightActive( true );
2410         }
2411
2412         if( mEventData->mGrabHandlePopupEnabled )
2413         {
2414           mEventData->mDecorator->SetPopupActive( false );
2415         }
2416
2417         mEventData->mDecoratorUpdated = true;
2418         break;
2419       }
2420     }
2421   }
2422 }
2423
2424 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2425                                           CursorInfo& cursorInfo )
2426 {
2427   if( !IsShowingRealText() )
2428   {
2429     // Do not want to use the place-holder text to set the cursor position.
2430
2431     // Use the line's height of the font's family set to set the cursor's size.
2432     // If there is no font's family set, use the default font.
2433     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2434
2435     cursorInfo.lineOffset = 0.f;
2436     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2437     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2438
2439     switch( mLayoutEngine.GetHorizontalAlignment() )
2440     {
2441       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2442       {
2443         cursorInfo.primaryPosition.x = 0.f;
2444         break;
2445       }
2446       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2447       {
2448         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2449         break;
2450       }
2451       case LayoutEngine::HORIZONTAL_ALIGN_END:
2452       {
2453         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2454         break;
2455       }
2456     }
2457
2458     // Nothing else to do.
2459     return;
2460   }
2461
2462   Text::GetCursorPosition( mVisualModel,
2463                            mLogicalModel,
2464                            mMetrics,
2465                            logical,
2466                            cursorInfo );
2467
2468   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2469   {
2470     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2471
2472     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2473     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2474
2475     if( 0.f > cursorInfo.primaryPosition.x )
2476     {
2477       cursorInfo.primaryPosition.x = 0.f;
2478     }
2479
2480     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2481     if( cursorInfo.primaryPosition.x > edgeWidth )
2482     {
2483       cursorInfo.primaryPosition.x = edgeWidth;
2484     }
2485   }
2486 }
2487
2488 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2489 {
2490   if( NULL == mEventData )
2491   {
2492     // Nothing to do if there is no text input.
2493     return 0u;
2494   }
2495
2496   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2497
2498   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2499   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2500
2501   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2502   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2503
2504   if( numberOfCharacters > 1u )
2505   {
2506     const Script script = mLogicalModel->GetScript( index );
2507     if( HasLigatureMustBreak( script ) )
2508     {
2509       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»,  ...
2510       numberOfCharacters = 1u;
2511     }
2512   }
2513   else
2514   {
2515     while( 0u == numberOfCharacters )
2516     {
2517       ++glyphIndex;
2518       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2519     }
2520   }
2521
2522   if( index < mEventData->mPrimaryCursorPosition )
2523   {
2524     cursorIndex -= numberOfCharacters;
2525   }
2526   else
2527   {
2528     cursorIndex += numberOfCharacters;
2529   }
2530
2531   // Will update the cursor hook position.
2532   mEventData->mUpdateCursorHookPosition = true;
2533
2534   return cursorIndex;
2535 }
2536
2537 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2538 {
2539   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2540   if( NULL == mEventData )
2541   {
2542     // Nothing to do if there is no text input.
2543     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2544     return;
2545   }
2546
2547   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2548
2549   // Sets the cursor position.
2550   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2551                                        cursorPosition.x,
2552                                        cursorPosition.y,
2553                                        cursorInfo.primaryCursorHeight,
2554                                        cursorInfo.lineHeight );
2555   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2556
2557   if( mEventData->mUpdateGrabHandlePosition )
2558   {
2559     // Sets the grab handle position.
2560     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2561                                          cursorPosition.x,
2562                                          cursorInfo.lineOffset + mScrollPosition.y,
2563                                          cursorInfo.lineHeight );
2564   }
2565
2566   if( cursorInfo.isSecondaryCursor )
2567   {
2568     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2569                                          cursorInfo.secondaryPosition.x + mScrollPosition.x,
2570                                          cursorInfo.secondaryPosition.y + mScrollPosition.y,
2571                                          cursorInfo.secondaryCursorHeight,
2572                                          cursorInfo.lineHeight );
2573     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2574   }
2575
2576   // Set which cursors are active according the state.
2577   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2578   {
2579     if( cursorInfo.isSecondaryCursor )
2580     {
2581       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2582     }
2583     else
2584     {
2585       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2586     }
2587   }
2588   else
2589   {
2590     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2591   }
2592
2593   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2594 }
2595
2596 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2597                                               const CursorInfo& cursorInfo )
2598 {
2599   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2600       ( RIGHT_SELECTION_HANDLE != handleType ) )
2601   {
2602     return;
2603   }
2604
2605   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2606
2607   // Sets the handle's position.
2608   mEventData->mDecorator->SetPosition( handleType,
2609                                        cursorPosition.x,
2610                                        cursorInfo.lineOffset + mScrollPosition.y,
2611                                        cursorInfo.lineHeight );
2612
2613   // If selection handle at start of the text and other at end of the text then all text is selected.
2614   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2615   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2616   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2617 }
2618
2619 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2620 {
2621   // Clamp between -space & 0.
2622
2623   if( layoutSize.width > mVisualModel->mControlSize.width )
2624   {
2625     const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2626     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2627     mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2628
2629     mEventData->mDecoratorUpdated = true;
2630   }
2631   else
2632   {
2633     mScrollPosition.x = 0.f;
2634   }
2635 }
2636
2637 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2638 {
2639   // Clamp between -space & 0.
2640   if( layoutSize.height > mVisualModel->mControlSize.height )
2641   {
2642     const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2643     mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2644     mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2645
2646     mEventData->mDecoratorUpdated = true;
2647   }
2648   else
2649   {
2650     mScrollPosition.y = 0.f;
2651   }
2652 }
2653
2654 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2655 {
2656   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2657
2658   // position is in actor's coords.
2659   const float positionEndX = position.x + cursorWidth;
2660   const float positionEndY = position.y + lineHeight;
2661
2662   // Transform the position to decorator coords.
2663   const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2664   const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2665
2666   const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2667   const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2668
2669   if( decoratorPositionBeginX < 0.f )
2670   {
2671     mScrollPosition.x = -position.x;
2672   }
2673   else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2674   {
2675     mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2676   }
2677
2678   if( decoratorPositionBeginY < 0.f )
2679   {
2680     mScrollPosition.y = -position.y;
2681   }
2682   else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2683   {
2684     mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2685   }
2686 }
2687
2688 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2689 {
2690   // Get the current cursor position in decorator coords.
2691   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2692
2693   // Calculate the offset to match the cursor position before the character was deleted.
2694   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2695   mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2696
2697   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2698   ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2699
2700   // Makes the new cursor position visible if needed.
2701   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2702 }
2703
2704 void Controller::Impl::RequestRelayout()
2705 {
2706   mControlInterface.RequestTextRelayout();
2707 }
2708
2709 } // namespace Text
2710
2711 } // namespace Toolkit
2712
2713 } // namespace Dali