TextSelectionPopup follows Handles and button change with state and other improvements
[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 {}
141
142 EventData::~EventData()
143 {}
144
145 bool Controller::Impl::ProcessInputEvents()
146 {
147   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
148   if( NULL == mEventData )
149   {
150     // Nothing to do if there is no text input.
151     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
152     return false;
153   }
154
155   if( mEventData->mDecorator )
156   {
157     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
158          iter != mEventData->mEventQueue.end();
159          ++iter )
160     {
161       switch( iter->type )
162       {
163       case Event::CURSOR_KEY_EVENT:
164       {
165         OnCursorKeyEvent( *iter );
166         break;
167       }
168       case Event::TAP_EVENT:
169       {
170         OnTapEvent( *iter );
171         break;
172       }
173       case Event::PAN_EVENT:
174       {
175         OnPanEvent( *iter );
176         break;
177       }
178       case Event::GRAB_HANDLE_EVENT:
179       case Event::LEFT_SELECTION_HANDLE_EVENT:
180       case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
181       {
182         OnHandleEvent( *iter );
183         break;
184       }
185       }
186     }
187   }
188
189   // The cursor must also be repositioned after inserts into the model
190   if( mEventData->mUpdateCursorPosition )
191   {
192     // Updates the cursor position and scrolls the text to make it visible.
193
194     UpdateCursorPosition();
195
196     if( mEventData->mScrollAfterUpdatePosition )
197     {
198       const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
199
200       ScrollToMakePositionVisible( primaryCursorPosition );
201       mEventData->mScrollAfterUpdatePosition = false;
202     }
203
204     mEventData->mDecoratorUpdated = true;
205     mEventData->mUpdateCursorPosition = false;
206   }
207   else if( mEventData->mScrollAfterDelete )
208   {
209     ScrollTextToMatchCursor();
210     mEventData->mDecoratorUpdated = true;
211     mEventData->mScrollAfterDelete = false;
212   }
213   else
214   {
215     bool leftScroll = false;
216     bool rightScroll = false;
217
218     if( mEventData->mUpdateLeftSelectionPosition )
219     {
220       UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
221
222       if( mEventData->mScrollAfterUpdatePosition )
223       {
224         const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
225
226         ScrollToMakePositionVisible( leftHandlePosition );
227         leftScroll = true;
228       }
229
230       mEventData->mDecoratorUpdated = true;
231       mEventData->mUpdateLeftSelectionPosition = false;
232     }
233
234     if( mEventData->mUpdateRightSelectionPosition )
235     {
236       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
237
238       if( mEventData->mScrollAfterUpdatePosition )
239       {
240         const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
241
242         ScrollToMakePositionVisible( rightHandlePosition );
243         rightScroll = true;
244       }
245
246       mEventData->mDecoratorUpdated = true;
247       mEventData->mUpdateRightSelectionPosition = false;
248     }
249
250     if( leftScroll || rightScroll )
251     {
252       mEventData->mScrollAfterUpdatePosition = false;
253     }
254   }
255
256   mEventData->mEventQueue.clear();
257
258   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
259
260   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
261   mEventData->mDecoratorUpdated = false;
262
263   return decoratorUpdated;
264 }
265
266 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
267 {
268   // Calculate the operations to be done.
269   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
270
271   Vector<Character>& utf32Characters = mLogicalModel->mText;
272
273   const Length numberOfCharacters = utf32Characters.Count();
274
275   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
276   if( GET_LINE_BREAKS & operations )
277   {
278     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
279     // calculate the bidirectional info for each 'paragraph'.
280     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
281     // is not shaped together).
282     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
283
284     SetLineBreakInfo( utf32Characters,
285                       lineBreakInfo );
286   }
287
288   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
289   if( GET_WORD_BREAKS & operations )
290   {
291     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
292     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
293
294     SetWordBreakInfo( utf32Characters,
295                       wordBreakInfo );
296   }
297
298   const bool getScripts = GET_SCRIPTS & operations;
299   const bool validateFonts = VALIDATE_FONTS & operations;
300
301   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
302   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
303
304   if( getScripts || validateFonts )
305   {
306     // Validates the fonts assigned by the application or assigns default ones.
307     // It makes sure all the characters are going to be rendered by the correct font.
308     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
309
310     if( getScripts )
311     {
312       // Retrieves the scripts used in the text.
313       multilanguageSupport.SetScripts( utf32Characters,
314                                        lineBreakInfo,
315                                        scripts );
316     }
317
318     if( validateFonts )
319     {
320       if( 0u == validFonts.Count() )
321       {
322         // Copy the requested font defaults received via the property system.
323         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
324         GetDefaultFonts( validFonts, numberOfCharacters );
325       }
326
327       // Validates the fonts. If there is a character with no assigned font it sets a default one.
328       // After this call, fonts are validated.
329       multilanguageSupport.ValidateFonts( utf32Characters,
330                                           scripts,
331                                           validFonts );
332     }
333   }
334
335   Vector<Character> mirroredUtf32Characters;
336   bool textMirrored = false;
337   if( BIDI_INFO & operations )
338   {
339     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
340     // bidirectional info.
341
342     Length numberOfParagraphs = 0u;
343
344     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
345     for( Length index = 0u; index < numberOfCharacters; ++index )
346     {
347       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
348       {
349         ++numberOfParagraphs;
350       }
351     }
352
353     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
354     bidirectionalInfo.Reserve( numberOfParagraphs );
355
356     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
357     SetBidirectionalInfo( utf32Characters,
358                           scripts,
359                           lineBreakInfo,
360                           bidirectionalInfo );
361
362     if( 0u != bidirectionalInfo.Count() )
363     {
364       // This paragraph has right to left text. Some characters may need to be mirrored.
365       // TODO: consider if the mirrored string can be stored as well.
366
367       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
368
369       // Only set the character directions if there is right to left characters.
370       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
371       directions.Resize( numberOfCharacters );
372
373       GetCharactersDirection( bidirectionalInfo,
374                               directions );
375     }
376     else
377     {
378       // There is no right to left characters. Clear the directions vector.
379       mLogicalModel->mCharacterDirections.Clear();
380     }
381
382    }
383
384   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
385   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
386   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
387   if( SHAPE_TEXT & operations )
388   {
389     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
390     // Shapes the text.
391     ShapeText( textToShape,
392                lineBreakInfo,
393                scripts,
394                validFonts,
395                glyphs,
396                glyphsToCharactersMap,
397                charactersPerGlyph );
398
399     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
400     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
401     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
402   }
403
404   const Length numberOfGlyphs = glyphs.Count();
405
406   if( GET_GLYPH_METRICS & operations )
407   {
408     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
409   }
410 }
411
412 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
413 {
414   if( mFontDefaults )
415   {
416     FontRun fontRun;
417     fontRun.characterRun.characterIndex = 0;
418     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
419     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
420     fontRun.isDefault = true;
421
422     fonts.PushBack( fontRun );
423   }
424 }
425
426 void Controller::Impl::OnCursorKeyEvent( const Event& event )
427 {
428   if( NULL == mEventData )
429   {
430     // Nothing to do if there is no text input.
431     return;
432   }
433
434   int keyCode = event.p1.mInt;
435
436   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
437   {
438     if( mEventData->mPrimaryCursorPosition > 0u )
439     {
440       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
441     }
442   }
443   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
444   {
445     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
446     {
447       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
448     }
449   }
450   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
451   {
452     // TODO
453   }
454   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
455   {
456     // TODO
457   }
458
459   mEventData->mUpdateCursorPosition = true;
460   mEventData->mScrollAfterUpdatePosition = true;
461 }
462
463 void Controller::Impl::OnTapEvent( const Event& event )
464 {
465   if( NULL != mEventData )
466   {
467     const unsigned int tapCount = event.p1.mUint;
468
469     if( 1u == tapCount )
470     {
471       if( ! IsShowingPlaceholderText() )
472       {
473         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
474         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
475
476         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
477                                                                     yPosition );
478       }
479       else
480       {
481         mEventData->mPrimaryCursorPosition = 0u;
482       }
483
484       mEventData->mUpdateCursorPosition = true;
485       mEventData->mScrollAfterUpdatePosition = true;
486     }
487     else if( mEventData->mSelectionEnabled &&
488              ( 2u == tapCount ) )
489     {
490       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
491       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
492       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
493
494       RepositionSelectionHandles( xPosition,
495                                   yPosition );
496
497       mEventData->mScrollAfterUpdatePosition = true;
498       mEventData->mUpdateLeftSelectionPosition = true;
499       mEventData->mUpdateRightSelectionPosition = true;
500     }
501   }
502 }
503
504 void Controller::Impl::OnPanEvent( const Event& event )
505 {
506   if( NULL == mEventData )
507   {
508     // Nothing to do if there is no text input.
509     return;
510   }
511
512   int state = event.p1.mInt;
513
514   if( Gesture::Started    == state ||
515       Gesture::Continuing == state )
516   {
517     const Vector2& actualSize = mVisualModel->GetActualSize();
518     const Vector2 currentScroll = mEventData->mScrollPosition;
519
520     if( mEventData->mHorizontalScrollingEnabled )
521     {
522       const float displacementX = event.p2.mFloat;
523       mEventData->mScrollPosition.x += displacementX;
524
525       ClampHorizontalScroll( actualSize );
526     }
527
528     if( mEventData->mVerticalScrollingEnabled )
529     {
530       const float displacementY = event.p3.mFloat;
531       mEventData->mScrollPosition.y += displacementY;
532
533       ClampVerticalScroll( actualSize );
534     }
535
536     if( mEventData->mDecorator )
537     {
538       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
539     }
540   }
541 }
542
543 void Controller::Impl::OnHandleEvent( const Event& event )
544 {
545   if( NULL == mEventData )
546   {
547     // Nothing to do if there is no text input.
548     return;
549   }
550
551   const unsigned int state = event.p1.mUint;
552   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
553
554   if( HANDLE_PRESSED == state )
555   {
556     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
557     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
558     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
559
560     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
561
562     if( Event::GRAB_HANDLE_EVENT == event.type )
563     {
564       ChangeState ( EventData::GRAB_HANDLE_PANNING );
565
566       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
567       {
568         mEventData->mPrimaryCursorPosition = handleNewPosition;
569         mEventData->mUpdateCursorPosition = true;
570       }
571     }
572     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
573     {
574       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
575
576       if( handleNewPosition != mEventData->mLeftSelectionPosition )
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       {
592         mEventData->mRightSelectionPosition = handleNewPosition;
593
594         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
595                                     mEventData->mRightSelectionPosition );
596
597         mEventData->mUpdateRightSelectionPosition = true;
598       }
599     }
600   } // end ( HANDLE_PRESSED == state )
601   else if( ( HANDLE_RELEASED == state ) ||
602            handleStopScrolling )
603   {
604     CharacterIndex handlePosition = 0u;
605     if( handleStopScrolling )
606     {
607       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
608       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
609       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
610
611       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
612     }
613
614     if( Event::GRAB_HANDLE_EVENT == event.type )
615     {
616       mEventData->mUpdateCursorPosition = true;
617
618       ChangeState( EventData::EDITING_WITH_POPUP );
619
620       if( handleStopScrolling )
621       {
622         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
623         mEventData->mPrimaryCursorPosition = handlePosition;
624       }
625     }
626     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
627     {
628       ChangeState( EventData::SELECTING );
629
630       if( handleStopScrolling )
631       {
632         mEventData->mUpdateLeftSelectionPosition = mEventData->mLeftSelectionPosition != handlePosition;
633         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
634         mEventData->mLeftSelectionPosition = handlePosition;
635
636         if( mEventData->mUpdateLeftSelectionPosition )
637         {
638           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
639                                       mEventData->mRightSelectionPosition );
640         }
641       }
642     }
643     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
644     {
645       ChangeState( EventData::SELECTING );
646
647       if( handleStopScrolling )
648       {
649         mEventData->mUpdateRightSelectionPosition = mEventData->mRightSelectionPosition != handlePosition;
650         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
651         mEventData->mRightSelectionPosition = handlePosition;
652
653         if( mEventData->mUpdateRightSelectionPosition )
654         {
655           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
656                                       mEventData->mRightSelectionPosition );
657         }
658       }
659     }
660
661     mEventData->mDecoratorUpdated = true;
662   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
663   else if( HANDLE_SCROLLING == state )
664   {
665     const float xSpeed = event.p2.mFloat;
666     const Vector2& actualSize = mVisualModel->GetActualSize();
667
668     mEventData->mScrollPosition.x += xSpeed;
669
670     ClampHorizontalScroll( actualSize );
671
672     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
673     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
674
675     if( Event::GRAB_HANDLE_EVENT == event.type )
676     {
677       ChangeState( EventData::GRAB_HANDLE_PANNING );
678     }
679     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
680     {
681       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
682       //       Think if something can be done to save power.
683
684       ChangeState( EventData::SELECTION_HANDLE_PANNING );
685
686       const Vector2& position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
687
688       // Get the new handle position.
689       // The selection handle's position is in decorator coords. Need to transforms to text coords.
690       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
691                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
692
693       if( leftSelectionHandleEvent )
694       {
695         mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
696         mEventData->mLeftSelectionPosition = handlePosition;
697       }
698       else
699       {
700         mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
701         mEventData->mRightSelectionPosition = handlePosition;
702       }
703
704       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
705       {
706         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
707                                     mEventData->mRightSelectionPosition );
708       }
709     }
710     mEventData->mDecoratorUpdated = true;
711   } // end ( HANDLE_SCROLLING == state )
712 }
713
714 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
715 {
716   if( selectionStart == selectionEnd )
717   {
718     // Nothing to select if handles are in the same place.
719     return;
720   }
721
722   mEventData->mDecorator->ClearHighlights();
723
724   mEventData->mLeftSelectionPosition = selectionStart;
725   mEventData->mRightSelectionPosition = selectionEnd;
726
727   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
728   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
729   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
730   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
731
732   // TODO: Better algorithm to create the highlight box.
733   // TODO: Multi-line.
734
735   const Vector<LineRun>& lines = mVisualModel->mLines;
736   const LineRun& firstLine = *lines.Begin();
737   const float height = firstLine.ascender + -firstLine.descender;
738
739   const bool indicesSwapped = ( selectionStart > selectionEnd );
740   if( indicesSwapped )
741   {
742     std::swap( selectionStart, selectionEnd );
743   }
744
745   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
746   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
747
748   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
749
750   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
751
752   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
753   {
754     const GlyphInfo& glyph = *( glyphsBuffer + index );
755     const Vector2& position = *( positionsBuffer + index );
756
757     const float xPosition = position.x - glyph.xBearing + offset.x;
758     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
759   }
760
761   CursorInfo primaryCursorInfo;
762   GetCursorPosition( mEventData->mLeftSelectionPosition,
763                      primaryCursorInfo );
764
765   CursorInfo secondaryCursorInfo;
766   GetCursorPosition( mEventData->mRightSelectionPosition,
767                      secondaryCursorInfo );
768
769   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
770   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
771
772   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
773
774   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
775
776   // Set the flag to update the decorator.
777   mEventData->mDecoratorUpdated = true;
778 }
779
780 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
781 {
782   if( NULL == mEventData )
783   {
784     // Nothing to do if there is no text input.
785     return;
786   }
787
788   if( IsShowingPlaceholderText() )
789   {
790     // Nothing to do if there is the place-holder text.
791     return;
792   }
793
794   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
795   const Length numberOfLines  = mVisualModel->mLines.Count();
796   if( 0 == numberOfGlyphs ||
797       0 == numberOfLines )
798   {
799     // Nothing to do if there is no text.
800     return;
801   }
802
803   // Find which word was selected
804   CharacterIndex selectionStart( 0 );
805   CharacterIndex selectionEnd( 0 );
806   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
807   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
808
809   if( selectionStart == selectionEnd )
810   {
811     ChangeState( EventData::EDITING );
812     // Nothing to select. i.e. a white space, out of bounds
813     return;
814   }
815
816   RepositionSelectionHandles( selectionStart, selectionEnd );
817 }
818
819 void Controller::Impl::ChangeState( EventData::State newState )
820 {
821   if( NULL == mEventData )
822   {
823     // Nothing to do if there is no text input.
824     return;
825   }
826
827   if( mEventData->mState != newState )
828   {
829     mEventData->mState = newState;
830
831     if( EventData::INACTIVE == mEventData->mState )
832     {
833       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
834       mEventData->mDecorator->StopCursorBlink();
835       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
836       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
837       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
838       mEventData->mDecorator->SetPopupActive( false );
839       mEventData->mDecoratorUpdated = true;
840     }
841     else if ( EventData::SELECTING == mEventData->mState )
842     {
843       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
844       mEventData->mDecorator->StopCursorBlink();
845       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
846       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
847       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
848       if( mEventData->mGrabHandlePopupEnabled )
849       {
850         TextSelectionPopup::Buttons selectedButtons = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
851         mEventData->mDecorator->SetEnabledPopupButtons( selectedButtons );
852         mEventData->mDecorator->SetPopupActive( true );
853       }
854       mEventData->mDecoratorUpdated = true;
855     }
856     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
857     {
858       if( mEventData->mGrabHandlePopupEnabled )
859       {
860         TextSelectionPopup::Buttons selectedButtons = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
861         mEventData->mDecorator->SetEnabledPopupButtons( selectedButtons );
862         mEventData->mDecorator->SetPopupActive( true );
863       }
864       mEventData->mDecoratorUpdated = true;
865     }
866     else if( EventData::EDITING == mEventData->mState )
867     {
868       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
869       if( mEventData->mCursorBlinkEnabled )
870       {
871         mEventData->mDecorator->StartCursorBlink();
872       }
873       // Grab handle is not shown until a tap is received whilst EDITING
874       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
875       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
876       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
877       if( mEventData->mGrabHandlePopupEnabled )
878       {
879         mEventData->mDecorator->SetPopupActive( false );
880       }
881       mEventData->mDecoratorUpdated = true;
882     }
883     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
884     {
885       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
886       if( mEventData->mCursorBlinkEnabled )
887       {
888         mEventData->mDecorator->StartCursorBlink();
889       }
890       if( mEventData->mSelectionEnabled )
891       {
892         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
893         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
894       }
895       else
896       {
897         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
898       }
899       if( mEventData->mGrabHandlePopupEnabled )
900       {
901         TextSelectionPopup::Buttons selectionButtons = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
902         mEventData->mDecorator->SetEnabledPopupButtons( selectionButtons );
903         mEventData->mDecorator->SetPopupActive( true );
904       }
905       mEventData->mDecoratorUpdated = true;
906     }
907     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
908     {
909       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
910       mEventData->mDecorator->StopCursorBlink();
911       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
912       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
913       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
914       if( mEventData->mGrabHandlePopupEnabled )
915       {
916         mEventData->mDecorator->SetPopupActive( false );
917       }
918       mEventData->mDecoratorUpdated = true;
919     }
920     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
921     {
922       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
923       if( mEventData->mCursorBlinkEnabled )
924       {
925         mEventData->mDecorator->StartCursorBlink();
926       }
927       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
928       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
929       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
930       if( mEventData->mGrabHandlePopupEnabled )
931       {
932         mEventData->mDecorator->SetPopupActive( false );
933       }
934       mEventData->mDecoratorUpdated = true;
935     }
936   }
937 }
938
939 LineIndex Controller::Impl::GetClosestLine( float y ) const
940 {
941   float totalHeight = 0.f;
942   LineIndex lineIndex = 0u;
943
944   const Vector<LineRun>& lines = mVisualModel->mLines;
945   for( LineIndex endLine = lines.Count();
946        lineIndex < endLine;
947        ++lineIndex )
948   {
949     const LineRun& lineRun = lines[lineIndex];
950     totalHeight += lineRun.ascender + -lineRun.descender;
951     if( y < totalHeight )
952     {
953       return lineIndex;
954     }
955   }
956
957   if( lineIndex == 0 )
958   {
959     return 0;
960   }
961
962   return lineIndex-1;
963 }
964
965 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
966 {
967   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
968   if( hitCharacter >= mLogicalModel->mText.Count() )
969   {
970     // Selection out of bounds.
971     return;
972   }
973
974   startIndex = hitCharacter;
975   endIndex = hitCharacter;
976
977   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
978   {
979     // Find the start and end of the text
980     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
981     {
982       Character charCode = mLogicalModel->mText[ startIndex-1 ];
983       if( TextAbstraction::IsWhiteSpace( charCode ) )
984       {
985         break;
986       }
987     }
988     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
989     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
990     {
991       Character charCode = mLogicalModel->mText[ endIndex ];
992       if( TextAbstraction::IsWhiteSpace( charCode ) )
993       {
994         break;
995       }
996     }
997   }
998 }
999
1000 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1001                                                         float visualY )
1002 {
1003   if( NULL == mEventData )
1004   {
1005     // Nothing to do if there is no text input.
1006     return 0u;
1007   }
1008
1009   CharacterIndex logicalIndex = 0u;
1010
1011   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1012   const Length numberOfLines  = mVisualModel->mLines.Count();
1013   if( 0 == numberOfGlyphs ||
1014       0 == numberOfLines )
1015   {
1016     return logicalIndex;
1017   }
1018
1019   // Find which line is closest
1020   const LineIndex lineIndex = GetClosestLine( visualY );
1021   const LineRun& line = mVisualModel->mLines[lineIndex];
1022
1023   // Get the positions of the glyphs.
1024   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1025   const Vector2* const positionsBuffer = positions.Begin();
1026
1027   // Get the visual to logical conversion tables.
1028   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1029   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1030
1031   // Get the character to glyph conversion table.
1032   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1033
1034   // Get the glyphs per character table.
1035   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1036
1037   // If the vector is void, there is no right to left characters.
1038   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1039
1040   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1041   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1042   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1043
1044   // Whether there is a hit on a glyph.
1045   bool matched = false;
1046
1047   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1048   CharacterIndex visualIndex = startCharacter;
1049   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1050   {
1051     // The character in logical order.
1052     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1053
1054     // The first glyph for that character in logical order.
1055     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1056
1057     // The number of glyphs for that character
1058     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1059
1060     // Get the metrics for the group of glyphs.
1061     GlyphMetrics glyphMetrics;
1062     GetGlyphsMetrics( glyphLogicalOrderIndex,
1063                       numberOfGlyphs,
1064                       glyphMetrics,
1065                       mVisualModel,
1066                       mFontClient );
1067
1068     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1069
1070     // Find the mid-point of the area containing the glyph
1071     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1072
1073     if( visualX < glyphCenter )
1074     {
1075       matched = true;
1076       break;
1077     }
1078   }
1079
1080   // Return the logical position of the cursor in characters.
1081
1082   if( !matched )
1083   {
1084     visualIndex = endCharacter;
1085   }
1086
1087   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1088   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1089   return logicalIndex;
1090 }
1091
1092 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1093                                           CursorInfo& cursorInfo )
1094 {
1095   // TODO: Check for multiline with \n, etc...
1096
1097   // Check if the logical position is the first or the last one of the text.
1098   const bool isFirstPosition = 0u == logical;
1099   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
1100
1101   if( isFirstPosition && isLastPosition )
1102   {
1103     // There is zero characters. Get the default font.
1104
1105     FontId defaultFontId = 0u;
1106     if( NULL == mFontDefaults )
1107     {
1108       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1109                                              EMPTY_STRING );
1110     }
1111     else
1112     {
1113       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1114     }
1115
1116     Text::FontMetrics fontMetrics;
1117     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1118
1119     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
1120     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1121
1122     cursorInfo.primaryPosition.x = 0.f;
1123     cursorInfo.primaryPosition.y = 0.f;
1124
1125     // Nothing else to do.
1126     return;
1127   }
1128
1129   // Get the previous logical index.
1130   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1131
1132   // Decrease the logical index if it's the last one.
1133   if( isLastPosition )
1134   {
1135     --logical;
1136   }
1137
1138   // Get the direction of the character and the previous one.
1139   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1140
1141   CharacterDirection isCurrentRightToLeft = false;
1142   CharacterDirection isPreviousRightToLeft = false;
1143   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1144   {
1145     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1146     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1147   }
1148
1149   // Get the line where the character is laid-out.
1150   const LineRun* modelLines = mVisualModel->mLines.Begin();
1151
1152   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1153   const LineRun& line = *( modelLines + lineIndex );
1154
1155   // Get the paragraph's direction.
1156   const CharacterDirection isRightToLeftParagraph = line.direction;
1157
1158   // Check whether there is an alternative position:
1159
1160   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1161     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1162
1163   // Set the line height.
1164   cursorInfo.lineHeight = line.ascender + -line.descender;
1165
1166   // Convert the cursor position into the glyph position.
1167   CharacterIndex characterIndex = logical;
1168   if( cursorInfo.isSecondaryCursor &&
1169       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1170   {
1171     characterIndex = previousLogical;
1172   }
1173
1174   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1175   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1176   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1177
1178   // Get the metrics for the group of glyphs.
1179   GlyphMetrics glyphMetrics;
1180   GetGlyphsMetrics( currentGlyphIndex,
1181                     numberOfGlyphs,
1182                     glyphMetrics,
1183                     mVisualModel,
1184                     mFontClient );
1185
1186   float interGlyphAdvance = 0.f;
1187   if( !isLastPosition &&
1188       ( numberOfCharacters > 1u ) )
1189   {
1190     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1191     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1192   }
1193
1194   // Get the glyph position and x bearing.
1195   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1196
1197   // Set the cursor's height.
1198   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1199
1200   // Set the position.
1201   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1202   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1203
1204   if( isLastPosition )
1205   {
1206     // The position of the cursor after the last character needs special
1207     // care depending on its direction and the direction of the paragraph.
1208
1209     if( cursorInfo.isSecondaryCursor )
1210     {
1211       // Need to find the first character after the last character with the paragraph's direction.
1212       // i.e l0 l1 l2 r0 r1 should find r0.
1213
1214       // TODO: check for more than one line!
1215       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1216       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1217
1218       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1219       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1220
1221       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1222
1223       // Get the metrics for the group of glyphs.
1224       GlyphMetrics glyphMetrics;
1225       GetGlyphsMetrics( glyphIndex,
1226                         numberOfGlyphs,
1227                         glyphMetrics,
1228                         mVisualModel,
1229                         mFontClient );
1230
1231       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1232
1233       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1234     }
1235     else
1236     {
1237       if( !isCurrentRightToLeft )
1238       {
1239         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1240       }
1241       else
1242       {
1243         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1244       }
1245     }
1246   }
1247
1248   // Set the alternative cursor position.
1249   if( cursorInfo.isSecondaryCursor )
1250   {
1251     // Convert the cursor position into the glyph position.
1252     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1253     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1254     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1255
1256     // Get the glyph position.
1257     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1258
1259     // Get the metrics for the group of glyphs.
1260     GlyphMetrics glyphMetrics;
1261     GetGlyphsMetrics( previousGlyphIndex,
1262                       numberOfGlyphs,
1263                       glyphMetrics,
1264                       mVisualModel,
1265                       mFontClient );
1266
1267     // Set the cursor position and height.
1268     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1269                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1270
1271     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1272
1273     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1274
1275     // Update the primary cursor height as well.
1276     cursorInfo.primaryCursorHeight *= 0.5f;
1277   }
1278 }
1279
1280 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1281 {
1282   if( NULL == mEventData )
1283   {
1284     // Nothing to do if there is no text input.
1285     return 0u;
1286   }
1287
1288   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1289
1290   const Script script = mLogicalModel->GetScript( index );
1291   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1292   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1293
1294   Length numberOfCharacters = 0u;
1295   if( TextAbstraction::LATIN == script )
1296   {
1297     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1298     numberOfCharacters = 1u;
1299   }
1300   else
1301   {
1302     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1303     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1304
1305     while( 0u == numberOfCharacters )
1306     {
1307       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1308       ++glyphIndex;
1309     }
1310   }
1311
1312   if( index < mEventData->mPrimaryCursorPosition )
1313   {
1314     cursorIndex -= numberOfCharacters;
1315   }
1316   else
1317   {
1318     cursorIndex += numberOfCharacters;
1319   }
1320
1321   return cursorIndex;
1322 }
1323
1324 void Controller::Impl::UpdateCursorPosition()
1325 {
1326   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1327   if( NULL == mEventData )
1328   {
1329     // Nothing to do if there is no text input.
1330     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1331     return;
1332   }
1333
1334   if( IsShowingPlaceholderText() )
1335   {
1336     // Do not want to use the place-holder text to set the cursor position.
1337
1338     // Use the line's height of the font's family set to set the cursor's size.
1339     // If there is no font's family set, use the default font.
1340     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1341
1342     float lineHeight = 0.f;
1343
1344     FontId defaultFontId = 0u;
1345     if( NULL == mFontDefaults )
1346     {
1347       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1348                                              EMPTY_STRING );
1349     }
1350     else
1351     {
1352       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1353     }
1354
1355     Text::FontMetrics fontMetrics;
1356     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1357
1358     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1359
1360
1361     Vector2 cursorPosition;
1362
1363     switch( mLayoutEngine.GetHorizontalAlignment() )
1364     {
1365       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1366       {
1367         cursorPosition.x = 1.f;
1368         break;
1369       }
1370       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1371       {
1372         cursorPosition.x = floor( 0.5f * mControlSize.width );
1373         break;
1374       }
1375       case LayoutEngine::HORIZONTAL_ALIGN_END:
1376       {
1377         cursorPosition.x = mControlSize.width;
1378         break;
1379       }
1380     }
1381
1382     switch( mLayoutEngine.GetVerticalAlignment() )
1383     {
1384       case LayoutEngine::VERTICAL_ALIGN_TOP:
1385       {
1386         cursorPosition.y = 0.f;
1387         break;
1388       }
1389       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1390       {
1391         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1392         break;
1393       }
1394       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1395       {
1396         cursorPosition.y = mControlSize.height - lineHeight;
1397         break;
1398       }
1399     }
1400
1401     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1402                                          cursorPosition.x,
1403                                          cursorPosition.y,
1404                                          lineHeight,
1405                                          lineHeight );
1406   }
1407   else
1408   {
1409     CursorInfo cursorInfo;
1410     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1411                        cursorInfo );
1412
1413     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1414     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1415
1416     // Sets the cursor position.
1417     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1418                                          cursorPosition.x,
1419                                          cursorPosition.y,
1420                                          cursorInfo.primaryCursorHeight,
1421                                          cursorInfo.lineHeight );
1422     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1423
1424     // Sets the grab handle position.
1425     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1426                                          cursorPosition.x,
1427                                          cursorPosition.y,
1428                                          cursorInfo.lineHeight );
1429
1430     if( cursorInfo.isSecondaryCursor )
1431     {
1432       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1433       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1434                                            cursorInfo.secondaryPosition.x + offset.x,
1435                                            cursorInfo.secondaryPosition.y + offset.y,
1436                                            cursorInfo.secondaryCursorHeight,
1437                                            cursorInfo.lineHeight );
1438       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1439     }
1440     else
1441     {
1442       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1443     }
1444   }
1445   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1446 }
1447
1448 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1449 {
1450   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1451       ( RIGHT_SELECTION_HANDLE != handleType ) )
1452   {
1453     return;
1454   }
1455
1456   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1457   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1458
1459   CursorInfo cursorInfo;
1460   GetCursorPosition( index,
1461                      cursorInfo );
1462
1463   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1464   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1465
1466   // Sets the grab handle position.
1467   mEventData->mDecorator->SetPosition( handleType,
1468                                        cursorPosition.x,
1469                                        cursorPosition.y,
1470                                        cursorInfo.lineHeight );
1471 }
1472
1473 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1474 {
1475   // Clamp between -space & 0 (and the text alignment).
1476   if( actualSize.width > mControlSize.width )
1477   {
1478     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1479     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1480     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1481
1482     mEventData->mDecoratorUpdated = true;
1483   }
1484   else
1485   {
1486     mEventData->mScrollPosition.x = 0.f;
1487   }
1488 }
1489
1490 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1491 {
1492   // Clamp between -space & 0 (and the text alignment).
1493   if( actualSize.height > mControlSize.height )
1494   {
1495     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1496     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1497     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1498
1499     mEventData->mDecoratorUpdated = true;
1500   }
1501   else
1502   {
1503     mEventData->mScrollPosition.y = 0.f;
1504   }
1505 }
1506
1507 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1508 {
1509   Vector2 offset;
1510   bool updateDecorator = false;
1511   if( position.x < 0.f )
1512   {
1513     offset.x = -position.x;
1514     mEventData->mScrollPosition.x += offset.x;
1515     updateDecorator = true;
1516   }
1517   else if( position.x > mControlSize.width )
1518   {
1519     offset.x = mControlSize.width - position.x;
1520     mEventData->mScrollPosition.x += offset.x;
1521     updateDecorator = true;
1522   }
1523
1524   if( updateDecorator && mEventData->mDecorator )
1525   {
1526     mEventData->mDecorator->UpdatePositions( offset );
1527   }
1528
1529   // TODO : calculate the vertical scroll.
1530 }
1531
1532 void Controller::Impl::ScrollTextToMatchCursor()
1533 {
1534   // Get the current cursor position in decorator coords.
1535   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1536
1537   // Calculate the new cursor position.
1538   CursorInfo cursorInfo;
1539   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1540                      cursorInfo );
1541
1542   // Calculate the offset to match the cursor position before the character was deleted.
1543   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1544
1545   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1546   bool updateCursorPosition = true;
1547
1548   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1549   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1550
1551   if( updateCursorPosition )
1552   {
1553     // Sets the cursor position.
1554     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1555                                          cursorPosition.x,
1556                                          cursorPosition.y,
1557                                          cursorInfo.primaryCursorHeight,
1558                                          cursorInfo.lineHeight );
1559
1560     // Sets the grab handle position.
1561     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1562                                          cursorPosition.x,
1563                                          cursorPosition.y,
1564                                          cursorInfo.lineHeight );
1565
1566     if( cursorInfo.isSecondaryCursor )
1567     {
1568       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1569       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1570                                            cursorInfo.secondaryPosition.x + offset.x,
1571                                            cursorInfo.secondaryPosition.y + offset.y,
1572                                            cursorInfo.secondaryCursorHeight,
1573                                            cursorInfo.lineHeight );
1574       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1575     }
1576     else
1577     {
1578       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1579     }
1580   }
1581 }
1582
1583 void Controller::Impl::RequestRelayout()
1584 {
1585   mControlInterface.RequestTextRelayout();
1586 }
1587
1588 } // namespace Text
1589
1590 } // namespace Toolkit
1591
1592 } // namespace Dali