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