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