f7f173969f07d4228c0e591bac434a4c0a51cdff
[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   mLeftSelectionPosition( 0u ),
105   mRightSelectionPosition( 0u ),
106   mDecoratorUpdated( false ),
107   mCursorBlinkEnabled( true ),
108   mGrabHandleEnabled( true ),
109   mGrabHandlePopupEnabled( false ),
110   mSelectionEnabled( false ),
111   mHorizontalScrollingEnabled( true ),
112   mVerticalScrollingEnabled( false ),
113   mUpdateCursorPosition( false ),
114   mUpdateLeftSelectionPosition( false ),
115   mUpdateRightSelectionPosition( false ),
116   mScrollAfterUpdateCursorPosition( false )
117 {}
118
119 EventData::~EventData()
120 {}
121
122 bool Controller::Impl::ProcessInputEvents()
123 {
124   if( NULL == mEventData )
125   {
126     // Nothing to do if there is no text input.
127     return false;
128   }
129
130   mEventData->mDecoratorUpdated = false;
131
132   if( mEventData->mDecorator )
133   {
134     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
135          iter != mEventData->mEventQueue.end();
136          ++iter )
137     {
138       switch( iter->type )
139       {
140       case Event::KEYBOARD_FOCUS_GAIN_EVENT:
141       {
142         OnKeyboardFocus( true );
143         break;
144       }
145       case Event::KEYBOARD_FOCUS_LOST_EVENT:
146       {
147         OnKeyboardFocus( false );
148         break;
149       }
150       case Event::CURSOR_KEY_EVENT:
151       {
152         OnCursorKeyEvent( *iter );
153         break;
154       }
155       case Event::TAP_EVENT:
156       {
157         OnTapEvent( *iter );
158         break;
159       }
160       case Event::PAN_EVENT:
161       {
162         OnPanEvent( *iter );
163         break;
164       }
165       case Event::GRAB_HANDLE_EVENT:
166       case Event::LEFT_SELECTION_HANDLE_EVENT:
167       case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
168       {
169         OnHandleEvent( *iter );
170         break;
171       }
172       }
173     }
174   }
175
176   // The cursor must also be repositioned after inserts into the model
177   if( mEventData->mUpdateCursorPosition )
178   {
179     // Updates the cursor position and scrolls the text to make it visible.
180
181     UpdateCursorPosition();
182
183     if( mEventData->mScrollAfterUpdateCursorPosition )
184     {
185       ScrollToMakeCursorVisible();
186       mEventData->mScrollAfterUpdateCursorPosition = false;
187     }
188
189     mEventData->mDecoratorUpdated = true;
190     mEventData->mUpdateCursorPosition = false;
191   }
192   else if( mEventData->mUpdateLeftSelectionPosition )
193   {
194     UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
195
196     if( mEventData->mScrollAfterUpdateCursorPosition )
197     {
198       ScrollToMakeCursorVisible();
199       mEventData->mScrollAfterUpdateCursorPosition = false;
200     }
201
202     mEventData->mDecoratorUpdated = true;
203     mEventData->mUpdateLeftSelectionPosition = false;
204   }
205   else if( mEventData->mUpdateRightSelectionPosition )
206   {
207     UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
208
209     if( mEventData->mScrollAfterUpdateCursorPosition )
210     {
211       ScrollToMakeCursorVisible();
212       mEventData->mScrollAfterUpdateCursorPosition = false;
213     }
214
215     mEventData->mDecoratorUpdated = true;
216     mEventData->mUpdateRightSelectionPosition = false;
217   }
218
219   mEventData->mEventQueue.clear();
220
221   return mEventData->mDecoratorUpdated;
222 }
223
224 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
225 {
226   if( NULL == mEventData )
227   {
228     // Nothing to do if there is no text input.
229     return;
230   }
231
232   if( !hasFocus )
233   {
234     ChangeState( EventData::INACTIVE );
235   }
236   else
237   {
238     ChangeState( EventData::EDITING );
239   }
240 }
241
242 void Controller::Impl::OnCursorKeyEvent( const Event& event )
243 {
244   if( NULL == mEventData )
245   {
246     // Nothing to do if there is no text input.
247     return;
248   }
249
250   int keyCode = event.p1.mInt;
251
252   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
253   {
254     if( mEventData->mPrimaryCursorPosition > 0u )
255     {
256       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
257     }
258   }
259   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
260   {
261     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
262     {
263       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
264     }
265   }
266   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
267   {
268     // TODO
269   }
270   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
271   {
272     // TODO
273   }
274
275   mEventData->mUpdateCursorPosition = true;
276   mEventData->mScrollAfterUpdateCursorPosition = true;
277 }
278
279 void Controller::Impl::HandleCursorKey( int keyCode )
280 {
281   // TODO
282   if( NULL == mEventData )
283   {
284     // Nothing to do if there is no text input.
285     return;
286   }
287 }
288
289 void Controller::Impl::OnTapEvent( const Event& event )
290 {
291   if( NULL == mEventData )
292   {
293     // Nothing to do if there is no text input.
294     return;
295   }
296
297   const unsigned int tapCount = event.p1.mUint;
298
299   if( 1u == tapCount )
300   {
301     ChangeState( EventData::EDITING );
302
303     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
304     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
305
306     mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
307                                                                 yPosition );
308
309     mEventData->mUpdateCursorPosition = true;
310     mEventData->mScrollAfterUpdateCursorPosition = true;
311   }
312   else if( mEventData->mSelectionEnabled &&
313            ( 2u == tapCount ) )
314   {
315     ChangeState( EventData::SELECTING );
316
317     RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
318   }
319 }
320
321 void Controller::Impl::OnPanEvent( const Event& event )
322 {
323   if( NULL == mEventData )
324   {
325     // Nothing to do if there is no text input.
326     return;
327   }
328
329   int state = event.p1.mInt;
330
331   if( Gesture::Started    == state ||
332       Gesture::Continuing == state )
333   {
334     const Vector2& actualSize = mVisualModel->GetActualSize();
335     const Vector2 currentScroll = mEventData->mScrollPosition;
336
337     if( mEventData->mHorizontalScrollingEnabled )
338     {
339       const float displacementX = event.p2.mFloat;
340       mEventData->mScrollPosition.x += displacementX;
341
342       ClampHorizontalScroll( actualSize );
343     }
344
345     if( mEventData->mVerticalScrollingEnabled )
346     {
347       const float displacementY = event.p3.mFloat;
348       mEventData->mScrollPosition.y += displacementY;
349
350       ClampVerticalScroll( actualSize );
351     }
352
353     if( mEventData->mDecorator )
354     {
355       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
356     }
357   }
358 }
359
360 void Controller::Impl::OnHandleEvent( const Event& event )
361 {
362   if( NULL == mEventData )
363   {
364     // Nothing to do if there is no text input.
365     return;
366   }
367
368   const unsigned int state = event.p1.mUint;
369
370   if( HANDLE_PRESSED == state )
371   {
372     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
373     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
374     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
375
376     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
377
378     if( Event::GRAB_HANDLE_EVENT == event.type )
379     {
380       ChangeState ( EventData::EDITING );
381
382       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
383       {
384         mEventData->mPrimaryCursorPosition = handleNewPosition;
385         mEventData->mUpdateCursorPosition = true;
386       }
387     }
388     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
389     {
390       if( handleNewPosition != mEventData->mLeftSelectionPosition )
391       {
392         mEventData->mLeftSelectionPosition = handleNewPosition;
393         mEventData->mUpdateLeftSelectionPosition = true;
394       }
395     }
396     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
397     {
398       if( handleNewPosition != mEventData->mRightSelectionPosition )
399       {
400         mEventData->mRightSelectionPosition = handleNewPosition;
401         mEventData->mUpdateRightSelectionPosition = true;
402       }
403     }
404   }
405   else if( ( HANDLE_RELEASED == state ) ||
406            ( HANDLE_STOP_SCROLLING == state ) )
407   {
408     if( mEventData->mGrabHandlePopupEnabled )
409     {
410       ChangeState( EventData::EDITING_WITH_POPUP );
411     }
412     if( Event::GRAB_HANDLE_EVENT == event.type )
413     {
414       mEventData->mUpdateCursorPosition = true;
415
416       if( HANDLE_STOP_SCROLLING == state )
417       {
418         // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
419         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
420         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
421
422         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
423
424         mEventData->mScrollAfterUpdateCursorPosition = true;
425       }
426     }
427     mEventData->mDecoratorUpdated = true;
428   }
429   else if( HANDLE_SCROLLING == state )
430   {
431     const float xSpeed = event.p2.mFloat;
432     const Vector2& actualSize = mVisualModel->GetActualSize();
433
434     mEventData->mScrollPosition.x += xSpeed;
435
436     ClampHorizontalScroll( actualSize );
437
438    mEventData->mDecoratorUpdated = true;
439   }
440 }
441
442 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
443 {
444   if( NULL == mEventData )
445   {
446     // Nothing to do if there is no text input.
447     return;
448   }
449
450   // TODO - Find which word was selected
451
452   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
453   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
454
455   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
456   const Vector<Vector2>::SizeType positionCount = positions.Count();
457
458   // Guard against glyphs which did not fit inside the layout
459   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
460
461   if( count )
462   {
463     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
464     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
465
466     // TODO - multi-line selection
467     const Vector<LineRun>& lines = mVisualModel->mLines;
468     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
469
470     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
471     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
472
473     mEventData->mDecorator->ClearHighlights();
474     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
475   }
476 }
477
478 void Controller::Impl::ChangeState( EventData::State newState )
479 {
480   if( NULL == mEventData )
481   {
482     // Nothing to do if there is no text input.
483     return;
484   }
485
486   if( mEventData->mState != newState )
487   {
488     mEventData->mState = newState;
489
490     if( EventData::INACTIVE == mEventData->mState )
491     {
492       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
493       mEventData->mDecorator->StopCursorBlink();
494       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
495       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
496       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
497       mEventData->mDecorator->SetPopupActive( false );
498       mEventData->mDecoratorUpdated = true;
499     }
500     else if ( EventData::SELECTING == mEventData->mState )
501     {
502       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
503       mEventData->mDecorator->StopCursorBlink();
504       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
505       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
506       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
507       mEventData->mDecoratorUpdated = true;
508     }
509     else if( EventData::EDITING == mEventData->mState )
510     {
511       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
512       if( mEventData->mCursorBlinkEnabled )
513       {
514         mEventData->mDecorator->StartCursorBlink();
515       }
516       if( mEventData->mGrabHandleEnabled )
517       {
518         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
519       }
520       if( mEventData->mGrabHandlePopupEnabled )
521       {
522         mEventData->mDecorator->SetPopupActive( false );
523       }
524       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
525       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
526       mEventData->mDecoratorUpdated = true;
527     }
528     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
529     {
530       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
531       if( mEventData->mCursorBlinkEnabled )
532       {
533         mEventData->mDecorator->StartCursorBlink();
534       }
535       if( mEventData->mGrabHandleEnabled )
536       {
537         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
538         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
539       }
540       if( mEventData->mGrabHandlePopupEnabled )
541       {
542         mEventData->mDecorator->SetPopupActive( true );
543       }
544       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
545       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
546       mEventData->mDecoratorUpdated = true;
547     }
548   }
549 }
550
551 LineIndex Controller::Impl::GetClosestLine( float y ) const
552 {
553   float totalHeight = 0.f;
554   LineIndex lineIndex = 0u;
555
556   const Vector<LineRun>& lines = mVisualModel->mLines;
557   for( LineIndex endLine = lines.Count();
558        lineIndex < endLine;
559        ++lineIndex )
560   {
561     const LineRun& lineRun = lines[lineIndex];
562     totalHeight += lineRun.ascender + -lineRun.descender;
563     if( y < totalHeight )
564     {
565       return lineIndex;
566     }
567   }
568
569   return lineIndex-1;
570 }
571
572 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
573                                                         float visualY )
574 {
575   if( NULL == mEventData )
576   {
577     // Nothing to do if there is no text input.
578     return 0u;
579   }
580
581   CharacterIndex logicalIndex = 0u;
582
583   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
584   const Length numberOfLines  = mVisualModel->mLines.Count();
585   if( 0 == numberOfGlyphs ||
586       0 == numberOfLines )
587   {
588     return logicalIndex;
589   }
590
591   // Find which line is closest
592   const LineIndex lineIndex = GetClosestLine( visualY );
593   const LineRun& line = mVisualModel->mLines[lineIndex];
594
595   // Get the positions of the glyphs.
596   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
597   const Vector2* const positionsBuffer = positions.Begin();
598
599   // Get the visual to logical conversion tables.
600   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
601   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
602
603   // Get the character to glyph conversion table.
604   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
605
606   // Get the glyphs per character table.
607   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
608
609   // If the vector is void, there is no right to left characters.
610   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
611
612   const CharacterIndex startCharacter = line.characterRun.characterIndex;
613   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
614   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
615
616   // Whether there is a hit on a glyph.
617   bool matched = false;
618
619   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
620   CharacterIndex visualIndex = startCharacter;
621   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
622   {
623     // The character in logical order.
624     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
625
626     // The first glyph for that character in logical order.
627     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
628
629     // The number of glyphs for that character
630     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
631
632     // Get the metrics for the group of glyphs.
633     GlyphMetrics glyphMetrics;
634     GetGlyphsMetrics( glyphLogicalOrderIndex,
635                       numberOfGlyphs,
636                       glyphMetrics,
637                       mVisualModel,
638                       mFontClient );
639
640     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
641
642     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
643
644     if( visualX < glyphX )
645     {
646       matched = true;
647       break;
648     }
649   }
650
651   // Return the logical position of the cursor in characters.
652
653   if( !matched )
654   {
655     visualIndex = endCharacter;
656   }
657
658   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
659 }
660
661 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
662                                           CursorInfo& cursorInfo )
663 {
664   // TODO: Check for multiline with \n, etc...
665
666   // Check if the logical position is the first or the last one of the text.
667   const bool isFirstPosition = 0u == logical;
668   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
669
670   if( isFirstPosition && isLastPosition )
671   {
672     // There is zero characters. Get the default font.
673
674     FontId defaultFontId = 0u;
675     if( NULL == mFontDefaults )
676     {
677       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
678                                              EMPTY_STRING );
679     }
680     else
681     {
682       defaultFontId = mFontDefaults->GetFontId( mFontClient );
683     }
684
685     Text::FontMetrics fontMetrics;
686     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
687
688     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
689     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
690
691     cursorInfo.primaryPosition.x = 0.f;
692     cursorInfo.primaryPosition.y = 0.f;
693
694     // Nothing else to do.
695     return;
696   }
697
698   // Get the previous logical index.
699   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
700
701   // Decrease the logical index if it's the last one.
702   if( isLastPosition )
703   {
704     --logical;
705   }
706
707   // Get the direction of the character and the previous one.
708   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
709
710   CharacterDirection isCurrentRightToLeft = false;
711   CharacterDirection isPreviousRightToLeft = false;
712   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
713   {
714     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
715     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
716   }
717
718   // Get the line where the character is laid-out.
719   const LineRun* modelLines = mVisualModel->mLines.Begin();
720
721   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
722   const LineRun& line = *( modelLines + lineIndex );
723
724   // Get the paragraph's direction.
725   const CharacterDirection isRightToLeftParagraph = line.direction;
726
727   // Check whether there is an alternative position:
728
729   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
730     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
731
732   // Set the line height.
733   cursorInfo.lineHeight = line.ascender + -line.descender;
734
735   // Convert the cursor position into the glyph position.
736   CharacterIndex characterIndex = logical;
737   if( cursorInfo.isSecondaryCursor &&
738       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
739   {
740     characterIndex = previousLogical;
741   }
742
743   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
744   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
745   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
746
747   // Get the metrics for the group of glyphs.
748   GlyphMetrics glyphMetrics;
749   GetGlyphsMetrics( currentGlyphIndex,
750                     numberOfGlyphs,
751                     glyphMetrics,
752                     mVisualModel,
753                     mFontClient );
754
755   float interGlyphAdvance = 0.f;
756   if( !isLastPosition &&
757       ( numberOfCharacters > 1u ) )
758   {
759     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
760     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
761   }
762
763   // Get the glyph position and x bearing.
764   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
765
766   // Set the cursor's height.
767   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
768
769   // Set the position.
770   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
771   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
772
773   if( isLastPosition )
774   {
775     // The position of the cursor after the last character needs special
776     // care depending on its direction and the direction of the paragraph.
777
778     if( cursorInfo.isSecondaryCursor )
779     {
780       // Need to find the first character after the last character with the paragraph's direction.
781       // i.e l0 l1 l2 r0 r1 should find r0.
782
783       // TODO: check for more than one line!
784       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
785       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
786
787       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
788       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
789
790       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
791
792       // Get the metrics for the group of glyphs.
793       GlyphMetrics glyphMetrics;
794       GetGlyphsMetrics( glyphIndex,
795                         numberOfGlyphs,
796                         glyphMetrics,
797                         mVisualModel,
798                         mFontClient );
799
800       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
801
802       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
803     }
804     else
805     {
806       if( !isCurrentRightToLeft )
807       {
808         cursorInfo.primaryPosition.x += glyphMetrics.advance;
809       }
810       else
811       {
812         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
813       }
814     }
815   }
816
817   // Set the alternative cursor position.
818   if( cursorInfo.isSecondaryCursor )
819   {
820     // Convert the cursor position into the glyph position.
821     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
822     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
823     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
824
825     // Get the glyph position.
826     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
827
828     // Get the metrics for the group of glyphs.
829     GlyphMetrics glyphMetrics;
830     GetGlyphsMetrics( previousGlyphIndex,
831                       numberOfGlyphs,
832                       glyphMetrics,
833                       mVisualModel,
834                       mFontClient );
835
836     // Set the cursor position and height.
837     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
838                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
839
840     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
841
842     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
843
844     // Update the primary cursor height as well.
845     cursorInfo.primaryCursorHeight *= 0.5f;
846   }
847 }
848
849 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
850 {
851   if( NULL == mEventData )
852   {
853     // Nothing to do if there is no text input.
854     return 0u;
855   }
856
857   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
858
859   const Script script = mLogicalModel->GetScript( index );
860   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
861   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
862
863   Length numberOfCharacters = 0u;
864   if( TextAbstraction::LATIN == script )
865   {
866     // Prevents to jump the whole Latin ligatures like fi, ff, ...
867     numberOfCharacters = 1u;
868   }
869   else
870   {
871     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
872     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
873
874     while( 0u == numberOfCharacters )
875     {
876       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
877       ++glyphIndex;
878     }
879   }
880
881   if( index < mEventData->mPrimaryCursorPosition )
882   {
883     cursorIndex -= numberOfCharacters;
884   }
885   else
886   {
887     cursorIndex += numberOfCharacters;
888   }
889
890   return cursorIndex;
891 }
892
893 void Controller::Impl::UpdateCursorPosition()
894 {
895   if( NULL == mEventData )
896   {
897     // Nothing to do if there is no text input.
898     return;
899   }
900
901   CursorInfo cursorInfo;
902   GetCursorPosition( mEventData->mPrimaryCursorPosition,
903                      cursorInfo );
904
905   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
906   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
907
908   // Sets the cursor position.
909   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
910                                        cursorPosition.x,
911                                        cursorPosition.y,
912                                        cursorInfo.primaryCursorHeight,
913                                        cursorInfo.lineHeight );
914
915   // Sets the grab handle position.
916   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
917                                        cursorPosition.x,
918                                        cursorPosition.y,
919                                        cursorInfo.lineHeight );
920
921   if( cursorInfo.isSecondaryCursor )
922   {
923     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
924     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
925                                          cursorInfo.secondaryPosition.x + offset.x,
926                                          cursorInfo.secondaryPosition.y + offset.y,
927                                          cursorInfo.secondaryCursorHeight,
928                                          cursorInfo.lineHeight );
929   }
930   else
931   {
932     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
933   }
934 }
935
936 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
937 {
938   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
939       ( RIGHT_SELECTION_HANDLE != handleType ) )
940   {
941     return;
942   }
943
944   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
945   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
946
947   CursorInfo cursorInfo;
948   GetCursorPosition( index,
949                      cursorInfo );
950
951   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
952   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
953
954   // Sets the grab handle position.
955   mEventData->mDecorator->SetPosition( handleType,
956                                        cursorPosition.x,
957                                        cursorPosition.y,
958                                        cursorInfo.lineHeight );
959 }
960
961 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
962 {
963   // Clamp between -space & 0 (and the text alignment).
964   if( actualSize.width > mControlSize.width )
965   {
966     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
967     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
968     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
969
970     mEventData->mDecoratorUpdated = true;
971   }
972   else
973   {
974     mEventData->mScrollPosition.x = 0.f;
975   }
976 }
977
978 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
979 {
980   // Clamp between -space & 0 (and the text alignment).
981   if( actualSize.height > mControlSize.height )
982   {
983     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
984     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
985     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
986
987     mEventData->mDecoratorUpdated = true;
988   }
989   else
990   {
991     mEventData->mScrollPosition.y = 0.f;
992   }
993 }
994
995 void Controller::Impl::ScrollToMakeCursorVisible()
996 {
997   if( NULL == mEventData )
998   {
999     // Nothing to do if there is no text input.
1000     return;
1001   }
1002
1003   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1004
1005   Vector2 offset;
1006   bool updateDecorator = false;
1007   if( primaryCursorPosition.x < 0.f )
1008   {
1009     offset.x = -primaryCursorPosition.x;
1010     mEventData->mScrollPosition.x += offset.x;
1011     updateDecorator = true;
1012   }
1013   else if( primaryCursorPosition.x > mControlSize.width )
1014   {
1015     offset.x = mControlSize.width - primaryCursorPosition.x;
1016     mEventData->mScrollPosition.x += offset.x;
1017     updateDecorator = true;
1018   }
1019
1020   if( updateDecorator && mEventData->mDecorator )
1021   {
1022     mEventData->mDecorator->UpdatePositions( offset );
1023   }
1024
1025   // TODO : calculate the vertical scroll.
1026 }
1027
1028 void Controller::Impl::RequestRelayout()
1029 {
1030   mControlInterface.RequestTextRelayout();
1031 }
1032
1033 } // namespace Text
1034
1035 } // namespace Toolkit
1036
1037 } // namespace Dali