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