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