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