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