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