fb2bf2b28a8ab6a9c596929eadcde205d42ba1d8
[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::COPY );
851         mEventData->mDecorator->SetEnabledPopupButtons( selectedButtons );
852         mEventData->mDecorator->SetPopupActive( true );
853       }
854       mEventData->mDecoratorUpdated = true;
855     }
856     else if( EventData::EDITING == mEventData->mState )
857     {
858       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
859       if( mEventData->mCursorBlinkEnabled )
860       {
861         mEventData->mDecorator->StartCursorBlink();
862       }
863       // Grab handle is not shown until a tap is received whilst EDITING
864       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
865       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
866       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
867       if( mEventData->mGrabHandlePopupEnabled )
868       {
869         mEventData->mDecorator->SetPopupActive( false );
870       }
871       mEventData->mDecoratorUpdated = true;
872     }
873     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
874     {
875       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
876       if( mEventData->mCursorBlinkEnabled )
877       {
878         mEventData->mDecorator->StartCursorBlink();
879       }
880       if( mEventData->mSelectionEnabled )
881       {
882         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
883         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
884       }
885       else
886       {
887         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
888       }
889       if( mEventData->mGrabHandlePopupEnabled )
890       {
891         TextSelectionPopup::Buttons selectionButtons = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
892         mEventData->mDecorator->SetEnabledPopupButtons( selectionButtons );
893         mEventData->mDecorator->SetPopupActive( true );
894       }
895       mEventData->mDecoratorUpdated = true;
896     }
897     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
898     {
899       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
900       mEventData->mDecorator->StopCursorBlink();
901       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
902       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
903       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
904       if( mEventData->mGrabHandlePopupEnabled )
905       {
906         mEventData->mDecorator->SetPopupActive( false );
907       }
908       mEventData->mDecoratorUpdated = true;
909     }
910     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
911     {
912       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
913       if( mEventData->mCursorBlinkEnabled )
914       {
915         mEventData->mDecorator->StartCursorBlink();
916       }
917       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
918       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
919       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
920       if( mEventData->mGrabHandlePopupEnabled )
921       {
922         mEventData->mDecorator->SetPopupActive( false );
923       }
924       mEventData->mDecoratorUpdated = true;
925     }
926   }
927 }
928
929 LineIndex Controller::Impl::GetClosestLine( float y ) const
930 {
931   float totalHeight = 0.f;
932   LineIndex lineIndex = 0u;
933
934   const Vector<LineRun>& lines = mVisualModel->mLines;
935   for( LineIndex endLine = lines.Count();
936        lineIndex < endLine;
937        ++lineIndex )
938   {
939     const LineRun& lineRun = lines[lineIndex];
940     totalHeight += lineRun.ascender + -lineRun.descender;
941     if( y < totalHeight )
942     {
943       return lineIndex;
944     }
945   }
946
947   if( lineIndex == 0 )
948   {
949     return 0;
950   }
951
952   return lineIndex-1;
953 }
954
955 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
956 {
957   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
958   if( hitCharacter >= mLogicalModel->mText.Count() )
959   {
960     // Selection out of bounds.
961     return;
962   }
963
964   startIndex = hitCharacter;
965   endIndex = hitCharacter;
966
967   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
968   {
969     // Find the start and end of the text
970     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
971     {
972       Character charCode = mLogicalModel->mText[ startIndex-1 ];
973       if( TextAbstraction::IsWhiteSpace( charCode ) )
974       {
975         break;
976       }
977     }
978     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
979     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
980     {
981       Character charCode = mLogicalModel->mText[ endIndex ];
982       if( TextAbstraction::IsWhiteSpace( charCode ) )
983       {
984         break;
985       }
986     }
987   }
988 }
989
990 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
991                                                         float visualY )
992 {
993   if( NULL == mEventData )
994   {
995     // Nothing to do if there is no text input.
996     return 0u;
997   }
998
999   CharacterIndex logicalIndex = 0u;
1000
1001   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1002   const Length numberOfLines  = mVisualModel->mLines.Count();
1003   if( 0 == numberOfGlyphs ||
1004       0 == numberOfLines )
1005   {
1006     return logicalIndex;
1007   }
1008
1009   // Find which line is closest
1010   const LineIndex lineIndex = GetClosestLine( visualY );
1011   const LineRun& line = mVisualModel->mLines[lineIndex];
1012
1013   // Get the positions of the glyphs.
1014   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1015   const Vector2* const positionsBuffer = positions.Begin();
1016
1017   // Get the visual to logical conversion tables.
1018   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1019   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1020
1021   // Get the character to glyph conversion table.
1022   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1023
1024   // Get the glyphs per character table.
1025   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1026
1027   // If the vector is void, there is no right to left characters.
1028   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1029
1030   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1031   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1032   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1033
1034   // Whether there is a hit on a glyph.
1035   bool matched = false;
1036
1037   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1038   CharacterIndex visualIndex = startCharacter;
1039   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1040   {
1041     // The character in logical order.
1042     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1043
1044     // The first glyph for that character in logical order.
1045     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1046
1047     // The number of glyphs for that character
1048     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1049
1050     // Get the metrics for the group of glyphs.
1051     GlyphMetrics glyphMetrics;
1052     GetGlyphsMetrics( glyphLogicalOrderIndex,
1053                       numberOfGlyphs,
1054                       glyphMetrics,
1055                       mVisualModel,
1056                       mFontClient );
1057
1058     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1059
1060     // Find the mid-point of the area containing the glyph
1061     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1062
1063     if( visualX < glyphCenter )
1064     {
1065       matched = true;
1066       break;
1067     }
1068   }
1069
1070   // Return the logical position of the cursor in characters.
1071
1072   if( !matched )
1073   {
1074     visualIndex = endCharacter;
1075   }
1076
1077   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1078   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1079   return logicalIndex;
1080 }
1081
1082 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1083                                           CursorInfo& cursorInfo )
1084 {
1085   // TODO: Check for multiline with \n, etc...
1086
1087   // Check if the logical position is the first or the last one of the text.
1088   const bool isFirstPosition = 0u == logical;
1089   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
1090
1091   if( isFirstPosition && isLastPosition )
1092   {
1093     // There is zero characters. Get the default font.
1094
1095     FontId defaultFontId = 0u;
1096     if( NULL == mFontDefaults )
1097     {
1098       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1099                                              EMPTY_STRING );
1100     }
1101     else
1102     {
1103       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1104     }
1105
1106     Text::FontMetrics fontMetrics;
1107     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1108
1109     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
1110     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1111
1112     cursorInfo.primaryPosition.x = 0.f;
1113     cursorInfo.primaryPosition.y = 0.f;
1114
1115     // Nothing else to do.
1116     return;
1117   }
1118
1119   // Get the previous logical index.
1120   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1121
1122   // Decrease the logical index if it's the last one.
1123   if( isLastPosition )
1124   {
1125     --logical;
1126   }
1127
1128   // Get the direction of the character and the previous one.
1129   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1130
1131   CharacterDirection isCurrentRightToLeft = false;
1132   CharacterDirection isPreviousRightToLeft = false;
1133   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1134   {
1135     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1136     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1137   }
1138
1139   // Get the line where the character is laid-out.
1140   const LineRun* modelLines = mVisualModel->mLines.Begin();
1141
1142   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1143   const LineRun& line = *( modelLines + lineIndex );
1144
1145   // Get the paragraph's direction.
1146   const CharacterDirection isRightToLeftParagraph = line.direction;
1147
1148   // Check whether there is an alternative position:
1149
1150   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1151     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1152
1153   // Set the line height.
1154   cursorInfo.lineHeight = line.ascender + -line.descender;
1155
1156   // Convert the cursor position into the glyph position.
1157   CharacterIndex characterIndex = logical;
1158   if( cursorInfo.isSecondaryCursor &&
1159       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1160   {
1161     characterIndex = previousLogical;
1162   }
1163
1164   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1165   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1166   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1167
1168   // Get the metrics for the group of glyphs.
1169   GlyphMetrics glyphMetrics;
1170   GetGlyphsMetrics( currentGlyphIndex,
1171                     numberOfGlyphs,
1172                     glyphMetrics,
1173                     mVisualModel,
1174                     mFontClient );
1175
1176   float interGlyphAdvance = 0.f;
1177   if( !isLastPosition &&
1178       ( numberOfCharacters > 1u ) )
1179   {
1180     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1181     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1182   }
1183
1184   // Get the glyph position and x bearing.
1185   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1186
1187   // Set the cursor's height.
1188   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1189
1190   // Set the position.
1191   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1192   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1193
1194   if( isLastPosition )
1195   {
1196     // The position of the cursor after the last character needs special
1197     // care depending on its direction and the direction of the paragraph.
1198
1199     if( cursorInfo.isSecondaryCursor )
1200     {
1201       // Need to find the first character after the last character with the paragraph's direction.
1202       // i.e l0 l1 l2 r0 r1 should find r0.
1203
1204       // TODO: check for more than one line!
1205       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1206       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1207
1208       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1209       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1210
1211       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1212
1213       // Get the metrics for the group of glyphs.
1214       GlyphMetrics glyphMetrics;
1215       GetGlyphsMetrics( glyphIndex,
1216                         numberOfGlyphs,
1217                         glyphMetrics,
1218                         mVisualModel,
1219                         mFontClient );
1220
1221       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1222
1223       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1224     }
1225     else
1226     {
1227       if( !isCurrentRightToLeft )
1228       {
1229         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1230       }
1231       else
1232       {
1233         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1234       }
1235     }
1236   }
1237
1238   // Set the alternative cursor position.
1239   if( cursorInfo.isSecondaryCursor )
1240   {
1241     // Convert the cursor position into the glyph position.
1242     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1243     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1244     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1245
1246     // Get the glyph position.
1247     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1248
1249     // Get the metrics for the group of glyphs.
1250     GlyphMetrics glyphMetrics;
1251     GetGlyphsMetrics( previousGlyphIndex,
1252                       numberOfGlyphs,
1253                       glyphMetrics,
1254                       mVisualModel,
1255                       mFontClient );
1256
1257     // Set the cursor position and height.
1258     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1259                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1260
1261     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1262
1263     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1264
1265     // Update the primary cursor height as well.
1266     cursorInfo.primaryCursorHeight *= 0.5f;
1267   }
1268 }
1269
1270 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1271 {
1272   if( NULL == mEventData )
1273   {
1274     // Nothing to do if there is no text input.
1275     return 0u;
1276   }
1277
1278   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1279
1280   const Script script = mLogicalModel->GetScript( index );
1281   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1282   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1283
1284   Length numberOfCharacters = 0u;
1285   if( TextAbstraction::LATIN == script )
1286   {
1287     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1288     numberOfCharacters = 1u;
1289   }
1290   else
1291   {
1292     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1293     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1294
1295     while( 0u == numberOfCharacters )
1296     {
1297       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1298       ++glyphIndex;
1299     }
1300   }
1301
1302   if( index < mEventData->mPrimaryCursorPosition )
1303   {
1304     cursorIndex -= numberOfCharacters;
1305   }
1306   else
1307   {
1308     cursorIndex += numberOfCharacters;
1309   }
1310
1311   return cursorIndex;
1312 }
1313
1314 void Controller::Impl::UpdateCursorPosition()
1315 {
1316   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1317   if( NULL == mEventData )
1318   {
1319     // Nothing to do if there is no text input.
1320     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1321     return;
1322   }
1323
1324   if( IsShowingPlaceholderText() )
1325   {
1326     // Do not want to use the place-holder text to set the cursor position.
1327
1328     // Use the line's height of the font's family set to set the cursor's size.
1329     // If there is no font's family set, use the default font.
1330     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1331
1332     float lineHeight = 0.f;
1333
1334     FontId defaultFontId = 0u;
1335     if( NULL == mFontDefaults )
1336     {
1337       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1338                                              EMPTY_STRING );
1339     }
1340     else
1341     {
1342       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1343     }
1344
1345     Text::FontMetrics fontMetrics;
1346     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1347
1348     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1349
1350
1351     Vector2 cursorPosition;
1352
1353     switch( mLayoutEngine.GetHorizontalAlignment() )
1354     {
1355       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1356       {
1357         cursorPosition.x = 1.f;
1358         break;
1359       }
1360       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1361       {
1362         cursorPosition.x = floor( 0.5f * mControlSize.width );
1363         break;
1364       }
1365       case LayoutEngine::HORIZONTAL_ALIGN_END:
1366       {
1367         cursorPosition.x = mControlSize.width;
1368         break;
1369       }
1370     }
1371
1372     switch( mLayoutEngine.GetVerticalAlignment() )
1373     {
1374       case LayoutEngine::VERTICAL_ALIGN_TOP:
1375       {
1376         cursorPosition.y = 0.f;
1377         break;
1378       }
1379       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1380       {
1381         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1382         break;
1383       }
1384       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1385       {
1386         cursorPosition.y = mControlSize.height - lineHeight;
1387         break;
1388       }
1389     }
1390
1391     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1392                                          cursorPosition.x,
1393                                          cursorPosition.y,
1394                                          lineHeight,
1395                                          lineHeight );
1396   }
1397   else
1398   {
1399     CursorInfo cursorInfo;
1400     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1401                        cursorInfo );
1402
1403     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1404     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1405
1406     // Sets the cursor position.
1407     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1408                                          cursorPosition.x,
1409                                          cursorPosition.y,
1410                                          cursorInfo.primaryCursorHeight,
1411                                          cursorInfo.lineHeight );
1412     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1413
1414     // Sets the grab handle position.
1415     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1416                                          cursorPosition.x,
1417                                          cursorPosition.y,
1418                                          cursorInfo.lineHeight );
1419
1420     if( cursorInfo.isSecondaryCursor )
1421     {
1422       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1423       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1424                                            cursorInfo.secondaryPosition.x + offset.x,
1425                                            cursorInfo.secondaryPosition.y + offset.y,
1426                                            cursorInfo.secondaryCursorHeight,
1427                                            cursorInfo.lineHeight );
1428       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1429     }
1430     else
1431     {
1432       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1433     }
1434   }
1435   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1436 }
1437
1438 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1439 {
1440   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1441       ( RIGHT_SELECTION_HANDLE != handleType ) )
1442   {
1443     return;
1444   }
1445
1446   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1447   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1448
1449   CursorInfo cursorInfo;
1450   GetCursorPosition( index,
1451                      cursorInfo );
1452
1453   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1454   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1455
1456   // Sets the grab handle position.
1457   mEventData->mDecorator->SetPosition( handleType,
1458                                        cursorPosition.x,
1459                                        cursorPosition.y,
1460                                        cursorInfo.lineHeight );
1461 }
1462
1463 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1464 {
1465   // Clamp between -space & 0 (and the text alignment).
1466   if( actualSize.width > mControlSize.width )
1467   {
1468     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1469     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1470     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1471
1472     mEventData->mDecoratorUpdated = true;
1473   }
1474   else
1475   {
1476     mEventData->mScrollPosition.x = 0.f;
1477   }
1478 }
1479
1480 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1481 {
1482   // Clamp between -space & 0 (and the text alignment).
1483   if( actualSize.height > mControlSize.height )
1484   {
1485     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1486     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1487     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1488
1489     mEventData->mDecoratorUpdated = true;
1490   }
1491   else
1492   {
1493     mEventData->mScrollPosition.y = 0.f;
1494   }
1495 }
1496
1497 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1498 {
1499   Vector2 offset;
1500   bool updateDecorator = false;
1501   if( position.x < 0.f )
1502   {
1503     offset.x = -position.x;
1504     mEventData->mScrollPosition.x += offset.x;
1505     updateDecorator = true;
1506   }
1507   else if( position.x > mControlSize.width )
1508   {
1509     offset.x = mControlSize.width - position.x;
1510     mEventData->mScrollPosition.x += offset.x;
1511     updateDecorator = true;
1512   }
1513
1514   if( updateDecorator && mEventData->mDecorator )
1515   {
1516     mEventData->mDecorator->UpdatePositions( offset );
1517   }
1518
1519   // TODO : calculate the vertical scroll.
1520 }
1521
1522 void Controller::Impl::ScrollTextToMatchCursor()
1523 {
1524   // Get the current cursor position in decorator coords.
1525   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1526
1527   // Calculate the new cursor position.
1528   CursorInfo cursorInfo;
1529   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1530                      cursorInfo );
1531
1532   // Calculate the offset to match the cursor position before the character was deleted.
1533   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1534
1535   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1536   bool updateCursorPosition = true;
1537
1538   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1539   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1540
1541   if( updateCursorPosition )
1542   {
1543     // Sets the cursor position.
1544     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1545                                          cursorPosition.x,
1546                                          cursorPosition.y,
1547                                          cursorInfo.primaryCursorHeight,
1548                                          cursorInfo.lineHeight );
1549
1550     // Sets the grab handle position.
1551     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1552                                          cursorPosition.x,
1553                                          cursorPosition.y,
1554                                          cursorInfo.lineHeight );
1555
1556     if( cursorInfo.isSecondaryCursor )
1557     {
1558       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1559       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1560                                            cursorInfo.secondaryPosition.x + offset.x,
1561                                            cursorInfo.secondaryPosition.y + offset.y,
1562                                            cursorInfo.secondaryCursorHeight,
1563                                            cursorInfo.lineHeight );
1564       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1565     }
1566     else
1567     {
1568       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1569     }
1570   }
1571 }
1572
1573 void Controller::Impl::RequestRelayout()
1574 {
1575   mControlInterface.RequestTextRelayout();
1576 }
1577
1578 } // namespace Text
1579
1580 } // namespace Toolkit
1581
1582 } // namespace Dali