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