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