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