Merged with Tizen Branch ( builds )
[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 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/bidirectional-support.h>
26 #include <dali-toolkit/internal/text/character-set-conversion.h>
27 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
28 #include <dali-toolkit/internal/text/multi-language-support.h>
29 #include <dali-toolkit/internal/text/script-run.h>
30 #include <dali-toolkit/internal/text/segmentation.h>
31 #include <dali-toolkit/internal/text/shaper.h>
32 #include <dali-toolkit/internal/text/text-io.h>
33 #include <dali-toolkit/internal/text/text-view.h>
34
35 namespace
36 {
37
38 /**
39  * @brief Some characters can be shaped in more than one glyph.
40  * This struct is used to retrieve metrics from these group of glyphs.
41  */
42 struct GlyphMetrics
43 {
44   GlyphMetrics()
45   : fontHeight( 0.f ),
46     advance( 0.f ),
47     ascender( 0.f ),
48     xBearing( 0.f )
49   {}
50
51   ~GlyphMetrics()
52   {}
53
54   float fontHeight; ///< The font's height of that glyphs.
55   float advance;    ///< The sum of all the advances of all the glyphs.
56   float ascender;   ///< The font's ascender.
57   float xBearing;   ///< The x bearing of the first glyph.
58 };
59
60 const std::string EMPTY_STRING("");
61
62 } // namespace
63
64 namespace Dali
65 {
66
67 namespace Toolkit
68 {
69
70 namespace Text
71 {
72
73 /**
74  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
75  *
76  * @param[in] glyphIndex The index to the first glyph.
77  * @param[in] numberOfGlyphs The number of glyphs.
78  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
79  * @param[in]
80  * @param[in]
81  */
82 void GetGlyphsMetrics( GlyphIndex glyphIndex,
83                        Length numberOfGlyphs,
84                        GlyphMetrics& glyphMetrics,
85                        VisualModelPtr visualModel,
86                        TextAbstraction::FontClient& fontClient )
87 {
88   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
89
90   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
91
92   Text::FontMetrics fontMetrics;
93   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
94
95   glyphMetrics.fontHeight = fontMetrics.height;
96   glyphMetrics.advance = firstGlyph.advance;
97   glyphMetrics.ascender = fontMetrics.ascender;
98   glyphMetrics.xBearing = firstGlyph.xBearing;
99
100   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
101   {
102     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
103
104     glyphMetrics.advance += glyphInfo.advance;
105   }
106 }
107
108 EventData::EventData( DecoratorPtr decorator )
109 : mDecorator( decorator ),
110   mPlaceholderTextActive(),
111   mPlaceholderTextInactive(),
112   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
113   mEventQueue(),
114   mScrollPosition(),
115   mState( INACTIVE ),
116   mPrimaryCursorPosition( 0u ),
117   mLeftSelectionPosition( 0u ),
118   mRightSelectionPosition( 0u ),
119   mPreEditStartPosition( 0u ),
120   mPreEditLength( 0u ),
121   mIsShowingPlaceholderText( false ),
122   mPreEditFlag( false ),
123   mDecoratorUpdated( false ),
124   mCursorBlinkEnabled( true ),
125   mGrabHandleEnabled( true ),
126   mGrabHandlePopupEnabled( false ),
127   mSelectionEnabled( false ),
128   mHorizontalScrollingEnabled( true ),
129   mVerticalScrollingEnabled( false ),
130   mUpdateCursorPosition( false ),
131   mUpdateLeftSelectionPosition( false ),
132   mUpdateRightSelectionPosition( false ),
133   mScrollAfterUpdateCursorPosition( false )
134 {}
135
136 EventData::~EventData()
137 {}
138
139 bool Controller::Impl::ProcessInputEvents()
140 {
141   if( NULL == mEventData )
142   {
143     // Nothing to do if there is no text input.
144     return false;
145   }
146
147   mEventData->mDecoratorUpdated = false;
148
149   if( mEventData->mDecorator )
150   {
151     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
152          iter != mEventData->mEventQueue.end();
153          ++iter )
154     {
155       switch( iter->type )
156       {
157       case Event::KEYBOARD_FOCUS_GAIN_EVENT:
158       {
159         OnKeyboardFocus( true );
160         break;
161       }
162       case Event::KEYBOARD_FOCUS_LOST_EVENT:
163       {
164         OnKeyboardFocus( false );
165         break;
166       }
167       case Event::CURSOR_KEY_EVENT:
168       {
169         OnCursorKeyEvent( *iter );
170         break;
171       }
172       case Event::TAP_EVENT:
173       {
174         OnTapEvent( *iter );
175         break;
176       }
177       case Event::PAN_EVENT:
178       {
179         OnPanEvent( *iter );
180         break;
181       }
182       case Event::GRAB_HANDLE_EVENT:
183       case Event::LEFT_SELECTION_HANDLE_EVENT:
184       case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
185       {
186         OnHandleEvent( *iter );
187         break;
188       }
189       }
190     }
191   }
192
193   // The cursor must also be repositioned after inserts into the model
194   if( mEventData->mUpdateCursorPosition )
195   {
196     // Updates the cursor position and scrolls the text to make it visible.
197
198     UpdateCursorPosition();
199
200     if( mEventData->mScrollAfterUpdateCursorPosition )
201     {
202       ScrollToMakeCursorVisible();
203       mEventData->mScrollAfterUpdateCursorPosition = false;
204     }
205
206     mEventData->mDecoratorUpdated = true;
207     mEventData->mUpdateCursorPosition = false;
208   }
209   else if( mEventData->mUpdateLeftSelectionPosition )
210   {
211     UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
212
213     if( mEventData->mScrollAfterUpdateCursorPosition )
214     {
215       ScrollToMakeCursorVisible();
216       mEventData->mScrollAfterUpdateCursorPosition = false;
217     }
218
219     mEventData->mDecoratorUpdated = true;
220     mEventData->mUpdateLeftSelectionPosition = false;
221   }
222   else if( mEventData->mUpdateRightSelectionPosition )
223   {
224     UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
225
226     if( mEventData->mScrollAfterUpdateCursorPosition )
227     {
228       ScrollToMakeCursorVisible();
229       mEventData->mScrollAfterUpdateCursorPosition = false;
230     }
231
232     mEventData->mDecoratorUpdated = true;
233     mEventData->mUpdateRightSelectionPosition = false;
234   }
235
236   mEventData->mEventQueue.clear();
237
238   return mEventData->mDecoratorUpdated;
239 }
240
241 void Controller::Impl::ReplaceTextWithPlaceholder()
242 {
243   DALI_ASSERT_DEBUG( mEventData && "No placeholder text available" );
244   if( !mEventData )
245   {
246     return;
247   }
248
249   // Disable handles when showing place-holder text
250   mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
251   mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
252   mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
253
254   const char* text( NULL );
255   size_t size( 0 );
256
257   if( EventData::INACTIVE != mEventData->mState &&
258       0u != mEventData->mPlaceholderTextActive.c_str() )
259   {
260     text = mEventData->mPlaceholderTextActive.c_str();
261     size = mEventData->mPlaceholderTextActive.size();
262   }
263
264   else
265   {
266     text = mEventData->mPlaceholderTextInactive.c_str();
267     size = mEventData->mPlaceholderTextInactive.size();
268   }
269
270   // Reset buffers.
271   mLogicalModel->mText.Clear();
272   mLogicalModel->mScriptRuns.Clear();
273   mLogicalModel->mFontRuns.Clear();
274   mLogicalModel->mLineBreakInfo.Clear();
275   mLogicalModel->mWordBreakInfo.Clear();
276   mLogicalModel->mBidirectionalParagraphInfo.Clear();
277   mLogicalModel->mCharacterDirections.Clear();
278   mLogicalModel->mBidirectionalLineInfo.Clear();
279   mLogicalModel->mLogicalToVisualMap.Clear();
280   mLogicalModel->mVisualToLogicalMap.Clear();
281   mVisualModel->mGlyphs.Clear();
282   mVisualModel->mGlyphsToCharacters.Clear();
283   mVisualModel->mCharactersToGlyph.Clear();
284   mVisualModel->mCharactersPerGlyph.Clear();
285   mVisualModel->mGlyphsPerCharacter.Clear();
286   mVisualModel->mGlyphPositions.Clear();
287   mVisualModel->mLines.Clear();
288   mVisualModel->ClearCaches();
289   mVisualModel->SetTextColor( mEventData->mPlaceholderTextColor );
290
291   //  Convert text into UTF-32
292   Vector<Character>& utf32Characters = mLogicalModel->mText;
293   utf32Characters.Resize( size );
294
295   // This is a bit horrible but std::string returns a (signed) char*
296   const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
297
298   // Transform a text array encoded in utf8 into an array encoded in utf32.
299   // It returns the actual number of characters.
300   Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
301   utf32Characters.Resize( characterCount );
302
303   // Reset the cursor position
304   mEventData->mPrimaryCursorPosition = 0;
305
306   // The natural size needs to be re-calculated.
307   mRecalculateNaturalSize = true;
308
309   // Apply modifications to the model
310   mOperationsPending = ALL_OPERATIONS;
311   UpdateModel( ALL_OPERATIONS );
312   mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
313                                                     ALIGN              |
314                                                     UPDATE_ACTUAL_SIZE |
315                                                     REORDER );
316 }
317
318 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
319 {
320   // Calculate the operations to be done.
321   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
322
323   Vector<Character>& utf32Characters = mLogicalModel->mText;
324
325   const Length numberOfCharacters = mLogicalModel->GetNumberOfCharacters();
326
327   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
328   if( GET_LINE_BREAKS & operations )
329   {
330     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
331     // calculate the bidirectional info for each 'paragraph'.
332     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
333     // is not shaped together).
334     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
335
336     SetLineBreakInfo( utf32Characters,
337                       lineBreakInfo );
338   }
339
340   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
341   if( GET_WORD_BREAKS & operations )
342   {
343     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
344     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
345
346     SetWordBreakInfo( utf32Characters,
347                       wordBreakInfo );
348   }
349
350   const bool getScripts = GET_SCRIPTS & operations;
351   const bool validateFonts = VALIDATE_FONTS & operations;
352
353   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
354   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
355
356   if( getScripts || validateFonts )
357   {
358     // Validates the fonts assigned by the application or assigns default ones.
359     // It makes sure all the characters are going to be rendered by the correct font.
360     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
361
362     if( getScripts )
363     {
364       // Retrieves the scripts used in the text.
365       multilanguageSupport.SetScripts( utf32Characters,
366                                        lineBreakInfo,
367                                        scripts );
368     }
369
370     if( validateFonts )
371     {
372       if( 0u == validFonts.Count() )
373       {
374         // Copy the requested font defaults received via the property system.
375         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
376         GetDefaultFonts( validFonts, numberOfCharacters );
377       }
378
379       // Validates the fonts. If there is a character with no assigned font it sets a default one.
380       // After this call, fonts are validated.
381       multilanguageSupport.ValidateFonts( utf32Characters,
382                                           scripts,
383                                           validFonts );
384     }
385   }
386
387   Vector<Character> mirroredUtf32Characters;
388   bool textMirrored = false;
389   if( BIDI_INFO & operations )
390   {
391     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
392     // bidirectional info.
393
394     Length numberOfParagraphs = 0u;
395
396     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
397     for( Length index = 0u; index < numberOfCharacters; ++index )
398     {
399       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
400       {
401         ++numberOfParagraphs;
402       }
403     }
404
405     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
406     bidirectionalInfo.Reserve( numberOfParagraphs );
407
408     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
409     SetBidirectionalInfo( utf32Characters,
410                           scripts,
411                           lineBreakInfo,
412                           bidirectionalInfo );
413
414     if( 0u != bidirectionalInfo.Count() )
415     {
416       // This paragraph has right to left text. Some characters may need to be mirrored.
417       // TODO: consider if the mirrored string can be stored as well.
418
419       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
420
421       // Only set the character directions if there is right to left characters.
422       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
423       directions.Resize( numberOfCharacters );
424
425       GetCharactersDirection( bidirectionalInfo,
426                               directions );
427     }
428     else
429     {
430       // There is no right to left characters. Clear the directions vector.
431       mLogicalModel->mCharacterDirections.Clear();
432     }
433
434    }
435
436   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
437   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
438   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
439   if( SHAPE_TEXT & operations )
440   {
441     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
442     // Shapes the text.
443     ShapeText( textToShape,
444                lineBreakInfo,
445                scripts,
446                validFonts,
447                glyphs,
448                glyphsToCharactersMap,
449                charactersPerGlyph );
450
451     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
452     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
453     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
454   }
455
456   const Length numberOfGlyphs = glyphs.Count();
457
458   if( GET_GLYPH_METRICS & operations )
459   {
460     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
461   }
462 }
463
464 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
465 {
466   if( mFontDefaults )
467   {
468     FontRun fontRun;
469     fontRun.characterRun.characterIndex = 0;
470     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
471     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
472     fontRun.isDefault = true;
473
474     fonts.PushBack( fontRun );
475   }
476 }
477
478 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
479 {
480   if( NULL == mEventData )
481   {
482     // Nothing to do if there is no text input.
483     return;
484   }
485
486   if( !hasFocus )
487   {
488     ChangeState( EventData::INACTIVE );
489   }
490   else
491   {
492     ChangeState( EventData::EDITING );
493   }
494 }
495
496 void Controller::Impl::OnCursorKeyEvent( const Event& event )
497 {
498   if( NULL == mEventData )
499   {
500     // Nothing to do if there is no text input.
501     return;
502   }
503
504   int keyCode = event.p1.mInt;
505
506   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
507   {
508     if( mEventData->mPrimaryCursorPosition > 0u )
509     {
510       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
511     }
512   }
513   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
514   {
515     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
516     {
517       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
518     }
519   }
520   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
521   {
522     // TODO
523   }
524   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
525   {
526     // TODO
527   }
528
529   mEventData->mUpdateCursorPosition = true;
530   mEventData->mScrollAfterUpdateCursorPosition = true;
531 }
532
533 void Controller::Impl::OnTapEvent( const Event& event )
534 {
535   if( NULL == mEventData )
536   {
537     // Nothing to do if there is no text input.
538     return;
539   }
540
541   const unsigned int tapCount = event.p1.mUint;
542
543   if( 1u == tapCount )
544   {
545     // Grab handle is not shown until a tap is received whilst EDITING
546     if( EventData::EDITING == mEventData->mState &&
547         !IsShowingPlaceholderText() )
548     {
549       if( mEventData->mGrabHandleEnabled )
550       {
551         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
552       }
553       mEventData->mDecorator->SetPopupActive( false );
554     }
555
556     ChangeState( EventData::EDITING );
557
558     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
559     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
560
561     mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
562                                                                 yPosition );
563
564     mEventData->mUpdateCursorPosition = true;
565     mEventData->mScrollAfterUpdateCursorPosition = true;
566   }
567   else if( mEventData->mSelectionEnabled &&
568            ( 2u == tapCount ) )
569   {
570     ChangeState( EventData::SELECTING );
571
572     RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
573   }
574 }
575
576 void Controller::Impl::OnPanEvent( const Event& event )
577 {
578   if( NULL == mEventData )
579   {
580     // Nothing to do if there is no text input.
581     return;
582   }
583
584   int state = event.p1.mInt;
585
586   if( Gesture::Started    == state ||
587       Gesture::Continuing == state )
588   {
589     const Vector2& actualSize = mVisualModel->GetActualSize();
590     const Vector2 currentScroll = mEventData->mScrollPosition;
591
592     if( mEventData->mHorizontalScrollingEnabled )
593     {
594       const float displacementX = event.p2.mFloat;
595       mEventData->mScrollPosition.x += displacementX;
596
597       ClampHorizontalScroll( actualSize );
598     }
599
600     if( mEventData->mVerticalScrollingEnabled )
601     {
602       const float displacementY = event.p3.mFloat;
603       mEventData->mScrollPosition.y += displacementY;
604
605       ClampVerticalScroll( actualSize );
606     }
607
608     if( mEventData->mDecorator )
609     {
610       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
611     }
612   }
613 }
614
615 void Controller::Impl::OnHandleEvent( const Event& event )
616 {
617   if( NULL == mEventData )
618   {
619     // Nothing to do if there is no text input.
620     return;
621   }
622
623   const unsigned int state = event.p1.mUint;
624
625   if( HANDLE_PRESSED == state )
626   {
627     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
628     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
629     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
630
631     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
632
633     if( Event::GRAB_HANDLE_EVENT == event.type )
634     {
635       ChangeState ( EventData::EDITING );
636
637       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
638       {
639         mEventData->mPrimaryCursorPosition = handleNewPosition;
640         mEventData->mUpdateCursorPosition = true;
641       }
642     }
643     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
644     {
645       if( handleNewPosition != mEventData->mLeftSelectionPosition )
646       {
647         mEventData->mLeftSelectionPosition = handleNewPosition;
648         mEventData->mUpdateLeftSelectionPosition = true;
649       }
650     }
651     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
652     {
653       if( handleNewPosition != mEventData->mRightSelectionPosition )
654       {
655         mEventData->mRightSelectionPosition = handleNewPosition;
656         mEventData->mUpdateRightSelectionPosition = true;
657       }
658     }
659   }
660   else if( ( HANDLE_RELEASED == state ) ||
661            ( HANDLE_STOP_SCROLLING == state ) )
662   {
663     if( mEventData->mGrabHandlePopupEnabled )
664     {
665       ChangeState( EventData::EDITING_WITH_POPUP );
666     }
667     if( Event::GRAB_HANDLE_EVENT == event.type )
668     {
669       mEventData->mUpdateCursorPosition = true;
670
671       if( HANDLE_STOP_SCROLLING == state )
672       {
673         // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
674         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
675         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
676
677         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
678
679         mEventData->mScrollAfterUpdateCursorPosition = true;
680       }
681     }
682     mEventData->mDecoratorUpdated = true;
683   }
684   else if( HANDLE_SCROLLING == state )
685   {
686     const float xSpeed = event.p2.mFloat;
687     const Vector2& actualSize = mVisualModel->GetActualSize();
688
689     mEventData->mScrollPosition.x += xSpeed;
690
691     ClampHorizontalScroll( actualSize );
692
693    mEventData->mDecoratorUpdated = true;
694   }
695 }
696
697 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
698 {
699   if( NULL == mEventData )
700   {
701     // Nothing to do if there is no text input.
702     return;
703   }
704
705   // TODO - Find which word was selected
706
707   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
708   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
709
710   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
711   const Vector<Vector2>::SizeType positionCount = positions.Count();
712
713   // Guard against glyphs which did not fit inside the layout
714   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
715
716   if( count )
717   {
718     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
719     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
720
721     // TODO - multi-line selection
722     const Vector<LineRun>& lines = mVisualModel->mLines;
723     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
724
725     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
726     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
727
728     mEventData->mDecorator->ClearHighlights();
729     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
730   }
731 }
732
733 void Controller::Impl::ChangeState( EventData::State newState )
734 {
735   if( NULL == mEventData )
736   {
737     // Nothing to do if there is no text input.
738     return;
739   }
740
741   if( mEventData->mState != newState )
742   {
743     // Show different placeholder when switching between active & inactive
744     bool updatePlaceholder( false );
745     if( IsShowingPlaceholderText() &&
746         ( EventData::INACTIVE == newState ||
747           EventData::INACTIVE == mEventData->mState ) )
748     {
749       updatePlaceholder = true;
750     }
751
752     mEventData->mState = newState;
753
754     if( updatePlaceholder )
755     {
756       ReplaceTextWithPlaceholder();
757       mEventData->mDecoratorUpdated = true;
758     }
759
760     if( EventData::INACTIVE == mEventData->mState )
761     {
762       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
763       mEventData->mDecorator->StopCursorBlink();
764       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
765       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
766       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
767       mEventData->mDecorator->SetPopupActive( false );
768       mEventData->mDecoratorUpdated = true;
769     }
770     else if ( EventData::SELECTING == mEventData->mState )
771     {
772       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
773       mEventData->mDecorator->StopCursorBlink();
774       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
775       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
776       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
777       mEventData->mDecoratorUpdated = true;
778     }
779     else if( EventData::EDITING == mEventData->mState )
780     {
781       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
782       if( mEventData->mCursorBlinkEnabled )
783       {
784         mEventData->mDecorator->StartCursorBlink();
785       }
786       // Grab handle is not shown until a tap is received whilst EDITING
787       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
788       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
789       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
790       mEventData->mDecoratorUpdated = true;
791     }
792     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
793     {
794       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
795       if( mEventData->mCursorBlinkEnabled )
796       {
797         mEventData->mDecorator->StartCursorBlink();
798       }
799       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
800       if( mEventData->mSelectionEnabled )
801       {
802         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
803         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
804       }
805       if( mEventData->mGrabHandlePopupEnabled )
806       {
807         mEventData->mDecorator->SetPopupActive( true );
808       }
809       mEventData->mDecoratorUpdated = true;
810     }
811   }
812 }
813
814 LineIndex Controller::Impl::GetClosestLine( float y ) const
815 {
816   float totalHeight = 0.f;
817   LineIndex lineIndex = 0u;
818
819   const Vector<LineRun>& lines = mVisualModel->mLines;
820   for( LineIndex endLine = lines.Count();
821        lineIndex < endLine;
822        ++lineIndex )
823   {
824     const LineRun& lineRun = lines[lineIndex];
825     totalHeight += lineRun.ascender + -lineRun.descender;
826     if( y < totalHeight )
827     {
828       return lineIndex;
829     }
830   }
831
832   return lineIndex-1;
833 }
834
835 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
836                                                         float visualY )
837 {
838   if( NULL == mEventData )
839   {
840     // Nothing to do if there is no text input.
841     return 0u;
842   }
843
844   CharacterIndex logicalIndex = 0u;
845
846   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
847   const Length numberOfLines  = mVisualModel->mLines.Count();
848   if( 0 == numberOfGlyphs ||
849       0 == numberOfLines )
850   {
851     return logicalIndex;
852   }
853
854   // Find which line is closest
855   const LineIndex lineIndex = GetClosestLine( visualY );
856   const LineRun& line = mVisualModel->mLines[lineIndex];
857
858   // Get the positions of the glyphs.
859   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
860   const Vector2* const positionsBuffer = positions.Begin();
861
862   // Get the visual to logical conversion tables.
863   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
864   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
865
866   // Get the character to glyph conversion table.
867   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
868
869   // Get the glyphs per character table.
870   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
871
872   // If the vector is void, there is no right to left characters.
873   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
874
875   const CharacterIndex startCharacter = line.characterRun.characterIndex;
876   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
877   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
878
879   // Whether there is a hit on a glyph.
880   bool matched = false;
881
882   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
883   CharacterIndex visualIndex = startCharacter;
884   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
885   {
886     // The character in logical order.
887     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
888
889     // The first glyph for that character in logical order.
890     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
891
892     // The number of glyphs for that character
893     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
894
895     // Get the metrics for the group of glyphs.
896     GlyphMetrics glyphMetrics;
897     GetGlyphsMetrics( glyphLogicalOrderIndex,
898                       numberOfGlyphs,
899                       glyphMetrics,
900                       mVisualModel,
901                       mFontClient );
902
903     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
904
905     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
906
907     if( visualX < glyphX )
908     {
909       matched = true;
910       break;
911     }
912   }
913
914   // Return the logical position of the cursor in characters.
915
916   if( !matched )
917   {
918     visualIndex = endCharacter;
919   }
920
921   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
922 }
923
924 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
925                                           CursorInfo& cursorInfo )
926 {
927   // TODO: Check for multiline with \n, etc...
928
929   // Check if the logical position is the first or the last one of the text.
930   const bool isFirstPosition = 0u == logical;
931   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
932
933   if( isFirstPosition && isLastPosition )
934   {
935     // There is zero characters. Get the default font.
936
937     FontId defaultFontId = 0u;
938     if( NULL == mFontDefaults )
939     {
940       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
941                                              EMPTY_STRING );
942     }
943     else
944     {
945       defaultFontId = mFontDefaults->GetFontId( mFontClient );
946     }
947
948     Text::FontMetrics fontMetrics;
949     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
950
951     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
952     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
953
954     cursorInfo.primaryPosition.x = 0.f;
955     cursorInfo.primaryPosition.y = 0.f;
956
957     // Nothing else to do.
958     return;
959   }
960
961   // Get the previous logical index.
962   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
963
964   // Decrease the logical index if it's the last one.
965   if( isLastPosition )
966   {
967     --logical;
968   }
969
970   // Get the direction of the character and the previous one.
971   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
972
973   CharacterDirection isCurrentRightToLeft = false;
974   CharacterDirection isPreviousRightToLeft = false;
975   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
976   {
977     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
978     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
979   }
980
981   // Get the line where the character is laid-out.
982   const LineRun* modelLines = mVisualModel->mLines.Begin();
983
984   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
985   const LineRun& line = *( modelLines + lineIndex );
986
987   // Get the paragraph's direction.
988   const CharacterDirection isRightToLeftParagraph = line.direction;
989
990   // Check whether there is an alternative position:
991
992   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
993     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
994
995   // Set the line height.
996   cursorInfo.lineHeight = line.ascender + -line.descender;
997
998   // Convert the cursor position into the glyph position.
999   CharacterIndex characterIndex = logical;
1000   if( cursorInfo.isSecondaryCursor &&
1001       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1002   {
1003     characterIndex = previousLogical;
1004   }
1005
1006   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1007   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1008   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1009
1010   // Get the metrics for the group of glyphs.
1011   GlyphMetrics glyphMetrics;
1012   GetGlyphsMetrics( currentGlyphIndex,
1013                     numberOfGlyphs,
1014                     glyphMetrics,
1015                     mVisualModel,
1016                     mFontClient );
1017
1018   float interGlyphAdvance = 0.f;
1019   if( !isLastPosition &&
1020       ( numberOfCharacters > 1u ) )
1021   {
1022     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1023     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1024   }
1025
1026   // Get the glyph position and x bearing.
1027   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1028
1029   // Set the cursor's height.
1030   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1031
1032   // Set the position.
1033   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1034   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1035
1036   if( isLastPosition )
1037   {
1038     // The position of the cursor after the last character needs special
1039     // care depending on its direction and the direction of the paragraph.
1040
1041     if( cursorInfo.isSecondaryCursor )
1042     {
1043       // Need to find the first character after the last character with the paragraph's direction.
1044       // i.e l0 l1 l2 r0 r1 should find r0.
1045
1046       // TODO: check for more than one line!
1047       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1048       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1049
1050       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1051       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1052
1053       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1054
1055       // Get the metrics for the group of glyphs.
1056       GlyphMetrics glyphMetrics;
1057       GetGlyphsMetrics( glyphIndex,
1058                         numberOfGlyphs,
1059                         glyphMetrics,
1060                         mVisualModel,
1061                         mFontClient );
1062
1063       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1064
1065       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1066     }
1067     else
1068     {
1069       if( !isCurrentRightToLeft )
1070       {
1071         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1072       }
1073       else
1074       {
1075         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1076       }
1077     }
1078   }
1079
1080   // Set the alternative cursor position.
1081   if( cursorInfo.isSecondaryCursor )
1082   {
1083     // Convert the cursor position into the glyph position.
1084     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1085     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1086     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1087
1088     // Get the glyph position.
1089     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1090
1091     // Get the metrics for the group of glyphs.
1092     GlyphMetrics glyphMetrics;
1093     GetGlyphsMetrics( previousGlyphIndex,
1094                       numberOfGlyphs,
1095                       glyphMetrics,
1096                       mVisualModel,
1097                       mFontClient );
1098
1099     // Set the cursor position and height.
1100     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1101                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1102
1103     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1104
1105     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1106
1107     // Update the primary cursor height as well.
1108     cursorInfo.primaryCursorHeight *= 0.5f;
1109   }
1110 }
1111
1112 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1113 {
1114   if( NULL == mEventData )
1115   {
1116     // Nothing to do if there is no text input.
1117     return 0u;
1118   }
1119
1120   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1121
1122   const Script script = mLogicalModel->GetScript( index );
1123   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1124   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1125
1126   Length numberOfCharacters = 0u;
1127   if( TextAbstraction::LATIN == script )
1128   {
1129     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1130     numberOfCharacters = 1u;
1131   }
1132   else
1133   {
1134     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1135     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1136
1137     while( 0u == numberOfCharacters )
1138     {
1139       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1140       ++glyphIndex;
1141     }
1142   }
1143
1144   if( index < mEventData->mPrimaryCursorPosition )
1145   {
1146     cursorIndex -= numberOfCharacters;
1147   }
1148   else
1149   {
1150     cursorIndex += numberOfCharacters;
1151   }
1152
1153   return cursorIndex;
1154 }
1155
1156 void Controller::Impl::UpdateCursorPosition()
1157 {
1158   if( NULL == mEventData )
1159   {
1160     // Nothing to do if there is no text input.
1161     return;
1162   }
1163
1164   CursorInfo cursorInfo;
1165   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1166                      cursorInfo );
1167
1168   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1169   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1170
1171   // Sets the cursor position.
1172   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1173                                        cursorPosition.x,
1174                                        cursorPosition.y,
1175                                        cursorInfo.primaryCursorHeight,
1176                                        cursorInfo.lineHeight );
1177
1178   // Sets the grab handle position.
1179   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1180                                        cursorPosition.x,
1181                                        cursorPosition.y,
1182                                        cursorInfo.lineHeight );
1183
1184   if( cursorInfo.isSecondaryCursor )
1185   {
1186     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1187     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1188                                          cursorInfo.secondaryPosition.x + offset.x,
1189                                          cursorInfo.secondaryPosition.y + offset.y,
1190                                          cursorInfo.secondaryCursorHeight,
1191                                          cursorInfo.lineHeight );
1192   }
1193   else
1194   {
1195     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1196   }
1197 }
1198
1199 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1200 {
1201   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1202       ( RIGHT_SELECTION_HANDLE != handleType ) )
1203   {
1204     return;
1205   }
1206
1207   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1208   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1209
1210   CursorInfo cursorInfo;
1211   GetCursorPosition( index,
1212                      cursorInfo );
1213
1214   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1215   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1216
1217   // Sets the grab handle position.
1218   mEventData->mDecorator->SetPosition( handleType,
1219                                        cursorPosition.x,
1220                                        cursorPosition.y,
1221                                        cursorInfo.lineHeight );
1222 }
1223
1224 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1225 {
1226   // Clamp between -space & 0 (and the text alignment).
1227   if( actualSize.width > mControlSize.width )
1228   {
1229     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1230     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1231     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1232
1233     mEventData->mDecoratorUpdated = true;
1234   }
1235   else
1236   {
1237     mEventData->mScrollPosition.x = 0.f;
1238   }
1239 }
1240
1241 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1242 {
1243   // Clamp between -space & 0 (and the text alignment).
1244   if( actualSize.height > mControlSize.height )
1245   {
1246     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1247     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1248     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1249
1250     mEventData->mDecoratorUpdated = true;
1251   }
1252   else
1253   {
1254     mEventData->mScrollPosition.y = 0.f;
1255   }
1256 }
1257
1258 void Controller::Impl::ScrollToMakeCursorVisible()
1259 {
1260   if( NULL == mEventData )
1261   {
1262     // Nothing to do if there is no text input.
1263     return;
1264   }
1265
1266   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1267
1268   Vector2 offset;
1269   bool updateDecorator = false;
1270   if( primaryCursorPosition.x < 0.f )
1271   {
1272     offset.x = -primaryCursorPosition.x;
1273     mEventData->mScrollPosition.x += offset.x;
1274     updateDecorator = true;
1275   }
1276   else if( primaryCursorPosition.x > mControlSize.width )
1277   {
1278     offset.x = mControlSize.width - primaryCursorPosition.x;
1279     mEventData->mScrollPosition.x += offset.x;
1280     updateDecorator = true;
1281   }
1282
1283   if( updateDecorator && mEventData->mDecorator )
1284   {
1285     mEventData->mDecorator->UpdatePositions( offset );
1286   }
1287
1288   // TODO : calculate the vertical scroll.
1289 }
1290
1291 void Controller::Impl::RequestRelayout()
1292 {
1293   mControlInterface.RequestTextRelayout();
1294 }
1295
1296 } // namespace Text
1297
1298 } // namespace Toolkit
1299
1300 } // namespace Dali