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