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