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