795a92957cd720b4bfa0b298235b860223f20e92
[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       // Scroll after delete.
904       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
905       mEventData->mScrollAfterDelete = true;
906       mEventData->mDecoratorUpdated = true;
907     }
908   }
909 }
910
911 void Controller::Impl::ShowClipboard()
912 {
913   if ( mClipboard )
914   {
915     mClipboard.ShowClipboard();
916   }
917 }
918
919 void Controller::Impl::HideClipboard()
920 {
921   if ( mClipboard )
922   {
923     mClipboard.HideClipboard();
924   }
925 }
926
927 bool Controller::Impl::CopyStringToClipboard( std::string& source )
928 {
929   //Send string to clipboard
930   return ( mClipboard && mClipboard.SetItem( source ) );
931 }
932
933 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
934 {
935   std::string selectedText;
936   RetrieveSelection( selectedText, deleteAfterSending );
937   CopyStringToClipboard( selectedText );
938   ChangeState( EventData::EDITING );
939 }
940
941 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
942 {
943   if ( mClipboard )
944   {
945     retreivedString =  mClipboard.GetItem( itemIndex );
946   }
947 }
948
949 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
950 {
951   if( selectionStart == selectionEnd )
952   {
953     // Nothing to select if handles are in the same place.
954     return;
955   }
956
957   mEventData->mDecorator->ClearHighlights();
958
959   mEventData->mLeftSelectionPosition = selectionStart;
960   mEventData->mRightSelectionPosition = selectionEnd;
961
962   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
963   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
964   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
965   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
966   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
967   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
968   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
969
970   // TODO: Better algorithm to create the highlight box.
971   // TODO: Multi-line.
972
973   // Get the height of the line.
974   const Vector<LineRun>& lines = mVisualModel->mLines;
975   const LineRun& firstLine = *lines.Begin();
976   const float height = firstLine.ascender + -firstLine.descender;
977
978   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
979   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
980   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
981
982   // Swap the indices if the start is greater than the end.
983   const bool indicesSwapped = selectionStart > selectionEnd;
984
985   // Tell the decorator to flip the selection handles if needed.
986   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
987
988   if( indicesSwapped )
989   {
990     std::swap( selectionStart, selectionEnd );
991   }
992
993   // Get the indices to the first and last selected glyphs.
994   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
995   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
996   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
997   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
998
999   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1000   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1001   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1002
1003   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1004   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1005   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1006
1007   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1008
1009   // Traverse the glyphs.
1010   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1011   {
1012     const GlyphInfo& glyph = *( glyphsBuffer + index );
1013     const Vector2& position = *( positionsBuffer + index );
1014
1015     if( splitStartGlyph )
1016     {
1017       // 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.
1018
1019       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1020       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1021       // Get the direction of the character.
1022       CharacterDirection isCurrentRightToLeft = false;
1023       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1024       {
1025         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1026       }
1027
1028       // The end point could be in the middle of the ligature.
1029       // Calculate the number of characters selected.
1030       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1031
1032       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1033
1034       mEventData->mDecorator->AddHighlight( xPosition,
1035                                             offset.y,
1036                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1037                                             offset.y + height );
1038
1039       splitStartGlyph = false;
1040       continue;
1041     }
1042
1043     if( splitEndGlyph && ( index == glyphEnd ) )
1044     {
1045       // 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.
1046
1047       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1048       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1049       // Get the direction of the character.
1050       CharacterDirection isCurrentRightToLeft = false;
1051       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1052       {
1053         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1054       }
1055
1056       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1057
1058       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1059       mEventData->mDecorator->AddHighlight( xPosition,
1060                                             offset.y,
1061                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1062                                             offset.y + height );
1063
1064       splitEndGlyph = false;
1065       continue;
1066     }
1067
1068     const float xPosition = position.x - glyph.xBearing + offset.x;
1069     mEventData->mDecorator->AddHighlight( xPosition,
1070                                           offset.y,
1071                                           xPosition + glyph.advance,
1072                                           offset.y + height );
1073   }
1074
1075   CursorInfo primaryCursorInfo;
1076   GetCursorPosition( mEventData->mLeftSelectionPosition,
1077                      primaryCursorInfo );
1078
1079   CursorInfo secondaryCursorInfo;
1080   GetCursorPosition( mEventData->mRightSelectionPosition,
1081                      secondaryCursorInfo );
1082
1083   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1084   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1085
1086   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
1087
1088   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
1089
1090   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1091   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1092
1093   // Set the flag to update the decorator.
1094   mEventData->mDecoratorUpdated = true;
1095 }
1096
1097 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1098 {
1099   if( NULL == mEventData )
1100   {
1101     // Nothing to do if there is no text input.
1102     return;
1103   }
1104
1105   if( IsShowingPlaceholderText() )
1106   {
1107     // Nothing to do if there is the place-holder text.
1108     return;
1109   }
1110
1111   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1112   const Length numberOfLines  = mVisualModel->mLines.Count();
1113   if( 0 == numberOfGlyphs ||
1114       0 == numberOfLines )
1115   {
1116     // Nothing to do if there is no text.
1117     return;
1118   }
1119
1120   // Find which word was selected
1121   CharacterIndex selectionStart( 0 );
1122   CharacterIndex selectionEnd( 0 );
1123   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1124   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1125
1126   if( selectionStart == selectionEnd )
1127   {
1128     ChangeState( EventData::EDITING );
1129     // Nothing to select. i.e. a white space, out of bounds
1130     return;
1131   }
1132
1133   RepositionSelectionHandles( selectionStart, selectionEnd );
1134 }
1135
1136 void Controller::Impl::SetPopupButtons()
1137 {
1138   /**
1139    *  Sets the Popup buttons to be shown depending on State.
1140    *
1141    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1142    *
1143    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1144    */
1145
1146   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1147
1148   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1149   {
1150     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1151
1152     if ( !IsClipboardEmpty() )
1153     {
1154       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1155       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1156     }
1157
1158     if ( !mEventData->mAllTextSelected )
1159     {
1160       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1161     }
1162   }
1163   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1164   {
1165     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1166     {
1167       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1168     }
1169
1170     if ( !IsClipboardEmpty() )
1171     {
1172       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1173       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1174     }
1175   }
1176
1177   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1178 }
1179
1180 void Controller::Impl::ChangeState( EventData::State newState )
1181 {
1182   if( NULL == mEventData )
1183   {
1184     // Nothing to do if there is no text input.
1185     return;
1186   }
1187
1188   if( mEventData->mState != newState )
1189   {
1190     mEventData->mState = newState;
1191
1192     if( EventData::INACTIVE == mEventData->mState )
1193     {
1194       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1195       mEventData->mDecorator->StopCursorBlink();
1196       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1197       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1198       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1199       mEventData->mDecorator->SetPopupActive( false );
1200       mEventData->mDecoratorUpdated = true;
1201       HideClipboard();
1202     }
1203     else if ( EventData::INTERRUPTED  == mEventData->mState)
1204     {
1205       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1206       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1207       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1208       mEventData->mDecorator->SetPopupActive( false );
1209       mEventData->mDecoratorUpdated = true;
1210       HideClipboard();
1211     }
1212     else if ( EventData::SELECTING == mEventData->mState )
1213     {
1214       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1215       mEventData->mDecorator->StopCursorBlink();
1216       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1217       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1218       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1219       if( mEventData->mGrabHandlePopupEnabled )
1220       {
1221         SetPopupButtons();
1222         mEventData->mDecorator->SetPopupActive( true );
1223       }
1224       mEventData->mDecoratorUpdated = true;
1225     }
1226     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1227     {
1228       if( mEventData->mGrabHandlePopupEnabled )
1229       {
1230         SetPopupButtons();
1231         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1232         mEventData->mDecorator->SetPopupActive( true );
1233       }
1234       mEventData->mDecoratorUpdated = true;
1235     }
1236     else if( EventData::EDITING == mEventData->mState )
1237     {
1238       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1239       if( mEventData->mCursorBlinkEnabled )
1240       {
1241         mEventData->mDecorator->StartCursorBlink();
1242       }
1243       // Grab handle is not shown until a tap is received whilst EDITING
1244       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1245       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1246       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1247       if( mEventData->mGrabHandlePopupEnabled )
1248       {
1249         mEventData->mDecorator->SetPopupActive( false );
1250       }
1251       mEventData->mDecoratorUpdated = true;
1252       HideClipboard();
1253     }
1254     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1255     {
1256       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1257       if( mEventData->mCursorBlinkEnabled )
1258       {
1259         mEventData->mDecorator->StartCursorBlink();
1260       }
1261       if( mEventData->mSelectionEnabled )
1262       {
1263         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1264         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1265       }
1266       else
1267       {
1268         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1269       }
1270       if( mEventData->mGrabHandlePopupEnabled )
1271       {
1272         SetPopupButtons();
1273         mEventData->mDecorator->SetPopupActive( true );
1274       }
1275       HideClipboard();
1276       mEventData->mDecoratorUpdated = true;
1277     }
1278     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1279     {
1280       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1281       if( mEventData->mCursorBlinkEnabled )
1282       {
1283         mEventData->mDecorator->StartCursorBlink();
1284       }
1285       // Grab handle is not shown until a tap is received whilst EDITING
1286       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1287       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1288       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1289       if( mEventData->mGrabHandlePopupEnabled )
1290       {
1291         mEventData->mDecorator->SetPopupActive( false );
1292       }
1293       mEventData->mDecoratorUpdated = true;
1294       HideClipboard();
1295     }
1296     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1297     {
1298       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1299       mEventData->mDecorator->StopCursorBlink();
1300       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1301       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1302       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1303       if( mEventData->mGrabHandlePopupEnabled )
1304       {
1305         mEventData->mDecorator->SetPopupActive( false );
1306       }
1307       mEventData->mDecoratorUpdated = true;
1308     }
1309     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1310     {
1311       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1312       if( mEventData->mCursorBlinkEnabled )
1313       {
1314         mEventData->mDecorator->StartCursorBlink();
1315       }
1316       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1317       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1318       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1319       if( mEventData->mGrabHandlePopupEnabled )
1320       {
1321         mEventData->mDecorator->SetPopupActive( false );
1322       }
1323       mEventData->mDecoratorUpdated = true;
1324     }
1325   }
1326 }
1327
1328 LineIndex Controller::Impl::GetClosestLine( float y ) const
1329 {
1330   float totalHeight = 0.f;
1331   LineIndex lineIndex = 0u;
1332
1333   const Vector<LineRun>& lines = mVisualModel->mLines;
1334   for( LineIndex endLine = lines.Count();
1335        lineIndex < endLine;
1336        ++lineIndex )
1337   {
1338     const LineRun& lineRun = lines[lineIndex];
1339     totalHeight += lineRun.ascender + -lineRun.descender;
1340     if( y < totalHeight )
1341     {
1342       return lineIndex;
1343     }
1344   }
1345
1346   if( lineIndex == 0 )
1347   {
1348     return 0;
1349   }
1350
1351   return lineIndex-1;
1352 }
1353
1354 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1355 {
1356   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1357   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1358
1359   if ( mLogicalModel->mText.Count() == 0 )
1360   {
1361     return;  // if model empty
1362   }
1363
1364   if( hitCharacter >= mLogicalModel->mText.Count() )
1365   {
1366     // Closest hit character is the last character.
1367     if ( hitCharacter ==  mLogicalModel->mText.Count() )
1368     {
1369       hitCharacter--; //Hit character index set to last character in logical model
1370     }
1371     else
1372     {
1373       // hitCharacter is out of bounds
1374       return;
1375     }
1376   }
1377
1378   startIndex = hitCharacter;
1379   endIndex = hitCharacter;
1380
1381   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1382   {
1383     // Find the start and end of the text
1384     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1385     {
1386       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1387       if( TextAbstraction::IsWhiteSpace( charCode ) )
1388       {
1389         break;
1390       }
1391     }
1392     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1393     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1394     {
1395       Character charCode = mLogicalModel->mText[ endIndex ];
1396       if( TextAbstraction::IsWhiteSpace( charCode ) )
1397       {
1398         break;
1399       }
1400     }
1401   }
1402 }
1403
1404 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1405                                                         float visualY )
1406 {
1407   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1408
1409   if( NULL == mEventData )
1410   {
1411     // Nothing to do if there is no text input.
1412     return 0u;
1413   }
1414
1415   CharacterIndex logicalIndex = 0u;
1416
1417   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1418   const Length numberOfLines  = mVisualModel->mLines.Count();
1419   if( 0 == numberOfGlyphs ||
1420       0 == numberOfLines )
1421   {
1422     return logicalIndex;
1423   }
1424
1425   // Find which line is closest
1426   const LineIndex lineIndex = GetClosestLine( visualY );
1427   const LineRun& line = mVisualModel->mLines[lineIndex];
1428
1429   // Get the positions of the glyphs.
1430   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1431   const Vector2* const positionsBuffer = positions.Begin();
1432
1433   // Get the visual to logical conversion tables.
1434   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1435   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1436
1437   // Get the character to glyph conversion table.
1438   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1439
1440   // Get the glyphs per character table.
1441   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1442   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1443
1444   // If the vector is void, there is no right to left characters.
1445   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1446
1447   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1448   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1449   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1450
1451   // Whether there is a hit on a glyph.
1452   bool matched = false;
1453
1454   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1455   CharacterIndex visualIndex = startCharacter;
1456   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1457   {
1458     // The character in logical order.
1459     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1460
1461     // Get the script of the character.
1462     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1463
1464     // The first glyph for that character in logical order.
1465     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1466     // The number of glyphs for that character
1467     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1468
1469     // Get the metrics for the group of glyphs.
1470     GlyphMetrics glyphMetrics;
1471     GetGlyphsMetrics( glyphLogicalOrderIndex,
1472                       numberOfGlyphs,
1473                       glyphMetrics,
1474                       mVisualModel,
1475                       mMetrics );
1476
1477     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1478
1479     // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»...
1480     const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1481     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1482
1483     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1484     {
1485       // Find the mid-point of the area containing the glyph
1486       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1487
1488       if( visualX < glyphCenter )
1489       {
1490         visualIndex += index;
1491         matched = true;
1492         break;
1493       }
1494     }
1495
1496     if( matched )
1497     {
1498       break;
1499     }
1500   }
1501
1502   // Return the logical position of the cursor in characters.
1503
1504   if( !matched )
1505   {
1506     visualIndex = endCharacter;
1507   }
1508
1509   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1510   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1511
1512   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
1513
1514   return logicalIndex;
1515 }
1516
1517 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1518                                           CursorInfo& cursorInfo )
1519 {
1520   // TODO: Check for multiline with \n, etc...
1521
1522   // Check if the logical position is the first or the last one of the text.
1523   const bool isFirstPosition = 0u == logical;
1524   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1525
1526   if( isFirstPosition && isLastPosition )
1527   {
1528     // There is zero characters. Get the default font's line height.
1529     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1530     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1531
1532     cursorInfo.primaryPosition.x = 0.f;
1533     cursorInfo.primaryPosition.y = 0.f;
1534
1535     // Nothing else to do.
1536     return;
1537   }
1538
1539   // 'logical' is the logical 'cursor' index.
1540   // Get the next and current logical 'character' index.
1541   const CharacterIndex nextCharacterIndex = logical;
1542   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1543
1544   // Get the direction of the character and the next one.
1545   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1546
1547   CharacterDirection isCurrentRightToLeft = false;
1548   CharacterDirection isNextRightToLeft = false;
1549   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1550   {
1551     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1552     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1553   }
1554
1555   // Get the line where the character is laid-out.
1556   const LineRun* const modelLines = mVisualModel->mLines.Begin();
1557
1558   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1559   const LineRun& line = *( modelLines + lineIndex );
1560
1561   // Get the paragraph's direction.
1562   const CharacterDirection isRightToLeftParagraph = line.direction;
1563
1564   // Check whether there is an alternative position:
1565
1566   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1567                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1568
1569   // Set the line height.
1570   cursorInfo.lineHeight = line.ascender + -line.descender;
1571
1572   // Calculate the primary cursor.
1573
1574   CharacterIndex index = characterIndex;
1575   if( cursorInfo.isSecondaryCursor )
1576   {
1577     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1578
1579     if( isLastPosition )
1580     {
1581       // The position of the cursor after the last character needs special
1582       // care depending on its direction and the direction of the paragraph.
1583
1584       // Need to find the first character after the last character with the paragraph's direction.
1585       // i.e l0 l1 l2 r0 r1 should find r0.
1586
1587       // TODO: check for more than one line!
1588       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1589       index = mLogicalModel->GetLogicalCharacterIndex( index );
1590     }
1591     else
1592     {
1593       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1594     }
1595   }
1596
1597   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1598   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1599   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1600   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1601   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
1602
1603   // Convert the cursor position into the glyph position.
1604   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
1605   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1606   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
1607
1608   // Get the metrics for the group of glyphs.
1609   GlyphMetrics glyphMetrics;
1610   GetGlyphsMetrics( primaryGlyphIndex,
1611                     primaryNumberOfGlyphs,
1612                     glyphMetrics,
1613                     mVisualModel,
1614                     mMetrics );
1615
1616   // Whether to add the glyph's advance to the cursor position.
1617   // 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,
1618   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
1619   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
1620   //
1621   // FLCP A
1622   // ------
1623   // 0000 1
1624   // 0001 1
1625   // 0010 0
1626   // 0011 0
1627   // 0100 1
1628   // 0101 0
1629   // 0110 1
1630   // 0111 0
1631   // 1000 0
1632   // 1001 x
1633   // 1010 x
1634   // 1011 1
1635   // 1100 x
1636   // 1101 x
1637   // 1110 x
1638   // 1111 x
1639   //
1640   // Where F -> isFirstPosition
1641   //       L -> isLastPosition
1642   //       C -> isCurrentRightToLeft
1643   //       P -> isRightToLeftParagraph
1644   //       A -> Whether to add the glyph's advance.
1645
1646   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
1647                                  ( isFirstPosition && isRightToLeftParagraph ) ||
1648                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
1649
1650   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
1651
1652   if( !isLastPosition &&
1653       ( primaryNumberOfCharacters > 1u ) )
1654   {
1655     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
1656
1657     bool isCurrentRightToLeft = false;
1658     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1659     {
1660       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
1661     }
1662
1663     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
1664     if( isCurrentRightToLeft )
1665     {
1666       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
1667     }
1668
1669     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1670   }
1671
1672   // Get the glyph position and x bearing.
1673   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
1674
1675   // Set the primary cursor's height.
1676   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1677
1678   // Set the primary cursor's position.
1679   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
1680   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1681
1682   // Calculate the secondary cursor.
1683
1684   if( cursorInfo.isSecondaryCursor )
1685   {
1686     // Set the secondary cursor's height.
1687     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1688
1689     CharacterIndex index = characterIndex;
1690     if( !isLastPosition )
1691     {
1692       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1693     }
1694
1695     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
1696     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1697
1698     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
1699
1700     GetGlyphsMetrics( secondaryGlyphIndex,
1701                       secondaryNumberOfGlyphs,
1702                       glyphMetrics,
1703                       mVisualModel,
1704                       mMetrics );
1705
1706     // Set the secondary cursor's position.
1707     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1708     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1709   }
1710 }
1711
1712 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1713 {
1714   if( NULL == mEventData )
1715   {
1716     // Nothing to do if there is no text input.
1717     return 0u;
1718   }
1719
1720   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1721
1722   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1723   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1724
1725   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1726   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1727
1728   if( numberOfCharacters > 1u )
1729   {
1730     const Script script = mLogicalModel->GetScript( index );
1731     if( HasLigatureMustBreak( script ) )
1732     {
1733       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»,  ...
1734       numberOfCharacters = 1u;
1735     }
1736   }
1737   else
1738   {
1739     while( 0u == numberOfCharacters )
1740     {
1741       ++glyphIndex;
1742       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1743     }
1744   }
1745
1746   if( index < mEventData->mPrimaryCursorPosition )
1747   {
1748     cursorIndex -= numberOfCharacters;
1749   }
1750   else
1751   {
1752     cursorIndex += numberOfCharacters;
1753   }
1754
1755   return cursorIndex;
1756 }
1757
1758 void Controller::Impl::UpdateCursorPosition()
1759 {
1760   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1761   if( NULL == mEventData )
1762   {
1763     // Nothing to do if there is no text input.
1764     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1765     return;
1766   }
1767
1768   if( IsShowingPlaceholderText() || ( 0u == mLogicalModel->mText.Count() ) )
1769   {
1770     // Do not want to use the place-holder text to set the cursor position.
1771
1772     // Use the line's height of the font's family set to set the cursor's size.
1773     // If there is no font's family set, use the default font.
1774     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1775
1776     float lineHeight = 0.f;
1777
1778     FontId defaultFontId = 0u;
1779     if( NULL == mFontDefaults )
1780     {
1781       TextAbstraction::FontDescription fontDescription;
1782       defaultFontId = mFontClient.GetFontId( fontDescription );
1783     }
1784     else
1785     {
1786       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1787     }
1788
1789     Text::FontMetrics fontMetrics;
1790     mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1791
1792     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1793
1794
1795     Vector2 cursorPosition;
1796
1797     switch( mLayoutEngine.GetHorizontalAlignment() )
1798     {
1799       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1800       {
1801         cursorPosition.x = 0.f;
1802         break;
1803       }
1804       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1805       {
1806         cursorPosition.x = floor( 0.5f * mVisualModel->mControlSize.width );
1807         break;
1808       }
1809       case LayoutEngine::HORIZONTAL_ALIGN_END:
1810       {
1811         cursorPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1812         break;
1813       }
1814     }
1815
1816     switch( mLayoutEngine.GetVerticalAlignment() )
1817     {
1818       case LayoutEngine::VERTICAL_ALIGN_TOP:
1819       {
1820         cursorPosition.y = 0.f;
1821         break;
1822       }
1823       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1824       {
1825         cursorPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - lineHeight ) );
1826         break;
1827       }
1828       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1829       {
1830         cursorPosition.y = mVisualModel->mControlSize.height - lineHeight;
1831         break;
1832       }
1833     }
1834
1835     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1836                                          cursorPosition.x,
1837                                          cursorPosition.y,
1838                                          lineHeight,
1839                                          lineHeight );
1840   }
1841   else
1842   {
1843     CursorInfo cursorInfo;
1844     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1845                        cursorInfo );
1846
1847     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1848     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1849
1850     // Sets the cursor position.
1851     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1852                                          cursorPosition.x,
1853                                          cursorPosition.y,
1854                                          cursorInfo.primaryCursorHeight,
1855                                          cursorInfo.lineHeight );
1856     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1857
1858     // Sets the grab handle position.
1859     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1860                                          cursorPosition.x,
1861                                          cursorPosition.y,
1862                                          cursorInfo.lineHeight );
1863
1864     if( cursorInfo.isSecondaryCursor )
1865     {
1866       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1867       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1868                                            cursorInfo.secondaryPosition.x + offset.x,
1869                                            cursorInfo.secondaryPosition.y + offset.y,
1870                                            cursorInfo.secondaryCursorHeight,
1871                                            cursorInfo.lineHeight );
1872       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1873     }
1874     else
1875     {
1876       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1877     }
1878   }
1879   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1880 }
1881
1882 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1883 {
1884   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1885       ( RIGHT_SELECTION_HANDLE != handleType ) )
1886   {
1887     return;
1888   }
1889
1890   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1891   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1892
1893   CursorInfo cursorInfo;
1894   GetCursorPosition( index,
1895                      cursorInfo );
1896
1897   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1898   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1899
1900   // Sets the grab handle position.
1901   mEventData->mDecorator->SetPosition( handleType,
1902                                        cursorPosition.x,
1903                                        cursorPosition.y,
1904                                        cursorInfo.lineHeight );
1905
1906   // If selection handle at start of the text and other at end of the text then all text is selected.
1907   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1908   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1909   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1910 }
1911
1912 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1913 {
1914   // Clamp between -space & 0 (and the text alignment).
1915   if( actualSize.width > mVisualModel->mControlSize.width )
1916   {
1917     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
1918     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1919     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1920
1921     mEventData->mDecoratorUpdated = true;
1922   }
1923   else
1924   {
1925     mEventData->mScrollPosition.x = 0.f;
1926   }
1927 }
1928
1929 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1930 {
1931   // Clamp between -space & 0 (and the text alignment).
1932   if( actualSize.height > mVisualModel->mControlSize.height )
1933   {
1934     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
1935     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1936     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1937
1938     mEventData->mDecoratorUpdated = true;
1939   }
1940   else
1941   {
1942     mEventData->mScrollPosition.y = 0.f;
1943   }
1944 }
1945
1946 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1947 {
1948   Vector2 offset;
1949   bool updateDecorator = false;
1950   if( position.x < 0.f )
1951   {
1952     offset.x = -position.x;
1953     mEventData->mScrollPosition.x += offset.x;
1954     updateDecorator = true;
1955   }
1956   else if( position.x > mVisualModel->mControlSize.width )
1957   {
1958     offset.x = mVisualModel->mControlSize.width - position.x;
1959     mEventData->mScrollPosition.x += offset.x;
1960     updateDecorator = true;
1961   }
1962
1963   if( updateDecorator && mEventData->mDecorator )
1964   {
1965     mEventData->mDecorator->UpdatePositions( offset );
1966   }
1967
1968   // TODO : calculate the vertical scroll.
1969 }
1970
1971 void Controller::Impl::ScrollTextToMatchCursor()
1972 {
1973   // Get the current cursor position in decorator coords.
1974   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1975
1976   // Calculate the new cursor position.
1977   CursorInfo cursorInfo;
1978   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1979                      cursorInfo );
1980
1981   // Calculate the offset to match the cursor position before the character was deleted.
1982   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1983
1984   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1985
1986   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1987   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1988
1989   // Sets the cursor position.
1990   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1991                                        cursorPosition.x,
1992                                        cursorPosition.y,
1993                                        cursorInfo.primaryCursorHeight,
1994                                        cursorInfo.lineHeight );
1995
1996   // Sets the grab handle position.
1997   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1998                                        cursorPosition.x,
1999                                        cursorPosition.y,
2000                                        cursorInfo.lineHeight );
2001
2002   if( cursorInfo.isSecondaryCursor )
2003   {
2004     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2005                                          cursorInfo.secondaryPosition.x + offset.x,
2006                                          cursorInfo.secondaryPosition.y + offset.y,
2007                                          cursorInfo.secondaryCursorHeight,
2008                                          cursorInfo.lineHeight );
2009     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2010   }
2011
2012   // Set which cursors are active according the state.
2013   if( ( EventData::EDITING == mEventData->mState )                  ||
2014       ( EventData::EDITING_WITH_POPUP == mEventData->mState )       ||
2015       ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
2016       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2017   {
2018     if( cursorInfo.isSecondaryCursor )
2019     {
2020       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2021     }
2022     else
2023     {
2024       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2025     }
2026   }
2027   else
2028   {
2029     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2030   }
2031 }
2032
2033 void Controller::Impl::RequestRelayout()
2034 {
2035   mControlInterface.RequestTextRelayout();
2036 }
2037
2038 } // namespace Text
2039
2040 } // namespace Toolkit
2041
2042 } // namespace Dali