e533a3f051acdb9860d230c07b7b5d081ae802b1
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/multi-language-support.h>
29 #include <dali-toolkit/internal/text/segmentation.h>
30 #include <dali-toolkit/internal/text/shaper.h>
31
32 namespace
33 {
34
35 #if defined(DEBUG_ENABLED)
36   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
37 #endif
38
39 /**
40  * @brief Some characters can be shaped in more than one glyph.
41  * This struct is used to retrieve metrics from these group of glyphs.
42  */
43 struct GlyphMetrics
44 {
45   GlyphMetrics()
46   : fontHeight( 0.f ),
47     advance( 0.f ),
48     ascender( 0.f ),
49     xBearing( 0.f )
50   {}
51
52   ~GlyphMetrics()
53   {}
54
55   float fontHeight; ///< The font's height of that glyphs.
56   float advance;    ///< The sum of all the advances of all the glyphs.
57   float ascender;   ///< The font's ascender.
58   float xBearing;   ///< The x bearing of the first glyph.
59 };
60
61 } // namespace
62
63 namespace Dali
64 {
65
66 namespace Toolkit
67 {
68
69 namespace Text
70 {
71
72 /**
73  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
74  *
75  * @param[in] glyphIndex The index to the first glyph.
76  * @param[in] numberOfGlyphs The number of glyphs.
77  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
78  * @param[in] visualModel The visual model.
79  * @param[in] metrics Used to access metrics from FontClient.
80  */
81 void GetGlyphsMetrics( GlyphIndex glyphIndex,
82                        Length numberOfGlyphs,
83                        GlyphMetrics& glyphMetrics,
84                        VisualModelPtr& visualModel,
85                        MetricsPtr& metrics )
86 {
87   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
88
89   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
90
91   Text::FontMetrics fontMetrics;
92   metrics->GetFontMetrics( firstGlyph.fontId, fontMetrics );
93
94   glyphMetrics.fontHeight = fontMetrics.height;
95   glyphMetrics.advance = firstGlyph.advance;
96   glyphMetrics.ascender = fontMetrics.ascender;
97   glyphMetrics.xBearing = firstGlyph.xBearing;
98
99   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
100   {
101     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
102
103     glyphMetrics.advance += glyphInfo.advance;
104   }
105 }
106
107 EventData::EventData( DecoratorPtr decorator )
108 : mDecorator( decorator ),
109   mPlaceholderTextActive(),
110   mPlaceholderTextInactive(),
111   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
112   mEventQueue(),
113   mScrollPosition(),
114   mState( INACTIVE ),
115   mPrimaryCursorPosition( 0u ),
116   mLeftSelectionPosition( 0u ),
117   mRightSelectionPosition( 0u ),
118   mPreEditStartPosition( 0u ),
119   mPreEditLength( 0u ),
120   mIsShowingPlaceholderText( false ),
121   mPreEditFlag( false ),
122   mDecoratorUpdated( false ),
123   mCursorBlinkEnabled( true ),
124   mGrabHandleEnabled( true ),
125   mGrabHandlePopupEnabled( true ),
126   mSelectionEnabled( true ),
127   mHorizontalScrollingEnabled( true ),
128   mVerticalScrollingEnabled( false ),
129   mUpdateCursorPosition( false ),
130   mUpdateLeftSelectionPosition( false ),
131   mUpdateRightSelectionPosition( false ),
132   mScrollAfterUpdatePosition( false ),
133   mScrollAfterDelete( false ),
134   mAllTextSelected( false )
135 {}
136
137 EventData::~EventData()
138 {}
139
140 bool Controller::Impl::ProcessInputEvents()
141 {
142   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
143   if( NULL == mEventData )
144   {
145     // Nothing to do if there is no text input.
146     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
147     return false;
148   }
149
150   if( mEventData->mDecorator )
151   {
152     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
153          iter != mEventData->mEventQueue.end();
154          ++iter )
155     {
156       switch( iter->type )
157       {
158         case Event::CURSOR_KEY_EVENT:
159         {
160           OnCursorKeyEvent( *iter );
161           break;
162         }
163         case Event::TAP_EVENT:
164         {
165           OnTapEvent( *iter );
166           break;
167         }
168         case Event::LONG_PRESS_EVENT:
169         {
170           OnLongPressEvent( *iter );
171           break;
172         }
173         case Event::PAN_EVENT:
174         {
175           OnPanEvent( *iter );
176           break;
177         }
178         case Event::GRAB_HANDLE_EVENT:
179         case Event::LEFT_SELECTION_HANDLE_EVENT:
180         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
181         {
182           OnHandleEvent( *iter );
183           break;
184         }
185         case Event::SELECT:
186         {
187           OnSelectEvent( *iter );
188           break;
189         }
190         case Event::SELECT_ALL:
191         {
192           OnSelectAllEvent();
193           break;
194         }
195       }
196     }
197   }
198
199   // The cursor must also be repositioned after inserts into the model
200   if( mEventData->mUpdateCursorPosition )
201   {
202     // Updates the cursor position and scrolls the text to make it visible.
203
204     UpdateCursorPosition();
205
206     if( mEventData->mScrollAfterUpdatePosition )
207     {
208       const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
209
210       ScrollToMakePositionVisible( primaryCursorPosition );
211       mEventData->mScrollAfterUpdatePosition = false;
212     }
213
214     mEventData->mDecoratorUpdated = true;
215     mEventData->mUpdateCursorPosition = false;
216   }
217   else if( mEventData->mScrollAfterDelete )
218   {
219     ScrollTextToMatchCursor();
220     mEventData->mDecoratorUpdated = true;
221     mEventData->mScrollAfterDelete = false;
222   }
223   else
224   {
225     bool leftScroll = false;
226     bool rightScroll = false;
227
228     if( mEventData->mUpdateLeftSelectionPosition )
229     {
230       UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
231
232       if( mEventData->mScrollAfterUpdatePosition )
233       {
234         const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
235
236         ScrollToMakePositionVisible( leftHandlePosition );
237         leftScroll = true;
238       }
239
240       SetPopupButtons();
241       mEventData->mDecoratorUpdated = true;
242       mEventData->mUpdateLeftSelectionPosition = false;
243     }
244
245     if( mEventData->mUpdateRightSelectionPosition )
246     {
247       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
248
249       if( mEventData->mScrollAfterUpdatePosition )
250       {
251         const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
252
253         ScrollToMakePositionVisible( rightHandlePosition );
254         rightScroll = true;
255       }
256
257       SetPopupButtons();
258       mEventData->mDecoratorUpdated = true;
259       mEventData->mUpdateRightSelectionPosition = false;
260     }
261
262     if( leftScroll || rightScroll )
263     {
264       mEventData->mScrollAfterUpdatePosition = false;
265     }
266   }
267
268   mEventData->mEventQueue.clear();
269
270   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
271
272   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
273   mEventData->mDecoratorUpdated = false;
274
275   return decoratorUpdated;
276 }
277
278 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
279 {
280   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
281
282   // Calculate the operations to be done.
283   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
284
285   Vector<Character>& utf32Characters = mLogicalModel->mText;
286
287   const Length numberOfCharacters = utf32Characters.Count();
288
289   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
290   if( GET_LINE_BREAKS & operations )
291   {
292     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
293     // calculate the bidirectional info for each 'paragraph'.
294     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
295     // is not shaped together).
296     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
297
298     SetLineBreakInfo( utf32Characters,
299                       lineBreakInfo );
300   }
301
302   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
303   if( GET_WORD_BREAKS & operations )
304   {
305     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
306     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
307
308     SetWordBreakInfo( utf32Characters,
309                       wordBreakInfo );
310   }
311
312   const bool getScripts = GET_SCRIPTS & operations;
313   const bool validateFonts = VALIDATE_FONTS & operations;
314
315   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
316   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
317
318   if( getScripts || validateFonts )
319   {
320     // Validates the fonts assigned by the application or assigns default ones.
321     // It makes sure all the characters are going to be rendered by the correct font.
322     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
323
324     if( getScripts )
325     {
326       // Retrieves the scripts used in the text.
327       multilanguageSupport.SetScripts( utf32Characters,
328                                        scripts );
329     }
330
331     if( validateFonts )
332     {
333       if( 0u == validFonts.Count() )
334       {
335         // Copy the requested font defaults received via the property system.
336         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
337         GetDefaultFonts( validFonts, numberOfCharacters );
338       }
339
340       // Validates the fonts. If there is a character with no assigned font it sets a default one.
341       // After this call, fonts are validated.
342       multilanguageSupport.ValidateFonts( utf32Characters,
343                                           scripts,
344                                           validFonts );
345     }
346   }
347
348   Vector<Character> mirroredUtf32Characters;
349   bool textMirrored = false;
350   Length numberOfParagraphs = 0u;
351   if( BIDI_INFO & operations )
352   {
353     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
354     // bidirectional info.
355
356     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
357     for( Length index = 0u; index < numberOfCharacters; ++index )
358     {
359       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
360       {
361         ++numberOfParagraphs;
362       }
363     }
364
365     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
366     bidirectionalInfo.Reserve( numberOfParagraphs );
367
368     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
369     SetBidirectionalInfo( utf32Characters,
370                           scripts,
371                           lineBreakInfo,
372                           bidirectionalInfo );
373
374     if( 0u != bidirectionalInfo.Count() )
375     {
376       // This paragraph has right to left text. Some characters may need to be mirrored.
377       // TODO: consider if the mirrored string can be stored as well.
378
379       textMirrored = GetMirroredText( utf32Characters,
380                                       mirroredUtf32Characters,
381                                       bidirectionalInfo );
382
383       // Only set the character directions if there is right to left characters.
384       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
385       directions.Resize( numberOfCharacters );
386
387       GetCharactersDirection( bidirectionalInfo,
388                               directions );
389     }
390     else
391     {
392       // There is no right to left characters. Clear the directions vector.
393       mLogicalModel->mCharacterDirections.Clear();
394     }
395   }
396
397   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
398   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
399   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
400   Vector<GlyphIndex> newParagraphGlyphs;
401   newParagraphGlyphs.Reserve( numberOfParagraphs );
402
403   if( SHAPE_TEXT & operations )
404   {
405     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
406     // Shapes the text.
407     ShapeText( textToShape,
408                lineBreakInfo,
409                scripts,
410                validFonts,
411                glyphs,
412                glyphsToCharactersMap,
413                charactersPerGlyph,
414                newParagraphGlyphs );
415
416     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
417     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
418     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
419   }
420
421   const Length numberOfGlyphs = glyphs.Count();
422
423   if( GET_GLYPH_METRICS & operations )
424   {
425     GlyphInfo* glyphsBuffer = glyphs.Begin();
426     mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
427
428     // Update the width and advance of all new paragraph characters.
429     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
430     {
431       const GlyphIndex index = *it;
432       GlyphInfo& glyph = *( glyphsBuffer + index );
433
434       glyph.xBearing = 0.f;
435       glyph.width = 0.f;
436       glyph.advance = 0.f;
437     }
438   }
439
440   if( mEventData &&
441       mEventData->mPreEditFlag &&
442       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
443   {
444     // Add the underline for the pre-edit text.
445     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
446     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
447
448     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
449     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
450     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
451     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
452
453     GlyphRun underlineRun;
454     underlineRun.glyphIndex = glyphStart;
455     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
456
457     // TODO: At the moment the underline runs are only for pre-edit.
458     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
459   }
460 }
461
462 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
463 {
464   if( mFontDefaults )
465   {
466     DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::GetDefaultFonts font family(%s)\n", mFontDefaults->mFontDescription.family.c_str() );
467     FontRun fontRun;
468     fontRun.characterRun.characterIndex = 0;
469     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
470     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
471     fontRun.isDefault = true;
472
473     fonts.PushBack( fontRun );
474   }
475 }
476
477 float Controller::Impl::GetDefaultFontLineHeight()
478 {
479   FontId defaultFontId = 0u;
480   if( NULL == mFontDefaults )
481   {
482     TextAbstraction::FontDescription fontDescription;
483     defaultFontId = mFontClient.GetFontId( fontDescription );
484   }
485   else
486   {
487     defaultFontId = mFontDefaults->GetFontId( mFontClient );
488   }
489
490   Text::FontMetrics fontMetrics;
491   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
492
493   return( fontMetrics.ascender - fontMetrics.descender );
494 }
495
496 void Controller::Impl::OnCursorKeyEvent( const Event& event )
497 {
498   if( NULL == mEventData )
499   {
500     // Nothing to do if there is no text input.
501     return;
502   }
503
504   int keyCode = event.p1.mInt;
505
506   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
507   {
508     if( mEventData->mPrimaryCursorPosition > 0u )
509     {
510       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
511     }
512   }
513   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
514   {
515     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
516     {
517       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
518     }
519   }
520   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
521   {
522     // TODO
523   }
524   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
525   {
526     // TODO
527   }
528
529   mEventData->mUpdateCursorPosition = true;
530   mEventData->mScrollAfterUpdatePosition = true;
531 }
532
533 void Controller::Impl::OnTapEvent( const Event& event )
534 {
535   if( NULL != mEventData )
536   {
537     const unsigned int tapCount = event.p1.mUint;
538
539     if( 1u == tapCount )
540     {
541       if( IsShowingRealText() )
542       {
543         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
544         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
545
546         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
547                                                                     yPosition );
548
549         // When the cursor position is changing, delay cursor blinking
550         mEventData->mDecorator->DelayCursorBlink();
551       }
552       else
553       {
554         mEventData->mPrimaryCursorPosition = 0u;
555       }
556
557       mEventData->mUpdateCursorPosition = true;
558       mEventData->mScrollAfterUpdatePosition = true;
559     }
560   }
561 }
562
563 void Controller::Impl::OnPanEvent( const Event& event )
564 {
565   if( NULL == mEventData )
566   {
567     // Nothing to do if there is no text input.
568     return;
569   }
570
571   int state = event.p1.mInt;
572
573   if( Gesture::Started    == state ||
574       Gesture::Continuing == state )
575   {
576     const Vector2& actualSize = mVisualModel->GetActualSize();
577     const Vector2 currentScroll = mEventData->mScrollPosition;
578
579     if( mEventData->mHorizontalScrollingEnabled )
580     {
581       const float displacementX = event.p2.mFloat;
582       mEventData->mScrollPosition.x += displacementX;
583
584       ClampHorizontalScroll( actualSize );
585     }
586
587     if( mEventData->mVerticalScrollingEnabled )
588     {
589       const float displacementY = event.p3.mFloat;
590       mEventData->mScrollPosition.y += displacementY;
591
592       ClampVerticalScroll( actualSize );
593     }
594
595     if( mEventData->mDecorator )
596     {
597       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
598     }
599   }
600 }
601
602 void Controller::Impl::OnLongPressEvent( const Event& event )
603 {
604   if  ( EventData::EDITING == mEventData->mState )
605   {
606     ChangeState ( EventData::EDITING_WITH_POPUP );
607     mEventData->mDecoratorUpdated = true;
608   }
609 }
610
611 void Controller::Impl::OnHandleEvent( const Event& event )
612 {
613   if( NULL == mEventData )
614   {
615     // Nothing to do if there is no text input.
616     return;
617   }
618
619   const unsigned int state = event.p1.mUint;
620   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
621
622   if( HANDLE_PRESSED == state )
623   {
624     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
625     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
626     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
627
628     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
629
630     if( Event::GRAB_HANDLE_EVENT == event.type )
631     {
632       ChangeState ( EventData::GRAB_HANDLE_PANNING );
633
634       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
635       {
636         mEventData->mPrimaryCursorPosition = handleNewPosition;
637         mEventData->mUpdateCursorPosition = true;
638       }
639     }
640     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
641     {
642       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
643
644       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
645           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
646       {
647         mEventData->mLeftSelectionPosition = handleNewPosition;
648
649         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
650                                     mEventData->mRightSelectionPosition );
651
652         mEventData->mUpdateLeftSelectionPosition = true;
653       }
654     }
655     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
656     {
657       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
658
659       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
660           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
661       {
662         mEventData->mRightSelectionPosition = handleNewPosition;
663
664         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
665                                     mEventData->mRightSelectionPosition );
666
667         mEventData->mUpdateRightSelectionPosition = true;
668       }
669     }
670   } // end ( HANDLE_PRESSED == state )
671   else if( ( HANDLE_RELEASED == state ) ||
672            handleStopScrolling )
673   {
674     CharacterIndex handlePosition = 0u;
675     if( handleStopScrolling )
676     {
677       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
678       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
679       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
680
681       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
682     }
683
684     if( Event::GRAB_HANDLE_EVENT == event.type )
685     {
686       mEventData->mUpdateCursorPosition = true;
687
688       ChangeState( EventData::EDITING_WITH_POPUP );
689
690       if( handleStopScrolling )
691       {
692         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
693         mEventData->mPrimaryCursorPosition = handlePosition;
694       }
695     }
696     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
697     {
698       ChangeState( EventData::SELECTING );
699
700       if( handleStopScrolling )
701       {
702         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
703         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
704
705         if( mEventData->mUpdateLeftSelectionPosition )
706         {
707           mEventData->mLeftSelectionPosition = handlePosition;
708
709           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
710                                       mEventData->mRightSelectionPosition );
711         }
712       }
713     }
714     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
715     {
716       ChangeState( EventData::SELECTING );
717
718       if( handleStopScrolling )
719       {
720         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
721         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
722         if( mEventData->mUpdateRightSelectionPosition )
723         {
724           mEventData->mRightSelectionPosition = handlePosition;
725           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
726                                       mEventData->mRightSelectionPosition );
727         }
728       }
729     }
730
731     mEventData->mDecoratorUpdated = true;
732   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
733   else if( HANDLE_SCROLLING == state )
734   {
735     const float xSpeed = event.p2.mFloat;
736     const Vector2& actualSize = mVisualModel->GetActualSize();
737     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
738
739     mEventData->mScrollPosition.x += xSpeed;
740
741     ClampHorizontalScroll( actualSize );
742
743     bool endOfScroll = false;
744     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
745     {
746       // Notify the decorator there is no more text to scroll.
747       // The decorator won't send more scroll events.
748       mEventData->mDecorator->NotifyEndOfScroll();
749       // Still need to set the position of the handle.
750       endOfScroll = true;
751     }
752
753     // Set the position of the handle.
754     const bool scrollRightDirection = xSpeed > 0.f;
755     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
756     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
757
758     if( Event::GRAB_HANDLE_EVENT == event.type )
759     {
760       ChangeState( EventData::GRAB_HANDLE_PANNING );
761
762       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
763
764       // Position the grag handle close to either the left or right edge.
765       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
766
767       // Get the new handle position.
768       // The grab handle's position is in decorator coords. Need to transforms to text coords.
769       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
770                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
771
772       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
773       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
774       mEventData->mPrimaryCursorPosition = handlePosition;
775     }
776     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
777     {
778       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
779       //       Think if something can be done to save power.
780
781       ChangeState( EventData::SELECTION_HANDLE_PANNING );
782
783       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
784
785       // Position the selection handle close to either the left or right edge.
786       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
787
788       // Get the new handle position.
789       // The selection handle's position is in decorator coords. Need to transforms to text coords.
790       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
791                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
792
793       if( leftSelectionHandleEvent )
794       {
795         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
796         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
797         if( differentHandles )
798         {
799           mEventData->mLeftSelectionPosition = handlePosition;
800         }
801       }
802       else
803       {
804         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
805         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
806         if( differentHandles )
807         {
808           mEventData->mRightSelectionPosition = handlePosition;
809         }
810       }
811
812       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
813       {
814         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
815                                     mEventData->mRightSelectionPosition );
816
817         mEventData->mScrollAfterUpdatePosition = true;
818       }
819     }
820     mEventData->mDecoratorUpdated = true;
821   } // end ( HANDLE_SCROLLING == state )
822 }
823
824 void Controller::Impl::OnSelectEvent( const Event& event )
825 {
826   if( NULL == mEventData )
827   {
828     // Nothing to do if there is no text.
829     return;
830   }
831
832   if( mEventData->mSelectionEnabled )
833   {
834     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
835     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
836     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
837
838     const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
839     const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
840
841     RepositionSelectionHandles( xPosition,
842                                 yPosition );
843
844     mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
845     mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
846
847     mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
848                                                ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
849   }
850 }
851
852 void Controller::Impl::OnSelectAllEvent()
853 {
854   if( NULL == mEventData )
855   {
856     // Nothing to do if there is no text.
857     return;
858   }
859
860   if( mEventData->mSelectionEnabled )
861   {
862     RepositionSelectionHandles( 0u,
863                                 mLogicalModel->mText.Count() );
864
865     mEventData->mScrollAfterUpdatePosition = true;
866     mEventData->mUpdateLeftSelectionPosition = true;
867     mEventData->mUpdateRightSelectionPosition = true;
868   }
869 }
870
871 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
872 {
873   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
874   {
875     // Nothing to select if handles are in the same place.
876     selectedText="";
877     return;
878   }
879
880   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
881
882   //Get start and end position of selection
883   uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
884   uint32_t lengthOfSelectedText =  ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
885
886   // Validate the start and end selection points
887   if(  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() )
888   {
889     //Get text as a UTF8 string
890     Vector<Character>& utf32Characters = mLogicalModel->mText;
891
892     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
893
894     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
895     {
896       // Delete text between handles
897       Vector<Character>& currentText = mLogicalModel->mText;
898
899       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
900       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
901       currentText.Erase( first, last );
902     }
903     mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
904     mEventData->mScrollAfterDelete = true;
905     mEventData->mDecoratorUpdated = true;
906   }
907 }
908
909 void Controller::Impl::ShowClipboard()
910 {
911   if ( mClipboard )
912   {
913     mClipboard.ShowClipboard();
914   }
915 }
916
917 void Controller::Impl::HideClipboard()
918 {
919   if ( mClipboard )
920   {
921     mClipboard.HideClipboard();
922   }
923 }
924
925 bool Controller::Impl::CopyStringToClipboard( std::string& source )
926 {
927   //Send string to clipboard
928   return ( mClipboard && mClipboard.SetItem( source ) );
929 }
930
931 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
932 {
933   std::string selectedText;
934   RetrieveSelection( selectedText, deleteAfterSending );
935   CopyStringToClipboard( selectedText );
936   ChangeState( EventData::EDITING );
937 }
938
939 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
940 {
941   if ( mClipboard )
942   {
943     retreivedString =  mClipboard.GetItem( itemIndex );
944   }
945 }
946
947 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
948 {
949   if( selectionStart == selectionEnd )
950   {
951     // Nothing to select if handles are in the same place.
952     return;
953   }
954
955   mEventData->mDecorator->ClearHighlights();
956
957   mEventData->mLeftSelectionPosition = selectionStart;
958   mEventData->mRightSelectionPosition = selectionEnd;
959
960   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
961   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
962   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
963   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
964   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
965   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
966   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
967
968   // TODO: Better algorithm to create the highlight box.
969   // TODO: Multi-line.
970
971   // Get the height of the line.
972   const Vector<LineRun>& lines = mVisualModel->mLines;
973   const LineRun& firstLine = *lines.Begin();
974   const float height = firstLine.ascender + -firstLine.descender;
975
976   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
977   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
978   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
979
980   // Swap the indices if the start is greater than the end.
981   const bool indicesSwapped = selectionStart > selectionEnd;
982
983   // Tell the decorator to flip the selection handles if needed.
984   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
985
986   if( indicesSwapped )
987   {
988     std::swap( selectionStart, selectionEnd );
989   }
990
991   // Get the indices to the first and last selected glyphs.
992   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
993   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
994   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
995   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
996
997   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
998   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
999   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1000
1001   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1002   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1003   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1004
1005   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1006
1007   // Traverse the glyphs.
1008   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1009   {
1010     const GlyphInfo& glyph = *( glyphsBuffer + index );
1011     const Vector2& position = *( positionsBuffer + index );
1012
1013     if( splitStartGlyph )
1014     {
1015       // 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.
1016
1017       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1018       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1019       // Get the direction of the character.
1020       CharacterDirection isCurrentRightToLeft = false;
1021       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1022       {
1023         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1024       }
1025
1026       // The end point could be in the middle of the ligature.
1027       // Calculate the number of characters selected.
1028       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1029
1030       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1031
1032       mEventData->mDecorator->AddHighlight( xPosition,
1033                                             offset.y,
1034                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1035                                             offset.y + height );
1036
1037       splitStartGlyph = false;
1038       continue;
1039     }
1040
1041     if( splitEndGlyph && ( index == glyphEnd ) )
1042     {
1043       // 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.
1044
1045       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1046       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1047       // Get the direction of the character.
1048       CharacterDirection isCurrentRightToLeft = false;
1049       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1050       {
1051         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1052       }
1053
1054       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1055
1056       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1057       mEventData->mDecorator->AddHighlight( xPosition,
1058                                             offset.y,
1059                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1060                                             offset.y + height );
1061
1062       splitEndGlyph = false;
1063       continue;
1064     }
1065
1066     const float xPosition = position.x - glyph.xBearing + offset.x;
1067     mEventData->mDecorator->AddHighlight( xPosition,
1068                                           offset.y,
1069                                           xPosition + glyph.advance,
1070                                           offset.y + height );
1071   }
1072
1073   CursorInfo primaryCursorInfo;
1074   GetCursorPosition( mEventData->mLeftSelectionPosition,
1075                      primaryCursorInfo );
1076
1077   CursorInfo secondaryCursorInfo;
1078   GetCursorPosition( mEventData->mRightSelectionPosition,
1079                      secondaryCursorInfo );
1080
1081   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1082   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1083
1084   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
1085
1086   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
1087
1088   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1089   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1090
1091   // Set the flag to update the decorator.
1092   mEventData->mDecoratorUpdated = true;
1093 }
1094
1095 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1096 {
1097   if( NULL == mEventData )
1098   {
1099     // Nothing to do if there is no text input.
1100     return;
1101   }
1102
1103   if( IsShowingPlaceholderText() )
1104   {
1105     // Nothing to do if there is the place-holder text.
1106     return;
1107   }
1108
1109   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1110   const Length numberOfLines  = mVisualModel->mLines.Count();
1111   if( 0 == numberOfGlyphs ||
1112       0 == numberOfLines )
1113   {
1114     // Nothing to do if there is no text.
1115     return;
1116   }
1117
1118   // Find which word was selected
1119   CharacterIndex selectionStart( 0 );
1120   CharacterIndex selectionEnd( 0 );
1121   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1122   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1123
1124   if( selectionStart == selectionEnd )
1125   {
1126     ChangeState( EventData::EDITING );
1127     // Nothing to select. i.e. a white space, out of bounds
1128     return;
1129   }
1130
1131   RepositionSelectionHandles( selectionStart, selectionEnd );
1132 }
1133
1134 void Controller::Impl::SetPopupButtons()
1135 {
1136   /**
1137    *  Sets the Popup buttons to be shown depending on State.
1138    *
1139    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1140    *
1141    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1142    */
1143
1144   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1145
1146   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1147   {
1148     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1149
1150     if ( !IsClipboardEmpty() )
1151     {
1152       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1153       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1154     }
1155
1156     if ( !mEventData->mAllTextSelected )
1157     {
1158       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1159     }
1160   }
1161   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1162   {
1163     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1164     {
1165       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1166     }
1167
1168     if ( !IsClipboardEmpty() )
1169     {
1170       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1171       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1172     }
1173   }
1174
1175   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1176 }
1177
1178 void Controller::Impl::ChangeState( EventData::State newState )
1179 {
1180   if( NULL == mEventData )
1181   {
1182     // Nothing to do if there is no text input.
1183     return;
1184   }
1185
1186   if( mEventData->mState != newState )
1187   {
1188     mEventData->mState = newState;
1189
1190     if( EventData::INACTIVE == mEventData->mState )
1191     {
1192       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1193       mEventData->mDecorator->StopCursorBlink();
1194       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1195       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1196       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1197       mEventData->mDecorator->SetPopupActive( false );
1198       mEventData->mDecoratorUpdated = true;
1199       HideClipboard();
1200     }
1201     else if ( EventData::INTERRUPTED  == mEventData->mState)
1202     {
1203       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1204       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1205       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1206       mEventData->mDecorator->SetPopupActive( false );
1207       mEventData->mDecoratorUpdated = true;
1208       HideClipboard();
1209     }
1210     else if ( EventData::SELECTING == mEventData->mState )
1211     {
1212       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1213       mEventData->mDecorator->StopCursorBlink();
1214       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1215       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1216       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1217       if( mEventData->mGrabHandlePopupEnabled )
1218       {
1219         SetPopupButtons();
1220         mEventData->mDecorator->SetPopupActive( true );
1221       }
1222       mEventData->mDecoratorUpdated = true;
1223     }
1224     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1225     {
1226       if( mEventData->mGrabHandlePopupEnabled )
1227       {
1228         SetPopupButtons();
1229         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1230         mEventData->mDecorator->SetPopupActive( true );
1231       }
1232       mEventData->mDecoratorUpdated = true;
1233     }
1234     else if( EventData::EDITING == mEventData->mState )
1235     {
1236       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1237       if( mEventData->mCursorBlinkEnabled )
1238       {
1239         mEventData->mDecorator->StartCursorBlink();
1240       }
1241       // Grab handle is not shown until a tap is received whilst EDITING
1242       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1243       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1244       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1245       if( mEventData->mGrabHandlePopupEnabled )
1246       {
1247         mEventData->mDecorator->SetPopupActive( false );
1248       }
1249       mEventData->mDecoratorUpdated = true;
1250       HideClipboard();
1251     }
1252     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1253     {
1254       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1255       if( mEventData->mCursorBlinkEnabled )
1256       {
1257         mEventData->mDecorator->StartCursorBlink();
1258       }
1259       if( mEventData->mSelectionEnabled )
1260       {
1261         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1262         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1263       }
1264       else
1265       {
1266         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1267       }
1268       if( mEventData->mGrabHandlePopupEnabled )
1269       {
1270         SetPopupButtons();
1271         mEventData->mDecorator->SetPopupActive( true );
1272       }
1273       HideClipboard();
1274       mEventData->mDecoratorUpdated = true;
1275     }
1276     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1277     {
1278       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1279       if( mEventData->mCursorBlinkEnabled )
1280       {
1281         mEventData->mDecorator->StartCursorBlink();
1282       }
1283       // Grab handle is not shown until a tap is received whilst EDITING
1284       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1285       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1286       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1287       if( mEventData->mGrabHandlePopupEnabled )
1288       {
1289         mEventData->mDecorator->SetPopupActive( false );
1290       }
1291       mEventData->mDecoratorUpdated = true;
1292       HideClipboard();
1293     }
1294     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1295     {
1296       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1297       mEventData->mDecorator->StopCursorBlink();
1298       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1299       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1300       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1301       if( mEventData->mGrabHandlePopupEnabled )
1302       {
1303         mEventData->mDecorator->SetPopupActive( false );
1304       }
1305       mEventData->mDecoratorUpdated = true;
1306     }
1307     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1308     {
1309       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1310       if( mEventData->mCursorBlinkEnabled )
1311       {
1312         mEventData->mDecorator->StartCursorBlink();
1313       }
1314       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1315       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1316       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1317       if( mEventData->mGrabHandlePopupEnabled )
1318       {
1319         mEventData->mDecorator->SetPopupActive( false );
1320       }
1321       mEventData->mDecoratorUpdated = true;
1322     }
1323   }
1324 }
1325
1326 LineIndex Controller::Impl::GetClosestLine( float y ) const
1327 {
1328   float totalHeight = 0.f;
1329   LineIndex lineIndex = 0u;
1330
1331   const Vector<LineRun>& lines = mVisualModel->mLines;
1332   for( LineIndex endLine = lines.Count();
1333        lineIndex < endLine;
1334        ++lineIndex )
1335   {
1336     const LineRun& lineRun = lines[lineIndex];
1337     totalHeight += lineRun.ascender + -lineRun.descender;
1338     if( y < totalHeight )
1339     {
1340       return lineIndex;
1341     }
1342   }
1343
1344   if( lineIndex == 0 )
1345   {
1346     return 0;
1347   }
1348
1349   return lineIndex-1;
1350 }
1351
1352 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1353 {
1354   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1355   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1356
1357   if ( mLogicalModel->mText.Count() == 0 )
1358   {
1359     return;  // if model empty
1360   }
1361
1362   if( hitCharacter >= mLogicalModel->mText.Count() )
1363   {
1364     // Closest hit character is the last character.
1365     if ( hitCharacter ==  mLogicalModel->mText.Count() )
1366     {
1367       hitCharacter--; //Hit character index set to last character in logical model
1368     }
1369     else
1370     {
1371       // hitCharacter is out of bounds
1372       return;
1373     }
1374   }
1375
1376   startIndex = hitCharacter;
1377   endIndex = hitCharacter;
1378
1379   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1380   {
1381     // Find the start and end of the text
1382     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1383     {
1384       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1385       if( TextAbstraction::IsWhiteSpace( charCode ) )
1386       {
1387         break;
1388       }
1389     }
1390     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1391     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1392     {
1393       Character charCode = mLogicalModel->mText[ endIndex ];
1394       if( TextAbstraction::IsWhiteSpace( charCode ) )
1395       {
1396         break;
1397       }
1398     }
1399   }
1400 }
1401
1402 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1403                                                         float visualY )
1404 {
1405   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1406
1407   if( NULL == mEventData )
1408   {
1409     // Nothing to do if there is no text input.
1410     return 0u;
1411   }
1412
1413   CharacterIndex logicalIndex = 0u;
1414
1415   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1416   const Length numberOfLines  = mVisualModel->mLines.Count();
1417   if( 0 == numberOfGlyphs ||
1418       0 == numberOfLines )
1419   {
1420     return logicalIndex;
1421   }
1422
1423   // Find which line is closest
1424   const LineIndex lineIndex = GetClosestLine( visualY );
1425   const LineRun& line = mVisualModel->mLines[lineIndex];
1426
1427   // Get the positions of the glyphs.
1428   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1429   const Vector2* const positionsBuffer = positions.Begin();
1430
1431   // Get the visual to logical conversion tables.
1432   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1433   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1434
1435   // Get the character to glyph conversion table.
1436   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1437
1438   // Get the glyphs per character table.
1439   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1440   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1441
1442   // If the vector is void, there is no right to left characters.
1443   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1444
1445   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1446   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1447   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1448
1449   // Whether there is a hit on a glyph.
1450   bool matched = false;
1451
1452   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1453   CharacterIndex visualIndex = startCharacter;
1454   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1455   {
1456     // The character in logical order.
1457     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1458
1459     // Get the script of the character.
1460     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1461
1462     // The first glyph for that character in logical order.
1463     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1464     // The number of glyphs for that character
1465     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1466
1467     // Get the metrics for the group of glyphs.
1468     GlyphMetrics glyphMetrics;
1469     GetGlyphsMetrics( glyphLogicalOrderIndex,
1470                       numberOfGlyphs,
1471                       glyphMetrics,
1472                       mVisualModel,
1473                       mMetrics );
1474
1475     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1476
1477     // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»...
1478     const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1479     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1480
1481     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1482     {
1483       // Find the mid-point of the area containing the glyph
1484       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1485
1486       if( visualX < glyphCenter )
1487       {
1488         visualIndex += index;
1489         matched = true;
1490         break;
1491       }
1492     }
1493
1494     if( matched )
1495     {
1496       break;
1497     }
1498   }
1499
1500   // Return the logical position of the cursor in characters.
1501
1502   if( !matched )
1503   {
1504     visualIndex = endCharacter;
1505   }
1506
1507   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1508   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1509
1510   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
1511
1512   return logicalIndex;
1513 }
1514
1515 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1516                                           CursorInfo& cursorInfo )
1517 {
1518   // TODO: Check for multiline with \n, etc...
1519
1520   // Check if the logical position is the first or the last one of the text.
1521   const bool isFirstPosition = 0u == logical;
1522   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1523
1524   if( isFirstPosition && isLastPosition )
1525   {
1526     // There is zero characters. Get the default font's line height.
1527     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1528     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1529
1530     cursorInfo.primaryPosition.x = mEventData->mDecorator->GetCursorWidth();
1531     cursorInfo.primaryPosition.y = 0.f;
1532
1533     // Nothing else to do.
1534     return;
1535   }
1536
1537   // 'logical' is the logical 'cursor' index.
1538   // Get the next and current logical 'character' index.
1539   const CharacterIndex nextCharacterIndex = logical;
1540   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1541
1542   // Get the direction of the character and the next one.
1543   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1544
1545   CharacterDirection isCurrentRightToLeft = false;
1546   CharacterDirection isNextRightToLeft = false;
1547   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1548   {
1549     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1550     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1551   }
1552
1553   // Get the line where the character is laid-out.
1554   const LineRun* const modelLines = mVisualModel->mLines.Begin();
1555
1556   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1557   const LineRun& line = *( modelLines + lineIndex );
1558
1559   // Get the paragraph's direction.
1560   const CharacterDirection isRightToLeftParagraph = line.direction;
1561
1562   // Check whether there is an alternative position:
1563
1564   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1565                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1566
1567   // Set the line height.
1568   cursorInfo.lineHeight = line.ascender + -line.descender;
1569
1570   // Calculate the primary cursor.
1571
1572   CharacterIndex index = characterIndex;
1573   if( cursorInfo.isSecondaryCursor )
1574   {
1575     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1576
1577     if( isLastPosition )
1578     {
1579       // The position of the cursor after the last character needs special
1580       // care depending on its direction and the direction of the paragraph.
1581
1582       // Need to find the first character after the last character with the paragraph's direction.
1583       // i.e l0 l1 l2 r0 r1 should find r0.
1584
1585       // TODO: check for more than one line!
1586       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1587       index = mLogicalModel->GetLogicalCharacterIndex( index );
1588     }
1589     else
1590     {
1591       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1592     }
1593   }
1594
1595   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1596   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1597   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1598   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1599   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
1600
1601   // Convert the cursor position into the glyph position.
1602   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
1603   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1604   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
1605
1606   // Get the metrics for the group of glyphs.
1607   GlyphMetrics glyphMetrics;
1608   GetGlyphsMetrics( primaryGlyphIndex,
1609                     primaryNumberOfGlyphs,
1610                     glyphMetrics,
1611                     mVisualModel,
1612                     mMetrics );
1613
1614   // Whether to add the glyph's advance to the cursor position.
1615   // i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
1616   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
1617   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
1618   //
1619   // FLCP A
1620   // ------
1621   // 0000 1
1622   // 0001 1
1623   // 0010 0
1624   // 0011 0
1625   // 0100 1
1626   // 0101 0
1627   // 0110 1
1628   // 0111 0
1629   // 1000 0
1630   // 1001 x
1631   // 1010 x
1632   // 1011 1
1633   // 1100 x
1634   // 1101 x
1635   // 1110 x
1636   // 1111 x
1637   //
1638   // Where F -> isFirstPosition
1639   //       L -> isLastPosition
1640   //       C -> isCurrentRightToLeft
1641   //       P -> isRightToLeftParagraph
1642   //       A -> Whether to add the glyph's advance.
1643
1644   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
1645                                  ( isFirstPosition && isRightToLeftParagraph ) ||
1646                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
1647
1648   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
1649
1650   if( !isLastPosition &&
1651       ( primaryNumberOfCharacters > 1u ) )
1652   {
1653     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
1654
1655     bool isCurrentRightToLeft = false;
1656     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1657     {
1658       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
1659     }
1660
1661     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
1662     if( isCurrentRightToLeft )
1663     {
1664       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
1665     }
1666
1667     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1668   }
1669
1670   // Get the glyph position and x bearing.
1671   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
1672
1673   // Set the primary cursor's height.
1674   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1675
1676   // Set the primary cursor's position.
1677   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
1678   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1679
1680   // Calculate the secondary cursor.
1681
1682   if( cursorInfo.isSecondaryCursor )
1683   {
1684     // Set the secondary cursor's height.
1685     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1686
1687     CharacterIndex index = characterIndex;
1688     if( !isLastPosition )
1689     {
1690       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1691     }
1692
1693     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
1694     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1695
1696     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
1697
1698     GetGlyphsMetrics( secondaryGlyphIndex,
1699                       secondaryNumberOfGlyphs,
1700                       glyphMetrics,
1701                       mVisualModel,
1702                       mMetrics );
1703
1704     // Set the secondary cursor's position.
1705     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1706     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1707   }
1708 }
1709
1710 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1711 {
1712   if( NULL == mEventData )
1713   {
1714     // Nothing to do if there is no text input.
1715     return 0u;
1716   }
1717
1718   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1719
1720   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1721   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1722
1723   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1724   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1725
1726   if( numberOfCharacters > 1u )
1727   {
1728     const Script script = mLogicalModel->GetScript( index );
1729     if( HasLigatureMustBreak( script ) )
1730     {
1731       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»,  ...
1732       numberOfCharacters = 1u;
1733     }
1734   }
1735   else
1736   {
1737     while( 0u == numberOfCharacters )
1738     {
1739       ++glyphIndex;
1740       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1741     }
1742   }
1743
1744   if( index < mEventData->mPrimaryCursorPosition )
1745   {
1746     cursorIndex -= numberOfCharacters;
1747   }
1748   else
1749   {
1750     cursorIndex += numberOfCharacters;
1751   }
1752
1753   return cursorIndex;
1754 }
1755
1756 void Controller::Impl::UpdateCursorPosition()
1757 {
1758   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1759   if( NULL == mEventData )
1760   {
1761     // Nothing to do if there is no text input.
1762     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1763     return;
1764   }
1765
1766   if( IsShowingPlaceholderText() || ( 0u == mLogicalModel->mText.Count() ) )
1767   {
1768     // Do not want to use the place-holder text to set the cursor position.
1769
1770     // Use the line's height of the font's family set to set the cursor's size.
1771     // If there is no font's family set, use the default font.
1772     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1773
1774     float lineHeight = 0.f;
1775
1776     FontId defaultFontId = 0u;
1777     if( NULL == mFontDefaults )
1778     {
1779       TextAbstraction::FontDescription fontDescription;
1780       defaultFontId = mFontClient.GetFontId( fontDescription );
1781     }
1782     else
1783     {
1784       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1785     }
1786
1787     Text::FontMetrics fontMetrics;
1788     mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1789
1790     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1791
1792
1793     Vector2 cursorPosition;
1794
1795     switch( mLayoutEngine.GetHorizontalAlignment() )
1796     {
1797       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1798       {
1799         cursorPosition.x = mEventData->mDecorator->GetCursorWidth();
1800         break;
1801       }
1802       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1803       {
1804         cursorPosition.x = floor( 0.5f * mVisualModel->mControlSize.width );
1805         break;
1806       }
1807       case LayoutEngine::HORIZONTAL_ALIGN_END:
1808       {
1809         cursorPosition.x = mVisualModel->mControlSize.width;
1810         break;
1811       }
1812     }
1813
1814     switch( mLayoutEngine.GetVerticalAlignment() )
1815     {
1816       case LayoutEngine::VERTICAL_ALIGN_TOP:
1817       {
1818         cursorPosition.y = 0.f;
1819         break;
1820       }
1821       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1822       {
1823         cursorPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - lineHeight ) );
1824         break;
1825       }
1826       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1827       {
1828         cursorPosition.y = mVisualModel->mControlSize.height - lineHeight;
1829         break;
1830       }
1831     }
1832
1833     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1834                                          cursorPosition.x,
1835                                          cursorPosition.y,
1836                                          lineHeight,
1837                                          lineHeight );
1838   }
1839   else
1840   {
1841     CursorInfo cursorInfo;
1842     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1843                        cursorInfo );
1844
1845     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1846     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1847
1848     // Sets the cursor position.
1849     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1850                                          cursorPosition.x,
1851                                          cursorPosition.y,
1852                                          cursorInfo.primaryCursorHeight,
1853                                          cursorInfo.lineHeight );
1854     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1855
1856     // Sets the grab handle position.
1857     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1858                                          cursorPosition.x,
1859                                          cursorPosition.y,
1860                                          cursorInfo.lineHeight );
1861
1862     if( cursorInfo.isSecondaryCursor )
1863     {
1864       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1865       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1866                                            cursorInfo.secondaryPosition.x + offset.x,
1867                                            cursorInfo.secondaryPosition.y + offset.y,
1868                                            cursorInfo.secondaryCursorHeight,
1869                                            cursorInfo.lineHeight );
1870       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1871     }
1872     else
1873     {
1874       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1875     }
1876   }
1877   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1878 }
1879
1880 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1881 {
1882   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1883       ( RIGHT_SELECTION_HANDLE != handleType ) )
1884   {
1885     return;
1886   }
1887
1888   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1889   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1890
1891   CursorInfo cursorInfo;
1892   GetCursorPosition( index,
1893                      cursorInfo );
1894
1895   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1896   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1897
1898   // Sets the grab handle position.
1899   mEventData->mDecorator->SetPosition( handleType,
1900                                        cursorPosition.x,
1901                                        cursorPosition.y,
1902                                        cursorInfo.lineHeight );
1903
1904   // If selection handle at start of the text and other at end of the text then all text is selected.
1905   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1906   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1907   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1908 }
1909
1910 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1911 {
1912   // Clamp between -space & 0 (and the text alignment).
1913   if( actualSize.width > mVisualModel->mControlSize.width )
1914   {
1915     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
1916     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1917     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1918
1919     mEventData->mDecoratorUpdated = true;
1920   }
1921   else
1922   {
1923     mEventData->mScrollPosition.x = 0.f;
1924   }
1925 }
1926
1927 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1928 {
1929   // Clamp between -space & 0 (and the text alignment).
1930   if( actualSize.height > mVisualModel->mControlSize.height )
1931   {
1932     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
1933     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1934     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1935
1936     mEventData->mDecoratorUpdated = true;
1937   }
1938   else
1939   {
1940     mEventData->mScrollPosition.y = 0.f;
1941   }
1942 }
1943
1944 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1945 {
1946   Vector2 offset;
1947   bool updateDecorator = false;
1948   if( position.x < 0.f )
1949   {
1950     offset.x = -position.x;
1951     mEventData->mScrollPosition.x += offset.x;
1952     updateDecorator = true;
1953   }
1954   else if( position.x > mVisualModel->mControlSize.width )
1955   {
1956     offset.x = mVisualModel->mControlSize.width - position.x;
1957     mEventData->mScrollPosition.x += offset.x;
1958     updateDecorator = true;
1959   }
1960
1961   if( updateDecorator && mEventData->mDecorator )
1962   {
1963     mEventData->mDecorator->UpdatePositions( offset );
1964   }
1965
1966   // TODO : calculate the vertical scroll.
1967 }
1968
1969 void Controller::Impl::ScrollTextToMatchCursor()
1970 {
1971   // Get the current cursor position in decorator coords.
1972   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1973
1974   // Calculate the new cursor position.
1975   CursorInfo cursorInfo;
1976   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1977                      cursorInfo );
1978
1979   // Calculate the offset to match the cursor position before the character was deleted.
1980   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1981
1982   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1983
1984   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1985   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1986
1987   // Sets the cursor position.
1988   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1989                                        cursorPosition.x,
1990                                        cursorPosition.y,
1991                                        cursorInfo.primaryCursorHeight,
1992                                        cursorInfo.lineHeight );
1993
1994   // Sets the grab handle position.
1995   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1996                                        cursorPosition.x,
1997                                        cursorPosition.y,
1998                                        cursorInfo.lineHeight );
1999
2000   if( cursorInfo.isSecondaryCursor )
2001   {
2002     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2003                                          cursorInfo.secondaryPosition.x + offset.x,
2004                                          cursorInfo.secondaryPosition.y + offset.y,
2005                                          cursorInfo.secondaryCursorHeight,
2006                                          cursorInfo.lineHeight );
2007     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2008   }
2009
2010   // Set which cursors are active according the state.
2011   if( ( EventData::EDITING == mEventData->mState )                  ||
2012       ( EventData::EDITING_WITH_POPUP == mEventData->mState )       ||
2013       ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
2014       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2015   {
2016     if( cursorInfo.isSecondaryCursor )
2017     {
2018       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2019     }
2020     else
2021     {
2022       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2023     }
2024   }
2025   else
2026   {
2027     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2028   }
2029 }
2030
2031 void Controller::Impl::RequestRelayout()
2032 {
2033   mControlInterface.RequestTextRelayout();
2034 }
2035
2036 } // namespace Text
2037
2038 } // namespace Toolkit
2039
2040 } // namespace Dali