07e647c90ff39a767e456a7da09576cb8bcb449d
[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
24 namespace
25 {
26
27 /**
28  * @brief Some characters can be shaped in more than one glyph.
29  * This struct is used to retrieve metrics from these group of glyphs.
30  */
31 struct GlyphMetrics
32 {
33   GlyphMetrics()
34   : fontHeight( 0.f ),
35     advance( 0.f ),
36     ascender( 0.f ),
37     xBearing( 0.f )
38   {}
39
40   ~GlyphMetrics()
41   {}
42
43   float fontHeight; ///< The font's height of that glyphs.
44   float advance;    ///< The sum of all the advances of all the glyphs.
45   float ascender;   ///< The font's ascender.
46   float xBearing;   ///< The x bearing of the first glyph.
47 };
48
49 const std::string EMPTY_STRING("");
50
51 } // namespace
52
53 namespace Dali
54 {
55
56 namespace Toolkit
57 {
58
59 namespace Text
60 {
61
62 /**
63  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
64  *
65  * @param[in] glyphIndex The index to the first glyph.
66  * @param[in] numberOfGlyphs The number of glyphs.
67  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
68  * @param[in]
69  * @param[in]
70  */
71 void GetGlyphsMetrics( GlyphIndex glyphIndex,
72                        Length numberOfGlyphs,
73                        GlyphMetrics& glyphMetrics,
74                        VisualModelPtr visualModel,
75                        TextAbstraction::FontClient& fontClient )
76 {
77   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
78
79   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
80
81   Text::FontMetrics fontMetrics;
82   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
83
84   glyphMetrics.fontHeight = fontMetrics.height;
85   glyphMetrics.advance = firstGlyph.advance;
86   glyphMetrics.ascender = fontMetrics.ascender;
87   glyphMetrics.xBearing = firstGlyph.xBearing;
88
89   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
90   {
91     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
92
93     glyphMetrics.advance += glyphInfo.advance;
94   }
95 }
96
97 EventData::EventData( DecoratorPtr decorator )
98 : mDecorator( decorator ),
99   mPlaceholderText(),
100   mEventQueue(),
101   mScrollPosition(),
102   mState( INACTIVE ),
103   mPrimaryCursorPosition( 0u ),
104   mSecondaryCursorPosition( 0u ),
105   mDecoratorUpdated( false ),
106   mCursorBlinkEnabled( true ),
107   mGrabHandleEnabled( true ),
108   mGrabHandlePopupEnabled( true ),
109   mSelectionEnabled( true ),
110   mHorizontalScrollingEnabled( true ),
111   mVerticalScrollingEnabled( false ),
112   mUpdateCursorPosition( false )
113 {}
114
115 EventData::~EventData()
116 {}
117
118 bool Controller::Impl::ProcessInputEvents()
119 {
120   if( NULL == mEventData )
121   {
122     // Nothing to do if there is no text input.
123     return false;
124   }
125
126   mEventData->mDecoratorUpdated = false;
127
128   if( mEventData->mDecorator )
129   {
130     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
131          iter != mEventData->mEventQueue.end();
132          ++iter )
133     {
134       switch( iter->type )
135       {
136       case Event::KEYBOARD_FOCUS_GAIN_EVENT:
137       {
138         OnKeyboardFocus( true );
139         break;
140       }
141       case Event::KEYBOARD_FOCUS_LOST_EVENT:
142       {
143         OnKeyboardFocus( false );
144         break;
145       }
146       case Event::CURSOR_KEY_EVENT:
147       {
148         OnCursorKeyEvent( *iter );
149         break;
150       }
151       case Event::TAP_EVENT:
152       {
153         OnTapEvent( *iter );
154         break;
155       }
156       case Event::PAN_EVENT:
157       {
158         OnPanEvent( *iter );
159         break;
160       }
161       case Event::GRAB_HANDLE_EVENT:
162       {
163         OnGrabHandleEvent( *iter );
164         break;
165       }
166       }
167     }
168   }
169
170   // The cursor must also be repositioned after inserts into the model
171   if( mEventData->mUpdateCursorPosition )
172   {
173     UpdateCursorPosition();
174     mEventData->mUpdateCursorPosition = false;
175   }
176
177   mEventData->mEventQueue.clear();
178
179   return mEventData->mDecoratorUpdated;
180 }
181
182 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
183 {
184   if( NULL == mEventData )
185   {
186     // Nothing to do if there is no text input.
187     return;
188   }
189
190   if( !hasFocus )
191   {
192     ChangeState( EventData::INACTIVE );
193   }
194   else
195   {
196     ChangeState( EventData::EDITING );
197   }
198 }
199
200 void Controller::Impl::OnCursorKeyEvent( const Event& event )
201 {
202   if( NULL == mEventData )
203   {
204     // Nothing to do if there is no text input.
205     return;
206   }
207
208   int keyCode = event.p1.mInt;
209
210   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
211   {
212     if( mEventData->mPrimaryCursorPosition > 0u )
213     {
214       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
215     }
216   }
217   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
218   {
219     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
220     {
221       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
222     }
223   }
224   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
225   {
226     // TODO
227   }
228   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
229   {
230     // TODO
231   }
232
233   UpdateCursorPosition();
234 }
235
236 void Controller::Impl::HandleCursorKey( int keyCode )
237 {
238   // TODO
239   if( NULL == mEventData )
240   {
241     // Nothing to do if there is no text input.
242     return;
243   }
244 }
245
246 void Controller::Impl::OnTapEvent( const Event& event )
247 {
248   if( NULL == mEventData )
249   {
250     // Nothing to do if there is no text input.
251     return;
252   }
253
254   const unsigned int tapCount = event.p1.mUint;
255
256   if( 1u == tapCount )
257   {
258     ChangeState( EventData::EDITING );
259
260     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
261     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
262
263     mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
264                                                                 yPosition );
265
266     UpdateCursorPosition();
267   }
268   else if( mEventData->mSelectionEnabled &&
269            ( 2u == tapCount ) )
270   {
271     ChangeState( EventData::SELECTING );
272
273     RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
274   }
275 }
276
277 void Controller::Impl::OnPanEvent( const Event& event )
278 {
279   if( NULL == mEventData )
280   {
281     // Nothing to do if there is no text input.
282     return;
283   }
284
285   int state = event.p1.mInt;
286
287   if( Gesture::Started    == state ||
288       Gesture::Continuing == state )
289   {
290     const Vector2& actualSize = mVisualModel->GetActualSize();
291     const Vector2 currentScroll = mEventData->mScrollPosition;
292
293     if( mEventData->mHorizontalScrollingEnabled )
294     {
295       const float displacementX = event.p2.mFloat;
296       mEventData->mScrollPosition.x += displacementX;
297
298       // Clamp between -space & 0 (and the text alignment).
299       if( actualSize.width > mControlSize.width )
300       {
301         const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
302         mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
303         mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
304
305         mEventData->mDecoratorUpdated = true;
306       }
307       else
308       {
309         mEventData->mScrollPosition.x = 0.f;
310       }
311     }
312
313     if( mEventData->mVerticalScrollingEnabled )
314     {
315       const float displacementY = event.p3.mFloat;
316       mEventData->mScrollPosition.y += displacementY;
317
318       // Clamp between -space & 0 (and the text alignment).
319       if( actualSize.height > mControlSize.height )
320       {
321         const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
322         mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
323         mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
324
325         mEventData->mDecoratorUpdated = true;
326       }
327       else
328       {
329         mEventData->mScrollPosition.y = 0.f;
330       }
331     }
332
333     if( mEventData->mDecorator )
334     {
335       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
336     }
337   }
338 }
339
340 void Controller::Impl::OnGrabHandleEvent( const Event& event )
341 {
342   if( NULL == mEventData )
343   {
344     // Nothing to do if there is no text input.
345     return;
346   }
347
348   unsigned int state = event.p1.mUint;
349
350   if( GRAB_HANDLE_PRESSED == state )
351   {
352     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
353     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
354     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
355
356     mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
357
358     UpdateCursorPosition();
359
360     //mDecorator->HidePopup();
361     ChangeState ( EventData::EDITING );
362   }
363   else if( mEventData->mGrabHandlePopupEnabled &&
364            ( GRAB_HANDLE_RELEASED == state ) )
365   {
366     //mDecorator->ShowPopup();
367     ChangeState ( EventData::EDITING_WITH_POPUP );
368     mEventData->mDecoratorUpdated = true;
369   }
370 }
371
372 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
373 {
374   if( NULL == mEventData )
375   {
376     // Nothing to do if there is no text input.
377     return;
378   }
379
380   // TODO - Find which word was selected
381
382   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
383   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
384
385   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
386   const Vector<Vector2>::SizeType positionCount = positions.Count();
387
388   // Guard against glyphs which did not fit inside the layout
389   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
390
391   if( count )
392   {
393     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
394     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
395
396     // TODO - multi-line selection
397     const Vector<LineRun>& lines = mVisualModel->mLines;
398     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
399
400     mEventData->mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
401     mEventData->mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
402
403     mEventData->mDecorator->ClearHighlights();
404     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
405   }
406 }
407
408 void Controller::Impl::ChangeState( EventData::State newState )
409 {
410   if( NULL == mEventData )
411   {
412     // Nothing to do if there is no text input.
413     return;
414   }
415
416   if( mEventData->mState != newState )
417   {
418     mEventData->mState = newState;
419
420     if( EventData::INACTIVE == mEventData->mState )
421     {
422       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
423       mEventData->mDecorator->StopCursorBlink();
424       mEventData->mDecorator->SetGrabHandleActive( false );
425       mEventData->mDecorator->SetSelectionActive( false );
426       mEventData->mDecorator->SetPopupActive( false );
427       mEventData->mDecoratorUpdated = true;
428     }
429     else if ( EventData::SELECTING == mEventData->mState )
430     {
431       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
432       mEventData->mDecorator->StopCursorBlink();
433       mEventData->mDecorator->SetGrabHandleActive( false );
434       mEventData->mDecorator->SetSelectionActive( true );
435       mEventData->mDecoratorUpdated = true;
436     }
437     else if( EventData::EDITING == mEventData->mState )
438     {
439       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
440       if( mEventData->mCursorBlinkEnabled )
441       {
442         mEventData->mDecorator->StartCursorBlink();
443       }
444       if( mEventData->mGrabHandleEnabled )
445       {
446         mEventData->mDecorator->SetGrabHandleActive( true );
447       }
448       if( mEventData->mGrabHandlePopupEnabled )
449       {
450         mEventData->mDecorator->SetPopupActive( false );
451       }
452       mEventData->mDecorator->SetSelectionActive( false );
453       mEventData->mDecoratorUpdated = true;
454     }
455     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
456     {
457       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
458       if( mEventData->mCursorBlinkEnabled )
459       {
460         mEventData->mDecorator->StartCursorBlink();
461       }
462       if( mEventData->mGrabHandleEnabled )
463       {
464         mEventData->mDecorator->SetGrabHandleActive( true );
465       }
466       if( mEventData->mGrabHandlePopupEnabled )
467       {
468         mEventData->mDecorator->SetPopupActive( true );
469       }
470       mEventData->mDecorator->SetSelectionActive( false );
471       mEventData->mDecoratorUpdated = true;
472     }
473   }
474 }
475
476 LineIndex Controller::Impl::GetClosestLine( float y ) const
477 {
478   float totalHeight = 0.f;
479   LineIndex lineIndex = 0u;
480
481   const Vector<LineRun>& lines = mVisualModel->mLines;
482   for( LineIndex endLine = lines.Count();
483        lineIndex < endLine;
484        ++lineIndex )
485   {
486     const LineRun& lineRun = lines[lineIndex];
487     totalHeight += lineRun.ascender + -lineRun.descender;
488     if( y < totalHeight )
489     {
490       return lineIndex;
491     }
492   }
493
494   return lineIndex-1;
495 }
496
497 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
498                                                         float visualY )
499 {
500   if( NULL == mEventData )
501   {
502     // Nothing to do if there is no text input.
503     return 0u;
504   }
505
506   CharacterIndex logicalIndex = 0u;
507
508   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
509   const Length numberOfLines  = mVisualModel->mLines.Count();
510   if( 0 == numberOfGlyphs ||
511       0 == numberOfLines )
512   {
513     return logicalIndex;
514   }
515
516   // Find which line is closest
517   const LineIndex lineIndex = GetClosestLine( visualY );
518   const LineRun& line = mVisualModel->mLines[lineIndex];
519
520   // Get the positions of the glyphs.
521   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
522   const Vector2* const positionsBuffer = positions.Begin();
523
524   // Get the visual to logical conversion tables.
525   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
526   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
527
528   // Get the character to glyph conversion table.
529   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
530
531   // Get the glyphs per character table.
532   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
533
534   // If the vector is void, there is no right to left characters.
535   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
536
537   const CharacterIndex startCharacter = line.characterRun.characterIndex;
538   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
539   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
540
541   // Whether there is a hit on a glyph.
542   bool matched = false;
543
544   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
545   CharacterIndex visualIndex = startCharacter;
546   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
547   {
548     // The character in logical order.
549     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
550
551     // The first glyph for that character in logical order.
552     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
553
554     // The number of glyphs for that character
555     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
556
557     // Get the metrics for the group of glyphs.
558     GlyphMetrics glyphMetrics;
559     GetGlyphsMetrics( glyphLogicalOrderIndex,
560                       numberOfGlyphs,
561                       glyphMetrics,
562                       mVisualModel,
563                       mFontClient );
564
565     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
566
567     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
568
569     if( visualX < glyphX )
570     {
571       matched = true;
572       break;
573     }
574   }
575
576   // Return the logical position of the cursor in characters.
577
578   if( !matched )
579   {
580     visualIndex = endCharacter;
581   }
582
583   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
584 }
585
586 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
587                                           CursorInfo& cursorInfo )
588 {
589   // TODO: Check for multiline with \n, etc...
590
591   // Check if the logical position is the first or the last one of the text.
592   const bool isFirstPosition = 0u == logical;
593   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
594
595   if( isFirstPosition && isLastPosition )
596   {
597     // There is zero characters. Get the default font.
598
599     FontId defaultFontId = 0u;
600     if( NULL == mFontDefaults )
601     {
602       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
603                                              EMPTY_STRING );
604     }
605     else
606     {
607       defaultFontId = mFontDefaults->GetFontId( mFontClient );
608     }
609
610     Text::FontMetrics fontMetrics;
611     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
612
613     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
614     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
615
616     cursorInfo.primaryPosition.x = 0.f;
617     cursorInfo.primaryPosition.y = 0.f;
618
619     // Nothing else to do.
620     return;
621   }
622
623   // Get the previous logical index.
624   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
625
626   // Decrease the logical index if it's the last one.
627   if( isLastPosition )
628   {
629     --logical;
630   }
631
632   // Get the direction of the character and the previous one.
633   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
634
635   CharacterDirection isCurrentRightToLeft = false;
636   CharacterDirection isPreviousRightToLeft = false;
637   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
638   {
639     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
640     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
641   }
642
643   // Get the line where the character is laid-out.
644   const LineRun* modelLines = mVisualModel->mLines.Begin();
645
646   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
647   const LineRun& line = *( modelLines + lineIndex );
648
649   // Get the paragraph's direction.
650   const CharacterDirection isRightToLeftParagraph = line.direction;
651
652   // Check whether there is an alternative position:
653
654   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
655     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
656
657   // Set the line height.
658   cursorInfo.lineHeight = line.ascender + -line.descender;
659
660   // Convert the cursor position into the glyph position.
661   CharacterIndex characterIndex = logical;
662   if( cursorInfo.isSecondaryCursor &&
663       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
664   {
665     characterIndex = previousLogical;
666   }
667
668   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
669   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
670   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
671
672   // Get the metrics for the group of glyphs.
673   GlyphMetrics glyphMetrics;
674   GetGlyphsMetrics( currentGlyphIndex,
675                     numberOfGlyphs,
676                     glyphMetrics,
677                     mVisualModel,
678                     mFontClient );
679
680   float interGlyphAdvance = 0.f;
681   if( !isLastPosition &&
682       ( numberOfCharacters > 1u ) )
683   {
684     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
685     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
686   }
687
688   // Get the glyph position and x bearing.
689   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
690
691   // Set the cursor's height.
692   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
693
694   // Set the position.
695   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
696   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
697
698   if( isLastPosition )
699   {
700     // The position of the cursor after the last character needs special
701     // care depending on its direction and the direction of the paragraph.
702
703     if( cursorInfo.isSecondaryCursor )
704     {
705       // Need to find the first character after the last character with the paragraph's direction.
706       // i.e l0 l1 l2 r0 r1 should find r0.
707
708       // TODO: check for more than one line!
709       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
710       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
711
712       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
713       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
714
715       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
716
717       // Get the metrics for the group of glyphs.
718       GlyphMetrics glyphMetrics;
719       GetGlyphsMetrics( glyphIndex,
720                         numberOfGlyphs,
721                         glyphMetrics,
722                         mVisualModel,
723                         mFontClient );
724
725       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
726
727       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
728     }
729     else
730     {
731       if( !isCurrentRightToLeft )
732       {
733         cursorInfo.primaryPosition.x += glyphMetrics.advance;
734       }
735       else
736       {
737         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
738       }
739     }
740   }
741
742   // Set the alternative cursor position.
743   if( cursorInfo.isSecondaryCursor )
744   {
745     // Convert the cursor position into the glyph position.
746     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
747     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
748     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
749
750     // Get the glyph position.
751     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
752
753     // Get the metrics for the group of glyphs.
754     GlyphMetrics glyphMetrics;
755     GetGlyphsMetrics( previousGlyphIndex,
756                       numberOfGlyphs,
757                       glyphMetrics,
758                       mVisualModel,
759                       mFontClient );
760
761     // Set the cursor position and height.
762     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
763                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
764
765     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
766
767     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
768
769     // Update the primary cursor height as well.
770     cursorInfo.primaryCursorHeight *= 0.5f;
771   }
772 }
773
774 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
775 {
776   if( NULL == mEventData )
777   {
778     // Nothing to do if there is no text input.
779     return 0u;
780   }
781
782   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
783
784   const Script script = mLogicalModel->GetScript( index );
785   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
786   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
787
788   Length numberOfCharacters = 0u;
789   if( TextAbstraction::LATIN == script )
790   {
791     // Prevents to jump the whole Latin ligatures like fi, ff, ...
792     numberOfCharacters = 1u;
793   }
794   else
795   {
796     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
797     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
798
799     while( 0u == numberOfCharacters )
800     {
801       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
802       ++glyphIndex;
803     }
804   }
805
806   if( index < mEventData->mPrimaryCursorPosition )
807   {
808     cursorIndex -= numberOfCharacters;
809   }
810   else
811   {
812     cursorIndex += numberOfCharacters;
813   }
814
815   return cursorIndex;
816 }
817
818 void Controller::Impl::UpdateCursorPosition()
819 {
820   if( NULL == mEventData )
821   {
822     // Nothing to do if there is no text input.
823     return;
824   }
825
826   CursorInfo cursorInfo;
827   GetCursorPosition( mEventData->mPrimaryCursorPosition,
828                      cursorInfo );
829
830   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
831                                        cursorInfo.primaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
832                                        cursorInfo.primaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
833                                        cursorInfo.primaryCursorHeight,
834                                        cursorInfo.lineHeight );
835
836   if( cursorInfo.isSecondaryCursor )
837   {
838     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
839     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
840                                          cursorInfo.secondaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
841                                          cursorInfo.secondaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
842                                          cursorInfo.secondaryCursorHeight,
843                                          cursorInfo.lineHeight );
844   }
845   else
846   {
847     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
848   }
849
850   mEventData->mUpdateCursorPosition = false;
851   mEventData->mDecoratorUpdated = true;
852 }
853
854 void Controller::Impl::RequestRelayout()
855 {
856   mControlInterface.RequestTextRelayout();
857 }
858
859 } // namespace Text
860
861 } // namespace Toolkit
862
863 } // namespace Dali