Merge "Markup procesor - Font." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/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       // This paragraph has right to left text. Some characters may need to be mirrored.
416       // TODO: consider if the mirrored string can be stored as well.
417
418       textMirrored = GetMirroredText( utf32Characters,
419                                       mirroredUtf32Characters,
420                                       bidirectionalInfo );
421
422       // Only set the character directions if there is right to left characters.
423       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
424       directions.Resize( numberOfCharacters );
425
426       GetCharactersDirection( bidirectionalInfo,
427                               directions );
428     }
429     else
430     {
431       // There is no right to left characters. Clear the directions vector.
432       mLogicalModel->mCharacterDirections.Clear();
433     }
434   }
435
436   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
437   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
438   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
439   Vector<GlyphIndex> newParagraphGlyphs;
440   newParagraphGlyphs.Reserve( numberOfParagraphs );
441
442   if( SHAPE_TEXT & operations )
443   {
444     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
445     // Shapes the text.
446     ShapeText( textToShape,
447                lineBreakInfo,
448                scripts,
449                validFonts,
450                glyphs,
451                glyphsToCharactersMap,
452                charactersPerGlyph,
453                newParagraphGlyphs );
454
455     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
456     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
457     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
458   }
459
460   const Length numberOfGlyphs = glyphs.Count();
461
462   if( GET_GLYPH_METRICS & operations )
463   {
464     GlyphInfo* glyphsBuffer = glyphs.Begin();
465     mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
466
467     // Update the width and advance of all new paragraph characters.
468     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
469     {
470       const GlyphIndex index = *it;
471       GlyphInfo& glyph = *( glyphsBuffer + index );
472
473       glyph.xBearing = 0.f;
474       glyph.width = 0.f;
475       glyph.advance = 0.f;
476     }
477   }
478
479   if( ( NULL != mEventData ) &&
480       mEventData->mPreEditFlag &&
481       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
482   {
483     // Add the underline for the pre-edit text.
484     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
485     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
486
487     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
488     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
489     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
490     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
491
492     GlyphRun underlineRun;
493     underlineRun.glyphIndex = glyphStart;
494     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
495
496     // TODO: At the moment the underline runs are only for pre-edit.
497     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
498   }
499 }
500
501 bool Controller::Impl::UpdateModelStyle( OperationsMask operationsRequired )
502 {
503   bool updated = false;
504
505   if( COLOR & operationsRequired )
506   {
507     // Set the color runs in glyphs.
508     SetColorSegmentationInfo( mLogicalModel->mColorRuns,
509                               mVisualModel->mCharactersToGlyph,
510                               mVisualModel->mGlyphsPerCharacter,
511                               mVisualModel->mColorRuns );
512
513     updated = true;
514   }
515
516   return updated;
517 }
518
519 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
520 {
521   // Sets the default text's color.
522   inputStyle.textColor = mTextColor;
523
524   // Sets the default font's family name, weight, width, slant and size.
525   if( mFontDefaults )
526   {
527     inputStyle.familyName = mFontDefaults->mFontDescription.family;
528     inputStyle.weight = mFontDefaults->mFontDescription.weight;
529     inputStyle.width = mFontDefaults->mFontDescription.width;
530     inputStyle.slant = mFontDefaults->mFontDescription.slant;
531     inputStyle.size = mFontDefaults->mDefaultPointSize;
532
533     inputStyle.familyDefined = mFontDefaults->familyDefined;
534     inputStyle.weightDefined = mFontDefaults->weightDefined;
535     inputStyle.widthDefined = mFontDefaults->widthDefined;
536     inputStyle.slantDefined = mFontDefaults->slantDefined;
537     inputStyle.sizeDefined = mFontDefaults->sizeDefined;
538   }
539   else
540   {
541     inputStyle.familyName.clear();
542     inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
543     inputStyle.width = TextAbstraction::FontWidth::NORMAL;
544     inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
545     inputStyle.size = 0.f;
546
547     inputStyle.familyDefined = false;
548     inputStyle.weightDefined = false;
549     inputStyle.widthDefined = false;
550     inputStyle.slantDefined = false;
551     inputStyle.sizeDefined = false;
552   }
553 }
554
555 float Controller::Impl::GetDefaultFontLineHeight()
556 {
557   FontId defaultFontId = 0u;
558   if( NULL == mFontDefaults )
559   {
560     TextAbstraction::FontDescription fontDescription;
561     defaultFontId = mFontClient.GetFontId( fontDescription );
562   }
563   else
564   {
565     defaultFontId = mFontDefaults->GetFontId( mFontClient );
566   }
567
568   Text::FontMetrics fontMetrics;
569   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
570
571   return( fontMetrics.ascender - fontMetrics.descender );
572 }
573
574 void Controller::Impl::OnCursorKeyEvent( const Event& event )
575 {
576   if( NULL == mEventData )
577   {
578     // Nothing to do if there is no text input.
579     return;
580   }
581
582   int keyCode = event.p1.mInt;
583
584   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
585   {
586     if( mEventData->mPrimaryCursorPosition > 0u )
587     {
588       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
589     }
590   }
591   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
592   {
593     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
594     {
595       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
596     }
597   }
598   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
599   {
600     // TODO
601   }
602   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
603   {
604     // TODO
605   }
606
607   mEventData->mUpdateCursorPosition = true;
608   mEventData->mUpdateInputStyle = true;
609   mEventData->mScrollAfterUpdatePosition = true;
610 }
611
612 void Controller::Impl::OnTapEvent( const Event& event )
613 {
614   if( NULL != mEventData )
615   {
616     const unsigned int tapCount = event.p1.mUint;
617
618     if( 1u == tapCount )
619     {
620       if( IsShowingRealText() )
621       {
622         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
623         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
624
625         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
626                                                                     yPosition );
627
628         // When the cursor position is changing, delay cursor blinking
629         mEventData->mDecorator->DelayCursorBlink();
630       }
631       else
632       {
633         mEventData->mPrimaryCursorPosition = 0u;
634       }
635
636       mEventData->mUpdateCursorPosition = true;
637       mEventData->mScrollAfterUpdatePosition = true;
638       mEventData->mUpdateInputStyle = true;
639
640       // Notify the cursor position to the imf manager.
641       if( mEventData->mImfManager )
642       {
643         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
644         mEventData->mImfManager.NotifyCursorPosition();
645       }
646     }
647   }
648 }
649
650 void Controller::Impl::OnPanEvent( const Event& event )
651 {
652   if( NULL == mEventData )
653   {
654     // Nothing to do if there is no text input.
655     return;
656   }
657
658   int state = event.p1.mInt;
659
660   if( Gesture::Started    == state ||
661       Gesture::Continuing == state )
662   {
663     const Vector2& actualSize = mVisualModel->GetActualSize();
664     const Vector2 currentScroll = mEventData->mScrollPosition;
665
666     if( mEventData->mHorizontalScrollingEnabled )
667     {
668       const float displacementX = event.p2.mFloat;
669       mEventData->mScrollPosition.x += displacementX;
670
671       ClampHorizontalScroll( actualSize );
672     }
673
674     if( mEventData->mVerticalScrollingEnabled )
675     {
676       const float displacementY = event.p3.mFloat;
677       mEventData->mScrollPosition.y += displacementY;
678
679       ClampVerticalScroll( actualSize );
680     }
681
682     if( mEventData->mDecorator )
683     {
684       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
685     }
686   }
687 }
688
689 void Controller::Impl::OnLongPressEvent( const Event& event )
690 {
691   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
692
693   if( EventData::EDITING == mEventData->mState )
694   {
695     ChangeState ( EventData::EDITING_WITH_POPUP );
696     mEventData->mDecoratorUpdated = true;
697   }
698 }
699
700 void Controller::Impl::OnHandleEvent( const Event& event )
701 {
702   if( NULL == mEventData )
703   {
704     // Nothing to do if there is no text input.
705     return;
706   }
707
708   const unsigned int state = event.p1.mUint;
709   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
710
711   if( HANDLE_PRESSED == state )
712   {
713     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
714     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
715     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
716
717     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
718
719     if( Event::GRAB_HANDLE_EVENT == event.type )
720     {
721       ChangeState ( EventData::GRAB_HANDLE_PANNING );
722
723       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
724       {
725         mEventData->mPrimaryCursorPosition = handleNewPosition;
726         mEventData->mUpdateCursorPosition = true;
727       }
728     }
729     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
730     {
731       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
732
733       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
734           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
735       {
736         mEventData->mLeftSelectionPosition = handleNewPosition;
737
738         mEventData->mUpdateLeftSelectionPosition = true;
739       }
740     }
741     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
742     {
743       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
744
745       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
746           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
747       {
748         mEventData->mRightSelectionPosition = handleNewPosition;
749
750         mEventData->mUpdateRightSelectionPosition = true;
751       }
752     }
753   } // end ( HANDLE_PRESSED == state )
754   else if( ( HANDLE_RELEASED == state ) ||
755            handleStopScrolling )
756   {
757     CharacterIndex handlePosition = 0u;
758     if( handleStopScrolling )
759     {
760       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
761       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
762       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
763
764       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
765     }
766
767     if( Event::GRAB_HANDLE_EVENT == event.type )
768     {
769       mEventData->mUpdateCursorPosition = true;
770       mEventData->mUpdateInputStyle = true;
771
772       if( !IsClipboardEmpty() )
773       {
774         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
775       }
776
777       if( handleStopScrolling )
778       {
779         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
780         mEventData->mPrimaryCursorPosition = handlePosition;
781       }
782     }
783     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
784     {
785       ChangeState( EventData::SELECTING );
786
787       if( handleStopScrolling )
788       {
789         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
790         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
791
792         if( mEventData->mUpdateLeftSelectionPosition )
793         {
794           mEventData->mLeftSelectionPosition = handlePosition;
795         }
796       }
797     }
798     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
799     {
800       ChangeState( EventData::SELECTING );
801
802       if( handleStopScrolling )
803       {
804         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
805         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
806         if( mEventData->mUpdateRightSelectionPosition )
807         {
808           mEventData->mRightSelectionPosition = handlePosition;
809         }
810       }
811     }
812
813     mEventData->mDecoratorUpdated = true;
814   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
815   else if( HANDLE_SCROLLING == state )
816   {
817     const float xSpeed = event.p2.mFloat;
818     const Vector2& actualSize = mVisualModel->GetActualSize();
819     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
820
821     mEventData->mScrollPosition.x += xSpeed;
822
823     ClampHorizontalScroll( actualSize );
824
825     bool endOfScroll = false;
826     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
827     {
828       // Notify the decorator there is no more text to scroll.
829       // The decorator won't send more scroll events.
830       mEventData->mDecorator->NotifyEndOfScroll();
831       // Still need to set the position of the handle.
832       endOfScroll = true;
833     }
834
835     // Set the position of the handle.
836     const bool scrollRightDirection = xSpeed > 0.f;
837     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
838     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
839
840     if( Event::GRAB_HANDLE_EVENT == event.type )
841     {
842       ChangeState( EventData::GRAB_HANDLE_PANNING );
843
844       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
845
846       // Position the grag handle close to either the left or right edge.
847       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
848
849       // Get the new handle position.
850       // The grab handle's position is in decorator coords. Need to transforms to text coords.
851       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
852                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
853
854       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
855       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
856       mEventData->mPrimaryCursorPosition = handlePosition;
857       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
858     }
859     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
860     {
861       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
862       //       Think if something can be done to save power.
863
864       ChangeState( EventData::SELECTION_HANDLE_PANNING );
865
866       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
867
868       // Position the selection handle close to either the left or right edge.
869       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
870
871       // Get the new handle position.
872       // The selection handle's position is in decorator coords. Need to transforms to text coords.
873       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
874                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
875
876       if( leftSelectionHandleEvent )
877       {
878         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
879         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
880         if( differentHandles )
881         {
882           mEventData->mLeftSelectionPosition = handlePosition;
883         }
884       }
885       else
886       {
887         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
888         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
889         if( differentHandles )
890         {
891           mEventData->mRightSelectionPosition = handlePosition;
892         }
893       }
894
895       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
896       {
897         RepositionSelectionHandles();
898
899         mEventData->mScrollAfterUpdatePosition = true;
900       }
901     }
902     mEventData->mDecoratorUpdated = true;
903   } // end ( HANDLE_SCROLLING == state )
904 }
905
906 void Controller::Impl::OnSelectEvent( const Event& event )
907 {
908   if( NULL == mEventData )
909   {
910     // Nothing to do if there is no text.
911     return;
912   }
913
914   if( mEventData->mSelectionEnabled )
915   {
916     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
917     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
918     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
919
920     // Calculates the logical position from the x,y coords.
921     RepositionSelectionHandles( xPosition,
922                                 yPosition );
923
924     mEventData->mUpdateLeftSelectionPosition = true;
925     mEventData->mUpdateRightSelectionPosition = true;
926
927     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
928   }
929 }
930
931 void Controller::Impl::OnSelectAllEvent()
932 {
933   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
934
935   if( NULL == mEventData )
936   {
937     // Nothing to do if there is no text.
938     return;
939   }
940
941   if( mEventData->mSelectionEnabled )
942   {
943     mEventData->mLeftSelectionPosition = 0u;
944     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
945
946     mEventData->mScrollAfterUpdatePosition = true;
947     mEventData->mUpdateLeftSelectionPosition = true;
948     mEventData->mUpdateRightSelectionPosition = true;
949   }
950 }
951
952 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
953 {
954   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
955   {
956     // Nothing to select if handles are in the same place.
957     selectedText.clear();
958     return;
959   }
960
961   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
962
963   //Get start and end position of selection
964   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
965   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
966
967   // Validate the start and end selection points
968   if( ( startOfSelectedText + lengthOfSelectedText ) <= mLogicalModel->mText.Count() )
969   {
970     //Get text as a UTF8 string
971     Vector<Character>& utf32Characters = mLogicalModel->mText;
972
973     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
974
975     if( deleteAfterRetrieval ) // Only delete text if copied successfully
976     {
977       // Set as input style the style of the first deleted character.
978       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
979
980       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
981
982       // Delete text between handles
983       Vector<Character>& currentText = mLogicalModel->mText;
984
985       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
986       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
987       currentText.Erase( first, last );
988
989       // Scroll after delete.
990       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
991       mEventData->mScrollAfterDelete = true;
992     }
993     // Udpade the cursor position and the decorator.
994     // Scroll after the position is updated if is not scrolling after delete.
995     mEventData->mUpdateCursorPosition = true;
996     mEventData->mScrollAfterUpdatePosition = !mEventData->mScrollAfterDelete;
997     mEventData->mDecoratorUpdated = true;
998   }
999 }
1000
1001 void Controller::Impl::ShowClipboard()
1002 {
1003   if( mClipboard )
1004   {
1005     mClipboard.ShowClipboard();
1006   }
1007 }
1008
1009 void Controller::Impl::HideClipboard()
1010 {
1011   if( mClipboard )
1012   {
1013     mClipboard.HideClipboard();
1014   }
1015 }
1016
1017 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1018 {
1019   //Send string to clipboard
1020   return ( mClipboard && mClipboard.SetItem( source ) );
1021 }
1022
1023 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1024 {
1025   std::string selectedText;
1026   RetrieveSelection( selectedText, deleteAfterSending );
1027   CopyStringToClipboard( selectedText );
1028   ChangeState( EventData::EDITING );
1029 }
1030
1031 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1032 {
1033   if ( mClipboard )
1034   {
1035     retrievedString =  mClipboard.GetItem( itemIndex );
1036   }
1037 }
1038
1039 void Controller::Impl::RepositionSelectionHandles()
1040 {
1041   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1042   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1043
1044   if( selectionStart == selectionEnd )
1045   {
1046     // Nothing to select if handles are in the same place.
1047     return;
1048   }
1049
1050   mEventData->mDecorator->ClearHighlights();
1051
1052   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1053   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1054   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1055   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1056   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1057   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1058   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1059
1060   // TODO: Better algorithm to create the highlight box.
1061   // TODO: Multi-line.
1062
1063   // Get the height of the line.
1064   const Vector<LineRun>& lines = mVisualModel->mLines;
1065   const LineRun& firstLine = *lines.Begin();
1066   const float height = firstLine.ascender + -firstLine.descender;
1067
1068   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1069   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1070   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1071
1072   // Swap the indices if the start is greater than the end.
1073   const bool indicesSwapped = selectionStart > selectionEnd;
1074
1075   // Tell the decorator to flip the selection handles if needed.
1076   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1077
1078   if( indicesSwapped )
1079   {
1080     std::swap( selectionStart, selectionEnd );
1081   }
1082
1083   // Get the indices to the first and last selected glyphs.
1084   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1085   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1086   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1087   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1088
1089   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1090   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1091   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1092
1093   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1094   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1095   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1096
1097   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1098
1099   // Traverse the glyphs.
1100   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1101   {
1102     const GlyphInfo& glyph = *( glyphsBuffer + index );
1103     const Vector2& position = *( positionsBuffer + index );
1104
1105     if( splitStartGlyph )
1106     {
1107       // 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.
1108
1109       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1110       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1111       // Get the direction of the character.
1112       CharacterDirection isCurrentRightToLeft = false;
1113       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1114       {
1115         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1116       }
1117
1118       // The end point could be in the middle of the ligature.
1119       // Calculate the number of characters selected.
1120       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1121
1122       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1123
1124       mEventData->mDecorator->AddHighlight( xPosition,
1125                                             offset.y,
1126                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1127                                             offset.y + height );
1128
1129       splitStartGlyph = false;
1130       continue;
1131     }
1132
1133     if( splitEndGlyph && ( index == glyphEnd ) )
1134     {
1135       // 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.
1136
1137       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1138       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1139       // Get the direction of the character.
1140       CharacterDirection isCurrentRightToLeft = false;
1141       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1142       {
1143         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1144       }
1145
1146       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1147
1148       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1149       mEventData->mDecorator->AddHighlight( xPosition,
1150                                             offset.y,
1151                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1152                                             offset.y + height );
1153
1154       splitEndGlyph = false;
1155       continue;
1156     }
1157
1158     const float xPosition = position.x - glyph.xBearing + offset.x;
1159     mEventData->mDecorator->AddHighlight( xPosition,
1160                                           offset.y,
1161                                           xPosition + glyph.advance,
1162                                           offset.y + height );
1163   }
1164
1165   CursorInfo primaryCursorInfo;
1166   GetCursorPosition( mEventData->mLeftSelectionPosition,
1167                      primaryCursorInfo );
1168
1169   CursorInfo secondaryCursorInfo;
1170   GetCursorPosition( mEventData->mRightSelectionPosition,
1171                      secondaryCursorInfo );
1172
1173   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1174   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1175
1176   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1177                                        primaryPosition.x,
1178                                        primaryCursorInfo.lineOffset + offset.y,
1179                                        primaryCursorInfo.lineHeight );
1180
1181   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1182                                        secondaryPosition.x,
1183                                        secondaryCursorInfo.lineOffset + offset.y,
1184                                        secondaryCursorInfo.lineHeight );
1185
1186   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1187   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1188
1189   // Set the flag to update the decorator.
1190   mEventData->mDecoratorUpdated = true;
1191 }
1192
1193 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1194 {
1195   if( NULL == mEventData )
1196   {
1197     // Nothing to do if there is no text input.
1198     return;
1199   }
1200
1201   if( IsShowingPlaceholderText() )
1202   {
1203     // Nothing to do if there is the place-holder text.
1204     return;
1205   }
1206
1207   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1208   const Length numberOfLines  = mVisualModel->mLines.Count();
1209   if( ( 0 == numberOfGlyphs ) ||
1210       ( 0 == numberOfLines ) )
1211   {
1212     // Nothing to do if there is no text.
1213     return;
1214   }
1215
1216   // Find which word was selected
1217   CharacterIndex selectionStart( 0 );
1218   CharacterIndex selectionEnd( 0 );
1219   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1220   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1221
1222   if( selectionStart == selectionEnd )
1223   {
1224     ChangeState( EventData::EDITING );
1225     // Nothing to select. i.e. a white space, out of bounds
1226     return;
1227   }
1228
1229   mEventData->mLeftSelectionPosition = selectionStart;
1230   mEventData->mRightSelectionPosition = selectionEnd;
1231 }
1232
1233 void Controller::Impl::SetPopupButtons()
1234 {
1235   /**
1236    *  Sets the Popup buttons to be shown depending on State.
1237    *
1238    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1239    *
1240    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1241    */
1242
1243   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1244
1245   if( EventData::SELECTING == mEventData->mState )
1246   {
1247     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1248
1249     if( !IsClipboardEmpty() )
1250     {
1251       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1252       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1253     }
1254
1255     if( !mEventData->mAllTextSelected )
1256     {
1257       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1258     }
1259   }
1260   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1261   {
1262     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1263     {
1264       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1265     }
1266
1267     if( !IsClipboardEmpty() )
1268     {
1269       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1270       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1271     }
1272   }
1273   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1274   {
1275     if ( !IsClipboardEmpty() )
1276     {
1277       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1278       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1279     }
1280   }
1281
1282   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1283 }
1284
1285 void Controller::Impl::ChangeState( EventData::State newState )
1286 {
1287   if( NULL == mEventData )
1288   {
1289     // Nothing to do if there is no text input.
1290     return;
1291   }
1292
1293   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
1294
1295   if( mEventData->mState != newState )
1296   {
1297     mEventData->mState = newState;
1298
1299     if( EventData::INACTIVE == mEventData->mState )
1300     {
1301       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1302       mEventData->mDecorator->StopCursorBlink();
1303       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1304       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1305       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1306       mEventData->mDecorator->SetPopupActive( false );
1307       mEventData->mDecoratorUpdated = true;
1308       HideClipboard();
1309     }
1310     else if( EventData::INTERRUPTED  == mEventData->mState)
1311     {
1312       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1313       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1314       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1315       mEventData->mDecorator->SetPopupActive( false );
1316       mEventData->mDecoratorUpdated = true;
1317       HideClipboard();
1318     }
1319     else if( EventData::SELECTING == mEventData->mState )
1320     {
1321       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1322       mEventData->mDecorator->StopCursorBlink();
1323       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1324       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1325       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1326       if( mEventData->mGrabHandlePopupEnabled )
1327       {
1328         SetPopupButtons();
1329         mEventData->mDecorator->SetPopupActive( true );
1330       }
1331       mEventData->mDecoratorUpdated = true;
1332     }
1333     else if( EventData::EDITING == mEventData->mState )
1334     {
1335       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1336       if( mEventData->mCursorBlinkEnabled )
1337       {
1338         mEventData->mDecorator->StartCursorBlink();
1339       }
1340       // Grab handle is not shown until a tap is received whilst EDITING
1341       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1342       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1343       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1344       if( mEventData->mGrabHandlePopupEnabled )
1345       {
1346         mEventData->mDecorator->SetPopupActive( false );
1347       }
1348       mEventData->mDecoratorUpdated = true;
1349       HideClipboard();
1350     }
1351     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1352     {
1353       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1354
1355       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1356       if( mEventData->mCursorBlinkEnabled )
1357       {
1358         mEventData->mDecorator->StartCursorBlink();
1359       }
1360       if( mEventData->mSelectionEnabled )
1361       {
1362         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1363         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1364       }
1365       else
1366       {
1367         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1368       }
1369       if( mEventData->mGrabHandlePopupEnabled )
1370       {
1371         SetPopupButtons();
1372         mEventData->mDecorator->SetPopupActive( true );
1373       }
1374       HideClipboard();
1375       mEventData->mDecoratorUpdated = true;
1376     }
1377     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1378     {
1379       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1380
1381       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1382       if( mEventData->mCursorBlinkEnabled )
1383       {
1384         mEventData->mDecorator->StartCursorBlink();
1385       }
1386       // Grab handle is not shown until a tap is received whilst EDITING
1387       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1388       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1389       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1390       if( mEventData->mGrabHandlePopupEnabled )
1391       {
1392         mEventData->mDecorator->SetPopupActive( false );
1393       }
1394       mEventData->mDecoratorUpdated = true;
1395       HideClipboard();
1396     }
1397     else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1398     {
1399       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1400       mEventData->mDecorator->StopCursorBlink();
1401       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1402       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1403       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1404       if( mEventData->mGrabHandlePopupEnabled )
1405       {
1406         mEventData->mDecorator->SetPopupActive( false );
1407       }
1408       mEventData->mDecoratorUpdated = true;
1409     }
1410     else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1411     {
1412       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1413
1414       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1415       if( mEventData->mCursorBlinkEnabled )
1416       {
1417         mEventData->mDecorator->StartCursorBlink();
1418       }
1419       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1420       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1421       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1422       if( mEventData->mGrabHandlePopupEnabled )
1423       {
1424         mEventData->mDecorator->SetPopupActive( false );
1425       }
1426       mEventData->mDecoratorUpdated = true;
1427     }
1428     else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1429     {
1430       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1431
1432       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1433       if( mEventData->mCursorBlinkEnabled )
1434       {
1435         mEventData->mDecorator->StartCursorBlink();
1436       }
1437
1438       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1439       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1440       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1441
1442       if( mEventData->mGrabHandlePopupEnabled )
1443       {
1444         SetPopupButtons();
1445         mEventData->mDecorator->SetPopupActive( true );
1446       }
1447       HideClipboard();
1448       mEventData->mDecoratorUpdated = true;
1449     }
1450   }
1451 }
1452
1453 LineIndex Controller::Impl::GetClosestLine( float y ) const
1454 {
1455   float totalHeight = 0.f;
1456   LineIndex lineIndex = 0u;
1457
1458   const Vector<LineRun>& lines = mVisualModel->mLines;
1459   for( LineIndex endLine = lines.Count();
1460        lineIndex < endLine;
1461        ++lineIndex )
1462   {
1463     const LineRun& lineRun = lines[lineIndex];
1464     totalHeight += lineRun.ascender + -lineRun.descender;
1465     if( y < totalHeight )
1466     {
1467       return lineIndex;
1468     }
1469   }
1470
1471   if( lineIndex == 0 )
1472   {
1473     return 0;
1474   }
1475
1476   return lineIndex-1;
1477 }
1478
1479 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1480 {
1481   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1482   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1483
1484   if( mLogicalModel->mText.Count() == 0 )
1485   {
1486     return;  // if model empty
1487   }
1488
1489   if( hitCharacter >= mLogicalModel->mText.Count() )
1490   {
1491     // Closest hit character is the last character.
1492     if( hitCharacter ==  mLogicalModel->mText.Count() )
1493     {
1494       hitCharacter--; //Hit character index set to last character in logical model
1495     }
1496     else
1497     {
1498       // hitCharacter is out of bounds
1499       return;
1500     }
1501   }
1502
1503   startIndex = hitCharacter;
1504   endIndex = hitCharacter;
1505   bool isHitCharacterWhitespace = TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] );
1506
1507   // Find the start and end of the text
1508   for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1509   {
1510     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ startIndex-1 ] ) )
1511     {
1512       break;
1513     }
1514   }
1515   const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1516   for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1517   {
1518     if( isHitCharacterWhitespace != TextAbstraction::IsWhiteSpace( mLogicalModel->mText[ endIndex ] ) )
1519     {
1520       break;
1521     }
1522   }
1523 }
1524
1525 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1526                                                         float visualY )
1527 {
1528   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1529
1530   if( NULL == mEventData )
1531   {
1532     // Nothing to do if there is no text input.
1533     return 0u;
1534   }
1535
1536   CharacterIndex logicalIndex = 0u;
1537
1538   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1539   const Length numberOfLines  = mVisualModel->mLines.Count();
1540   if( ( 0 == numberOfGlyphs ) ||
1541       ( 0 == numberOfLines ) )
1542   {
1543     return logicalIndex;
1544   }
1545
1546   // Find which line is closest
1547   const LineIndex lineIndex = GetClosestLine( visualY );
1548   const LineRun& line = mVisualModel->mLines[lineIndex];
1549
1550   // Get the positions of the glyphs.
1551   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1552   const Vector2* const positionsBuffer = positions.Begin();
1553
1554   // Get the visual to logical conversion tables.
1555   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1556   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1557
1558   // Get the character to glyph conversion table.
1559   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1560
1561   // Get the glyphs per character table.
1562   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1563
1564   // If the vector is void, there is no right to left characters.
1565   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1566
1567   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1568   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1569   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1570
1571   // Whether there is a hit on a glyph.
1572   bool matched = false;
1573
1574   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1575   CharacterIndex visualIndex = startCharacter;
1576   Length numberOfCharacters = 0u;
1577   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1578   {
1579     // The character in logical order.
1580     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1581
1582     // Get the script of the character.
1583     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1584
1585     // The number of glyphs for that character
1586     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1587     ++numberOfCharacters;
1588
1589
1590     if( 0u != numberOfGlyphs )
1591     {
1592       // Get the first character/glyph of the group of glyphs.
1593       const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfCharacters;
1594       const CharacterIndex firstLogicalCharacterIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + firstVisualCharacterIndex ) : firstVisualCharacterIndex;
1595       const GlyphIndex firstLogicalGlyphIndex = *( charactersToGlyphBuffer + firstLogicalCharacterIndex );
1596
1597       // Get the metrics for the group of glyphs.
1598       GlyphMetrics glyphMetrics;
1599       GetGlyphsMetrics( firstLogicalGlyphIndex,
1600                         numberOfGlyphs,
1601                         glyphMetrics,
1602                         mVisualModel,
1603                         mMetrics );
1604
1605       // Get the position of the first glyph.
1606       const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex );
1607
1608       // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ﻻ.
1609       const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script );
1610       const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u;
1611       const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfBlocks );
1612
1613       GlyphIndex index = 0u;
1614       for( ; !matched && ( index < numberOfBlocks ); ++index )
1615       {
1616         // Find the mid-point of the area containing the glyph
1617         const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1618
1619         if( visualX < glyphCenter )
1620         {
1621           matched = true;
1622           break;
1623         }
1624       }
1625
1626       if( matched )
1627       {
1628         visualIndex = firstVisualCharacterIndex + index;
1629         break;
1630       }
1631
1632       numberOfCharacters = 0u;
1633     }
1634
1635   }
1636
1637
1638   // Return the logical position of the cursor in characters.
1639
1640   if( !matched )
1641   {
1642     visualIndex = endCharacter;
1643   }
1644
1645   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1646   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1647
1648   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
1649
1650   return logicalIndex;
1651 }
1652
1653 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1654                                           CursorInfo& cursorInfo )
1655 {
1656   // TODO: Check for multiline with \n, etc...
1657
1658   const Length numberOfCharacters = mLogicalModel->mText.Count();
1659   if( !IsShowingRealText() )
1660   {
1661     // Do not want to use the place-holder text to set the cursor position.
1662
1663     // Use the line's height of the font's family set to set the cursor's size.
1664     // If there is no font's family set, use the default font.
1665     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1666
1667     cursorInfo.lineOffset = 0.f;
1668     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1669     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1670
1671     switch( mLayoutEngine.GetHorizontalAlignment() )
1672     {
1673       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1674       {
1675         cursorInfo.primaryPosition.x = 0.f;
1676         break;
1677       }
1678       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1679       {
1680         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
1681         break;
1682       }
1683       case LayoutEngine::HORIZONTAL_ALIGN_END:
1684       {
1685         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1686         break;
1687       }
1688     }
1689
1690     switch( mLayoutEngine.GetVerticalAlignment() )
1691     {
1692       case LayoutEngine::VERTICAL_ALIGN_TOP:
1693       {
1694         cursorInfo.primaryPosition.y = 0.f;
1695         break;
1696       }
1697       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1698       {
1699         cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
1700         break;
1701       }
1702       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1703       {
1704         cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
1705         break;
1706       }
1707     }
1708
1709     // Nothing else to do.
1710     return;
1711   }
1712
1713   // Check if the logical position is the first or the last one of the text.
1714   const bool isFirstPosition = 0u == logical;
1715   const bool isLastPosition = numberOfCharacters == logical;
1716
1717   // 'logical' is the logical 'cursor' index.
1718   // Get the next and current logical 'character' index.
1719   const CharacterIndex nextCharacterIndex = logical;
1720   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1721
1722   // Get the direction of the character and the next one.
1723   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1724
1725   CharacterDirection isCurrentRightToLeft = false;
1726   CharacterDirection isNextRightToLeft = false;
1727   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1728   {
1729     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1730     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1731   }
1732
1733   // Get the line where the character is laid-out.
1734   const LineRun* const modelLines = mVisualModel->mLines.Begin();
1735
1736   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1737   const LineRun& line = *( modelLines + lineIndex );
1738
1739   // Get the paragraph's direction.
1740   const CharacterDirection isRightToLeftParagraph = line.direction;
1741
1742   // Check whether there is an alternative position:
1743
1744   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1745                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1746
1747   // Set the line offset and height.
1748   cursorInfo.lineOffset = 0.f;
1749   cursorInfo.lineHeight = line.ascender + -line.descender;
1750
1751   // Calculate the primary cursor.
1752
1753   CharacterIndex index = characterIndex;
1754   if( cursorInfo.isSecondaryCursor )
1755   {
1756     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1757
1758     if( isLastPosition )
1759     {
1760       // The position of the cursor after the last character needs special
1761       // care depending on its direction and the direction of the paragraph.
1762
1763       // Need to find the first character after the last character with the paragraph's direction.
1764       // i.e l0 l1 l2 r0 r1 should find r0.
1765
1766       // TODO: check for more than one line!
1767       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1768       index = mLogicalModel->GetLogicalCharacterIndex( index );
1769     }
1770     else
1771     {
1772       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1773     }
1774   }
1775
1776   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1777   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1778   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1779   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1780   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
1781
1782   // Convert the cursor position into the glyph position.
1783   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
1784   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1785   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
1786
1787   // Get the metrics for the group of glyphs.
1788   GlyphMetrics glyphMetrics;
1789   GetGlyphsMetrics( primaryGlyphIndex,
1790                     primaryNumberOfGlyphs,
1791                     glyphMetrics,
1792                     mVisualModel,
1793                     mMetrics );
1794
1795   // Whether to add the glyph's advance to the cursor position.
1796   // 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,
1797   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
1798   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
1799   //
1800   // FLCP A
1801   // ------
1802   // 0000 1
1803   // 0001 1
1804   // 0010 0
1805   // 0011 0
1806   // 0100 1
1807   // 0101 0
1808   // 0110 1
1809   // 0111 0
1810   // 1000 0
1811   // 1001 x
1812   // 1010 x
1813   // 1011 1
1814   // 1100 x
1815   // 1101 x
1816   // 1110 x
1817   // 1111 x
1818   //
1819   // Where F -> isFirstPosition
1820   //       L -> isLastPosition
1821   //       C -> isCurrentRightToLeft
1822   //       P -> isRightToLeftParagraph
1823   //       A -> Whether to add the glyph's advance.
1824
1825   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
1826                                  ( isFirstPosition && isRightToLeftParagraph ) ||
1827                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
1828
1829   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
1830
1831   if( !isLastPosition &&
1832       ( primaryNumberOfCharacters > 1u ) )
1833   {
1834     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
1835
1836     bool isCurrentRightToLeft = false;
1837     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1838     {
1839       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
1840     }
1841
1842     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
1843     if( isCurrentRightToLeft )
1844     {
1845       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
1846     }
1847
1848     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1849   }
1850
1851   // Get the glyph position and x bearing.
1852   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
1853
1854   // Set the primary cursor's height.
1855   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1856
1857   // Set the primary cursor's position.
1858   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
1859   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1860
1861   // Calculate the secondary cursor.
1862
1863   if( cursorInfo.isSecondaryCursor )
1864   {
1865     // Set the secondary cursor's height.
1866     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1867
1868     CharacterIndex index = characterIndex;
1869     if( !isLastPosition )
1870     {
1871       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1872     }
1873
1874     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
1875     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1876
1877     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
1878
1879     GetGlyphsMetrics( secondaryGlyphIndex,
1880                       secondaryNumberOfGlyphs,
1881                       glyphMetrics,
1882                       mVisualModel,
1883                       mMetrics );
1884
1885     // Set the secondary cursor's position.
1886     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1887     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1888   }
1889 }
1890
1891 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1892 {
1893   if( NULL == mEventData )
1894   {
1895     // Nothing to do if there is no text input.
1896     return 0u;
1897   }
1898
1899   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1900
1901   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1902   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1903
1904   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1905   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1906
1907   if( numberOfCharacters > 1u )
1908   {
1909     const Script script = mLogicalModel->GetScript( index );
1910     if( HasLigatureMustBreak( script ) )
1911     {
1912       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
1913       numberOfCharacters = 1u;
1914     }
1915   }
1916   else
1917   {
1918     while( 0u == numberOfCharacters )
1919     {
1920       ++glyphIndex;
1921       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1922     }
1923   }
1924
1925   if( index < mEventData->mPrimaryCursorPosition )
1926   {
1927     cursorIndex -= numberOfCharacters;
1928   }
1929   else
1930   {
1931     cursorIndex += numberOfCharacters;
1932   }
1933
1934   return cursorIndex;
1935 }
1936
1937 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1938 {
1939   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1940   if( NULL == mEventData )
1941   {
1942     // Nothing to do if there is no text input.
1943     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1944     return;
1945   }
1946
1947   const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
1948   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1949
1950   // Sets the cursor position.
1951   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1952                                        cursorPosition.x,
1953                                        cursorPosition.y,
1954                                        cursorInfo.primaryCursorHeight,
1955                                        cursorInfo.lineHeight );
1956   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1957
1958   // Sets the grab handle position.
1959   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1960                                        cursorPosition.x,
1961                                        cursorInfo.lineOffset + offset.y,
1962                                        cursorInfo.lineHeight );
1963
1964   if( cursorInfo.isSecondaryCursor )
1965   {
1966     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1967                                          cursorInfo.secondaryPosition.x + offset.x,
1968                                          cursorInfo.secondaryPosition.y + offset.y,
1969                                          cursorInfo.secondaryCursorHeight,
1970                                          cursorInfo.lineHeight );
1971     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1972   }
1973
1974   // Set which cursors are active according the state.
1975   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1976   {
1977     if( cursorInfo.isSecondaryCursor )
1978     {
1979       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1980     }
1981     else
1982     {
1983       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1984     }
1985   }
1986   else
1987   {
1988     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1989   }
1990
1991   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1992 }
1993
1994 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
1995                                               const CursorInfo& cursorInfo )
1996 {
1997   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1998       ( RIGHT_SELECTION_HANDLE != handleType ) )
1999   {
2000     return;
2001   }
2002
2003   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
2004   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2005
2006   // Sets the handle's position.
2007   mEventData->mDecorator->SetPosition( handleType,
2008                                        cursorPosition.x,
2009                                        cursorInfo.lineOffset + offset.y,
2010                                        cursorInfo.lineHeight );
2011
2012   // If selection handle at start of the text and other at end of the text then all text is selected.
2013   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2014   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2015   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2016 }
2017
2018 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2019 {
2020   // Clamp between -space & 0 (and the text alignment).
2021
2022   if( actualSize.width > mVisualModel->mControlSize.width )
2023   {
2024     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
2025     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
2026     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
2027
2028     mEventData->mDecoratorUpdated = true;
2029   }
2030   else
2031   {
2032     mEventData->mScrollPosition.x = 0.f;
2033   }
2034 }
2035
2036 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2037 {
2038   // Clamp between -space & 0 (and the text alignment).
2039   if( actualSize.height > mVisualModel->mControlSize.height )
2040   {
2041     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
2042     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
2043     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
2044
2045     mEventData->mDecoratorUpdated = true;
2046   }
2047   else
2048   {
2049     mEventData->mScrollPosition.y = 0.f;
2050   }
2051 }
2052
2053 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2054 {
2055   // position is in actor's coords.
2056   const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
2057
2058   // Transform the position to decorator coords.
2059   const float offset = mEventData->mScrollPosition.x + mAlignmentOffset.x;
2060   const float decoratorPositionBegin = position.x + offset;
2061   const float decoratorPositionEnd = positionEnd + offset;
2062
2063   if( decoratorPositionBegin < 0.f )
2064   {
2065     mEventData->mScrollPosition.x = -position.x - mAlignmentOffset.x;
2066   }
2067   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2068   {
2069     mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - mAlignmentOffset.x;
2070   }
2071 }
2072
2073 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2074 {
2075   // Get the current cursor position in decorator coords.
2076   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2077
2078   // Calculate the offset to match the cursor position before the character was deleted.
2079   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
2080
2081   ClampHorizontalScroll( mVisualModel->GetActualSize() );
2082 }
2083
2084 void Controller::Impl::RequestRelayout()
2085 {
2086   mControlInterface.RequestTextRelayout();
2087 }
2088
2089 } // namespace Text
2090
2091 } // namespace Toolkit
2092
2093 } // namespace Dali