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