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