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