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