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