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