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