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