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