Merge "New handle flip implementation." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/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   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
878
879   //Get start and end position of selection
880   uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
881   uint32_t lengthOfSelectedText =  ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
882
883   // Validate the start and end selection points
884   if(  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() )
885   {
886     //Get text as a UTF8 string
887     Vector<Character>& utf32Characters = mLogicalModel->mText;
888
889     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
890
891     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
892     {
893       // Delete text between handles
894       Vector<Character>& currentText = mLogicalModel->mText;
895
896       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
897       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
898       currentText.Erase( first, last );
899     }
900     mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
901     mEventData->mScrollAfterDelete = true;
902     mEventData->mDecoratorUpdated = true;
903   }
904 }
905
906 void Controller::Impl::ShowClipboard()
907 {
908   if ( mClipboard )
909   {
910     mClipboard.ShowClipboard();
911   }
912 }
913
914 void Controller::Impl::HideClipboard()
915 {
916   if ( mClipboard )
917   {
918     mClipboard.HideClipboard();
919   }
920 }
921
922 bool Controller::Impl::CopyStringToClipboard( std::string& source )
923 {
924   //Send string to clipboard
925   return ( mClipboard && mClipboard.SetItem( source ) );
926 }
927
928 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
929 {
930   std::string selectedText;
931   RetrieveSelection( selectedText, deleteAfterSending );
932   CopyStringToClipboard( selectedText );
933   ChangeState( EventData::EDITING );
934 }
935
936 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
937 {
938   if ( mClipboard )
939   {
940     retreivedString =  mClipboard.GetItem( itemIndex );
941   }
942 }
943
944 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
945 {
946   if( selectionStart == selectionEnd )
947   {
948     // Nothing to select if handles are in the same place.
949     return;
950   }
951
952   mEventData->mDecorator->ClearHighlights();
953
954   mEventData->mLeftSelectionPosition = selectionStart;
955   mEventData->mRightSelectionPosition = selectionEnd;
956
957   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
958   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
959   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
960   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
961   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
962   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
963   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
964
965   // TODO: Better algorithm to create the highlight box.
966   // TODO: Multi-line.
967
968   // Get the height of the line.
969   const Vector<LineRun>& lines = mVisualModel->mLines;
970   const LineRun& firstLine = *lines.Begin();
971   const float height = firstLine.ascender + -firstLine.descender;
972
973   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
974   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
975   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
976
977   // Swap the indices if the start is greater than the end.
978   const bool indicesSwapped = selectionStart > selectionEnd;
979
980   // Tell the decorator to flip the selection handles if needed.
981   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
982
983   if( indicesSwapped )
984   {
985     std::swap( selectionStart, selectionEnd );
986   }
987
988   // Get the indices to the first and last selected glyphs.
989   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
990   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
991   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
992   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
993
994   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
995   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
996   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
997
998   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
999   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1000   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1001
1002   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1003
1004   // Traverse the glyphs.
1005   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1006   {
1007     const GlyphInfo& glyph = *( glyphsBuffer + index );
1008     const Vector2& position = *( positionsBuffer + index );
1009
1010     if( splitStartGlyph )
1011     {
1012       // 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.
1013
1014       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1015       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1016       // Get the direction of the character.
1017       CharacterDirection isCurrentRightToLeft = false;
1018       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1019       {
1020         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1021       }
1022
1023       // The end point could be in the middle of the ligature.
1024       // Calculate the number of characters selected.
1025       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1026
1027       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1028
1029       mEventData->mDecorator->AddHighlight( xPosition,
1030                                             offset.y,
1031                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1032                                             offset.y + height );
1033
1034       splitStartGlyph = false;
1035       continue;
1036     }
1037
1038     if( splitEndGlyph && ( index == glyphEnd ) )
1039     {
1040       // 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.
1041
1042       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1043       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1044       // Get the direction of the character.
1045       CharacterDirection isCurrentRightToLeft = false;
1046       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1047       {
1048         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1049       }
1050
1051       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1052
1053       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1054       mEventData->mDecorator->AddHighlight( xPosition,
1055                                             offset.y,
1056                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1057                                             offset.y + height );
1058
1059       splitEndGlyph = false;
1060       continue;
1061     }
1062
1063     const float xPosition = position.x - glyph.xBearing + offset.x;
1064     mEventData->mDecorator->AddHighlight( xPosition,
1065                                           offset.y,
1066                                           xPosition + glyph.advance,
1067                                           offset.y + height );
1068   }
1069
1070   CursorInfo primaryCursorInfo;
1071   GetCursorPosition( mEventData->mLeftSelectionPosition,
1072                      primaryCursorInfo );
1073
1074   CursorInfo secondaryCursorInfo;
1075   GetCursorPosition( mEventData->mRightSelectionPosition,
1076                      secondaryCursorInfo );
1077
1078   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1079   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1080
1081   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
1082
1083   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
1084
1085   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1086   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1087
1088   // Set the flag to update the decorator.
1089   mEventData->mDecoratorUpdated = true;
1090 }
1091
1092 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1093 {
1094   if( NULL == mEventData )
1095   {
1096     // Nothing to do if there is no text input.
1097     return;
1098   }
1099
1100   if( IsShowingPlaceholderText() )
1101   {
1102     // Nothing to do if there is the place-holder text.
1103     return;
1104   }
1105
1106   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1107   const Length numberOfLines  = mVisualModel->mLines.Count();
1108   if( 0 == numberOfGlyphs ||
1109       0 == numberOfLines )
1110   {
1111     // Nothing to do if there is no text.
1112     return;
1113   }
1114
1115   // Find which word was selected
1116   CharacterIndex selectionStart( 0 );
1117   CharacterIndex selectionEnd( 0 );
1118   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1119   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1120
1121   if( selectionStart == selectionEnd )
1122   {
1123     ChangeState( EventData::EDITING );
1124     // Nothing to select. i.e. a white space, out of bounds
1125     return;
1126   }
1127
1128   RepositionSelectionHandles( selectionStart, selectionEnd );
1129 }
1130
1131 void Controller::Impl::SetPopupButtons()
1132 {
1133   /**
1134    *  Sets the Popup buttons to be shown depending on State.
1135    *
1136    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1137    *
1138    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1139    */
1140
1141   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1142
1143   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1144   {
1145     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1146
1147     if ( !IsClipboardEmpty() )
1148     {
1149       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1150       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1151     }
1152
1153     if ( !mEventData->mAllTextSelected )
1154     {
1155       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1156     }
1157   }
1158   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1159   {
1160     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1161     {
1162       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1163     }
1164
1165     if ( !IsClipboardEmpty() )
1166     {
1167       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1168       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1169     }
1170   }
1171
1172   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1173 }
1174
1175 void Controller::Impl::ChangeState( EventData::State newState )
1176 {
1177   if( NULL == mEventData )
1178   {
1179     // Nothing to do if there is no text input.
1180     return;
1181   }
1182
1183   if( mEventData->mState != newState )
1184   {
1185     mEventData->mState = newState;
1186
1187     if( EventData::INACTIVE == mEventData->mState )
1188     {
1189       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1190       mEventData->mDecorator->StopCursorBlink();
1191       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1192       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1193       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1194       mEventData->mDecorator->SetPopupActive( false );
1195       mEventData->mDecoratorUpdated = true;
1196       HideClipboard();
1197     }
1198     else if ( EventData::INTERRUPTED  == mEventData->mState)
1199     {
1200       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1201       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1202       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1203       mEventData->mDecorator->SetPopupActive( false );
1204       mEventData->mDecoratorUpdated = true;
1205       HideClipboard();
1206     }
1207     else if ( EventData::SELECTING == mEventData->mState )
1208     {
1209       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1210       mEventData->mDecorator->StopCursorBlink();
1211       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1212       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1213       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1214       if( mEventData->mGrabHandlePopupEnabled )
1215       {
1216         SetPopupButtons();
1217         mEventData->mDecorator->SetPopupActive( true );
1218       }
1219       mEventData->mDecoratorUpdated = true;
1220     }
1221     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1222     {
1223       if( mEventData->mGrabHandlePopupEnabled )
1224       {
1225         SetPopupButtons();
1226         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1227         mEventData->mDecorator->SetPopupActive( true );
1228       }
1229       mEventData->mDecoratorUpdated = true;
1230     }
1231     else if( EventData::EDITING == mEventData->mState )
1232     {
1233       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1234       if( mEventData->mCursorBlinkEnabled )
1235       {
1236         mEventData->mDecorator->StartCursorBlink();
1237       }
1238       // Grab handle is not shown until a tap is received whilst EDITING
1239       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1240       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1241       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1242       if( mEventData->mGrabHandlePopupEnabled )
1243       {
1244         mEventData->mDecorator->SetPopupActive( false );
1245       }
1246       mEventData->mDecoratorUpdated = true;
1247       HideClipboard();
1248     }
1249     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1250     {
1251       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1252       if( mEventData->mCursorBlinkEnabled )
1253       {
1254         mEventData->mDecorator->StartCursorBlink();
1255       }
1256       if( mEventData->mSelectionEnabled )
1257       {
1258         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1259         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1260       }
1261       else
1262       {
1263         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1264       }
1265       if( mEventData->mGrabHandlePopupEnabled )
1266       {
1267         SetPopupButtons();
1268         mEventData->mDecorator->SetPopupActive( true );
1269       }
1270       HideClipboard();
1271       mEventData->mDecoratorUpdated = true;
1272     }
1273     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1274     {
1275       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1276       if( mEventData->mCursorBlinkEnabled )
1277       {
1278         mEventData->mDecorator->StartCursorBlink();
1279       }
1280       // Grab handle is not shown until a tap is received whilst EDITING
1281       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1282       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1283       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1284       if( mEventData->mGrabHandlePopupEnabled )
1285       {
1286         mEventData->mDecorator->SetPopupActive( false );
1287       }
1288       mEventData->mDecoratorUpdated = true;
1289       HideClipboard();
1290     }
1291     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1292     {
1293       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1294       mEventData->mDecorator->StopCursorBlink();
1295       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1296       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1297       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1298       if( mEventData->mGrabHandlePopupEnabled )
1299       {
1300         mEventData->mDecorator->SetPopupActive( false );
1301       }
1302       mEventData->mDecoratorUpdated = true;
1303     }
1304     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1305     {
1306       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1307       if( mEventData->mCursorBlinkEnabled )
1308       {
1309         mEventData->mDecorator->StartCursorBlink();
1310       }
1311       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1312       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1313       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1314       if( mEventData->mGrabHandlePopupEnabled )
1315       {
1316         mEventData->mDecorator->SetPopupActive( false );
1317       }
1318       mEventData->mDecoratorUpdated = true;
1319     }
1320   }
1321 }
1322
1323 LineIndex Controller::Impl::GetClosestLine( float y ) const
1324 {
1325   float totalHeight = 0.f;
1326   LineIndex lineIndex = 0u;
1327
1328   const Vector<LineRun>& lines = mVisualModel->mLines;
1329   for( LineIndex endLine = lines.Count();
1330        lineIndex < endLine;
1331        ++lineIndex )
1332   {
1333     const LineRun& lineRun = lines[lineIndex];
1334     totalHeight += lineRun.ascender + -lineRun.descender;
1335     if( y < totalHeight )
1336     {
1337       return lineIndex;
1338     }
1339   }
1340
1341   if( lineIndex == 0 )
1342   {
1343     return 0;
1344   }
1345
1346   return lineIndex-1;
1347 }
1348
1349 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1350 {
1351   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1352   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1353
1354   if ( mLogicalModel->mText.Count() == 0 )
1355   {
1356     return;  // if model empty
1357   }
1358
1359   if( hitCharacter >= mLogicalModel->mText.Count() )
1360   {
1361     // Closest hit character is the last character.
1362     if ( hitCharacter ==  mLogicalModel->mText.Count() )
1363     {
1364       hitCharacter--; //Hit character index set to last character in logical model
1365     }
1366     else
1367     {
1368       // hitCharacter is out of bounds
1369       return;
1370     }
1371   }
1372
1373   startIndex = hitCharacter;
1374   endIndex = hitCharacter;
1375
1376   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1377   {
1378     // Find the start and end of the text
1379     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1380     {
1381       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1382       if( TextAbstraction::IsWhiteSpace( charCode ) )
1383       {
1384         break;
1385       }
1386     }
1387     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1388     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1389     {
1390       Character charCode = mLogicalModel->mText[ endIndex ];
1391       if( TextAbstraction::IsWhiteSpace( charCode ) )
1392       {
1393         break;
1394       }
1395     }
1396   }
1397 }
1398
1399 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1400                                                         float visualY )
1401 {
1402   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1403
1404   if( NULL == mEventData )
1405   {
1406     // Nothing to do if there is no text input.
1407     return 0u;
1408   }
1409
1410   CharacterIndex logicalIndex = 0u;
1411
1412   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1413   const Length numberOfLines  = mVisualModel->mLines.Count();
1414   if( 0 == numberOfGlyphs ||
1415       0 == numberOfLines )
1416   {
1417     return logicalIndex;
1418   }
1419
1420   // Find which line is closest
1421   const LineIndex lineIndex = GetClosestLine( visualY );
1422   const LineRun& line = mVisualModel->mLines[lineIndex];
1423
1424   // Get the positions of the glyphs.
1425   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1426   const Vector2* const positionsBuffer = positions.Begin();
1427
1428   // Get the visual to logical conversion tables.
1429   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1430   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1431
1432   // Get the character to glyph conversion table.
1433   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1434
1435   // Get the glyphs per character table.
1436   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1437   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1438
1439   // If the vector is void, there is no right to left characters.
1440   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1441
1442   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1443   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1444   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1445
1446   // Whether there is a hit on a glyph.
1447   bool matched = false;
1448
1449   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1450   CharacterIndex visualIndex = startCharacter;
1451   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1452   {
1453     // The character in logical order.
1454     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1455
1456     // Get the script of the character.
1457     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1458
1459     // The first glyph for that character in logical order.
1460     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1461     // The number of glyphs for that character
1462     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1463
1464     // Get the metrics for the group of glyphs.
1465     GlyphMetrics glyphMetrics;
1466     GetGlyphsMetrics( glyphLogicalOrderIndex,
1467                       numberOfGlyphs,
1468                       glyphMetrics,
1469                       mVisualModel,
1470                       mMetrics );
1471
1472     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1473
1474     // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»...
1475     const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1476     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1477
1478     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1479     {
1480       // Find the mid-point of the area containing the glyph
1481       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1482
1483       if( visualX < glyphCenter )
1484       {
1485         visualIndex += index;
1486         matched = true;
1487         break;
1488       }
1489     }
1490
1491     if( matched )
1492     {
1493       break;
1494     }
1495   }
1496
1497   // Return the logical position of the cursor in characters.
1498
1499   if( !matched )
1500   {
1501     visualIndex = endCharacter;
1502   }
1503
1504   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1505   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1506
1507   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
1508
1509   return logicalIndex;
1510 }
1511
1512 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1513                                           CursorInfo& cursorInfo )
1514 {
1515   // TODO: Check for multiline with \n, etc...
1516
1517   // Check if the logical position is the first or the last one of the text.
1518   const bool isFirstPosition = 0u == logical;
1519   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1520
1521   if( isFirstPosition && isLastPosition )
1522   {
1523     // There is zero characters. Get the default font's line height.
1524     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1525     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1526
1527     cursorInfo.primaryPosition.x = mEventData->mDecorator->GetCursorWidth();
1528     cursorInfo.primaryPosition.y = 0.f;
1529
1530     // Nothing else to do.
1531     return;
1532   }
1533
1534   // 'logical' is the logical 'cursor' index.
1535   // Get the next and current logical 'character' index.
1536   const CharacterIndex nextCharacterIndex = logical;
1537   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1538
1539   // Get the direction of the character and the next one.
1540   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1541
1542   CharacterDirection isCurrentRightToLeft = false;
1543   CharacterDirection isNextRightToLeft = false;
1544   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1545   {
1546     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1547     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1548   }
1549
1550   // Get the line where the character is laid-out.
1551   const LineRun* const modelLines = mVisualModel->mLines.Begin();
1552
1553   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1554   const LineRun& line = *( modelLines + lineIndex );
1555
1556   // Get the paragraph's direction.
1557   const CharacterDirection isRightToLeftParagraph = line.direction;
1558
1559   // Check whether there is an alternative position:
1560
1561   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1562                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1563
1564   // Set the line height.
1565   cursorInfo.lineHeight = line.ascender + -line.descender;
1566
1567   // Calculate the primary cursor.
1568
1569   CharacterIndex index = characterIndex;
1570   if( cursorInfo.isSecondaryCursor )
1571   {
1572     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1573
1574     if( isLastPosition )
1575     {
1576       // The position of the cursor after the last character needs special
1577       // care depending on its direction and the direction of the paragraph.
1578
1579       // Need to find the first character after the last character with the paragraph's direction.
1580       // i.e l0 l1 l2 r0 r1 should find r0.
1581
1582       // TODO: check for more than one line!
1583       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1584       index = mLogicalModel->GetLogicalCharacterIndex( index );
1585     }
1586     else
1587     {
1588       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1589     }
1590   }
1591
1592   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1593   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1594   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1595   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1596   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
1597
1598   // Convert the cursor position into the glyph position.
1599   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
1600   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1601   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
1602
1603   // Get the metrics for the group of glyphs.
1604   GlyphMetrics glyphMetrics;
1605   GetGlyphsMetrics( primaryGlyphIndex,
1606                     primaryNumberOfGlyphs,
1607                     glyphMetrics,
1608                     mVisualModel,
1609                     mMetrics );
1610
1611   // Whether to add the glyph's advance to the cursor position.
1612   // 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,
1613   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
1614   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
1615   //
1616   // FLCP A
1617   // ------
1618   // 0000 1
1619   // 0001 1
1620   // 0010 0
1621   // 0011 0
1622   // 0100 1
1623   // 0101 0
1624   // 0110 1
1625   // 0111 0
1626   // 1000 0
1627   // 1001 x
1628   // 1010 x
1629   // 1011 1
1630   // 1100 x
1631   // 1101 x
1632   // 1110 x
1633   // 1111 x
1634   //
1635   // Where F -> isFirstPosition
1636   //       L -> isLastPosition
1637   //       C -> isCurrentRightToLeft
1638   //       P -> isRightToLeftParagraph
1639   //       A -> Whether to add the glyph's advance.
1640
1641   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
1642                                  ( isFirstPosition && isRightToLeftParagraph ) ||
1643                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
1644
1645   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
1646
1647   if( !isLastPosition &&
1648       ( primaryNumberOfCharacters > 1u ) )
1649   {
1650     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
1651
1652     bool isCurrentRightToLeft = false;
1653     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1654     {
1655       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
1656     }
1657
1658     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
1659     if( isCurrentRightToLeft )
1660     {
1661       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
1662     }
1663
1664     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1665   }
1666
1667   // Get the glyph position and x bearing.
1668   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
1669
1670   // Set the primary cursor's height.
1671   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1672
1673   // Set the primary cursor's position.
1674   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
1675   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1676
1677   // Calculate the secondary cursor.
1678
1679   if( cursorInfo.isSecondaryCursor )
1680   {
1681     // Set the secondary cursor's height.
1682     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1683
1684     CharacterIndex index = characterIndex;
1685     if( !isLastPosition )
1686     {
1687       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1688     }
1689
1690     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
1691     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1692
1693     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
1694
1695     GetGlyphsMetrics( secondaryGlyphIndex,
1696                       secondaryNumberOfGlyphs,
1697                       glyphMetrics,
1698                       mVisualModel,
1699                       mMetrics );
1700
1701     // Set the secondary cursor's position.
1702     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1703     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1704   }
1705 }
1706
1707 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1708 {
1709   if( NULL == mEventData )
1710   {
1711     // Nothing to do if there is no text input.
1712     return 0u;
1713   }
1714
1715   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1716
1717   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1718   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1719
1720   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1721   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1722
1723   if( numberOfCharacters > 1u )
1724   {
1725     const Script script = mLogicalModel->GetScript( index );
1726     if( HasLigatureMustBreak( script ) )
1727     {
1728       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»,  ...
1729       numberOfCharacters = 1u;
1730     }
1731   }
1732   else
1733   {
1734     while( 0u == numberOfCharacters )
1735     {
1736       ++glyphIndex;
1737       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1738     }
1739   }
1740
1741   if( index < mEventData->mPrimaryCursorPosition )
1742   {
1743     cursorIndex -= numberOfCharacters;
1744   }
1745   else
1746   {
1747     cursorIndex += numberOfCharacters;
1748   }
1749
1750   return cursorIndex;
1751 }
1752
1753 void Controller::Impl::UpdateCursorPosition()
1754 {
1755   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1756   if( NULL == mEventData )
1757   {
1758     // Nothing to do if there is no text input.
1759     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1760     return;
1761   }
1762
1763   if( IsShowingPlaceholderText() || ( 0u == mLogicalModel->mText.Count() ) )
1764   {
1765     // Do not want to use the place-holder text to set the cursor position.
1766
1767     // Use the line's height of the font's family set to set the cursor's size.
1768     // If there is no font's family set, use the default font.
1769     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1770
1771     float lineHeight = 0.f;
1772
1773     FontId defaultFontId = 0u;
1774     if( NULL == mFontDefaults )
1775     {
1776       TextAbstraction::FontDescription fontDescription;
1777       defaultFontId = mFontClient.GetFontId( fontDescription );
1778     }
1779     else
1780     {
1781       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1782     }
1783
1784     Text::FontMetrics fontMetrics;
1785     mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1786
1787     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1788
1789
1790     Vector2 cursorPosition;
1791
1792     switch( mLayoutEngine.GetHorizontalAlignment() )
1793     {
1794       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1795       {
1796         cursorPosition.x = mEventData->mDecorator->GetCursorWidth();
1797         break;
1798       }
1799       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1800       {
1801         cursorPosition.x = floor( 0.5f * mVisualModel->mControlSize.width );
1802         break;
1803       }
1804       case LayoutEngine::HORIZONTAL_ALIGN_END:
1805       {
1806         cursorPosition.x = mVisualModel->mControlSize.width;
1807         break;
1808       }
1809     }
1810
1811     switch( mLayoutEngine.GetVerticalAlignment() )
1812     {
1813       case LayoutEngine::VERTICAL_ALIGN_TOP:
1814       {
1815         cursorPosition.y = 0.f;
1816         break;
1817       }
1818       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1819       {
1820         cursorPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - lineHeight ) );
1821         break;
1822       }
1823       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1824       {
1825         cursorPosition.y = mVisualModel->mControlSize.height - lineHeight;
1826         break;
1827       }
1828     }
1829
1830     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1831                                          cursorPosition.x,
1832                                          cursorPosition.y,
1833                                          lineHeight,
1834                                          lineHeight );
1835   }
1836   else
1837   {
1838     CursorInfo cursorInfo;
1839     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1840                        cursorInfo );
1841
1842     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1843     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1844
1845     // Sets the cursor position.
1846     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1847                                          cursorPosition.x,
1848                                          cursorPosition.y,
1849                                          cursorInfo.primaryCursorHeight,
1850                                          cursorInfo.lineHeight );
1851     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1852
1853     // Sets the grab handle position.
1854     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1855                                          cursorPosition.x,
1856                                          cursorPosition.y,
1857                                          cursorInfo.lineHeight );
1858
1859     if( cursorInfo.isSecondaryCursor )
1860     {
1861       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1862       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1863                                            cursorInfo.secondaryPosition.x + offset.x,
1864                                            cursorInfo.secondaryPosition.y + offset.y,
1865                                            cursorInfo.secondaryCursorHeight,
1866                                            cursorInfo.lineHeight );
1867       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1868     }
1869     else
1870     {
1871       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1872     }
1873   }
1874   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1875 }
1876
1877 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1878 {
1879   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1880       ( RIGHT_SELECTION_HANDLE != handleType ) )
1881   {
1882     return;
1883   }
1884
1885   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1886   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1887
1888   CursorInfo cursorInfo;
1889   GetCursorPosition( index,
1890                      cursorInfo );
1891
1892   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1893   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1894
1895   // Sets the grab handle position.
1896   mEventData->mDecorator->SetPosition( handleType,
1897                                        cursorPosition.x,
1898                                        cursorPosition.y,
1899                                        cursorInfo.lineHeight );
1900
1901   // If selection handle at start of the text and other at end of the text then all text is selected.
1902   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1903   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1904   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1905 }
1906
1907 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1908 {
1909   // Clamp between -space & 0 (and the text alignment).
1910   if( actualSize.width > mVisualModel->mControlSize.width )
1911   {
1912     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
1913     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1914     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1915
1916     mEventData->mDecoratorUpdated = true;
1917   }
1918   else
1919   {
1920     mEventData->mScrollPosition.x = 0.f;
1921   }
1922 }
1923
1924 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1925 {
1926   // Clamp between -space & 0 (and the text alignment).
1927   if( actualSize.height > mVisualModel->mControlSize.height )
1928   {
1929     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
1930     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1931     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1932
1933     mEventData->mDecoratorUpdated = true;
1934   }
1935   else
1936   {
1937     mEventData->mScrollPosition.y = 0.f;
1938   }
1939 }
1940
1941 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1942 {
1943   Vector2 offset;
1944   bool updateDecorator = false;
1945   if( position.x < 0.f )
1946   {
1947     offset.x = -position.x;
1948     mEventData->mScrollPosition.x += offset.x;
1949     updateDecorator = true;
1950   }
1951   else if( position.x > mVisualModel->mControlSize.width )
1952   {
1953     offset.x = mVisualModel->mControlSize.width - position.x;
1954     mEventData->mScrollPosition.x += offset.x;
1955     updateDecorator = true;
1956   }
1957
1958   if( updateDecorator && mEventData->mDecorator )
1959   {
1960     mEventData->mDecorator->UpdatePositions( offset );
1961   }
1962
1963   // TODO : calculate the vertical scroll.
1964 }
1965
1966 void Controller::Impl::ScrollTextToMatchCursor()
1967 {
1968   // Get the current cursor position in decorator coords.
1969   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1970
1971   // Calculate the new cursor position.
1972   CursorInfo cursorInfo;
1973   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1974                      cursorInfo );
1975
1976   // Calculate the offset to match the cursor position before the character was deleted.
1977   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1978
1979   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1980
1981   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1982   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1983
1984   // Sets the cursor position.
1985   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1986                                        cursorPosition.x,
1987                                        cursorPosition.y,
1988                                        cursorInfo.primaryCursorHeight,
1989                                        cursorInfo.lineHeight );
1990
1991   // Sets the grab handle position.
1992   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1993                                        cursorPosition.x,
1994                                        cursorPosition.y,
1995                                        cursorInfo.lineHeight );
1996
1997   if( cursorInfo.isSecondaryCursor )
1998   {
1999     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2000                                          cursorInfo.secondaryPosition.x + offset.x,
2001                                          cursorInfo.secondaryPosition.y + offset.y,
2002                                          cursorInfo.secondaryCursorHeight,
2003                                          cursorInfo.lineHeight );
2004     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2005   }
2006
2007   // Set which cursors are active according the state.
2008   if( ( EventData::EDITING == mEventData->mState )                  ||
2009       ( EventData::EDITING_WITH_POPUP == mEventData->mState )       ||
2010       ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
2011       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2012   {
2013     if( cursorInfo.isSecondaryCursor )
2014     {
2015       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2016     }
2017     else
2018     {
2019       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2020     }
2021   }
2022   else
2023   {
2024     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2025   }
2026 }
2027
2028 void Controller::Impl::RequestRelayout()
2029 {
2030   mControlInterface.RequestTextRelayout();
2031 }
2032
2033 } // namespace Text
2034
2035 } // namespace Toolkit
2036
2037 } // namespace Dali