Fix prevent issues
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
29 #include <dali-toolkit/internal/text/multi-language-support.h>
30 #include <dali-toolkit/internal/text/script-run.h>
31 #include <dali-toolkit/internal/text/segmentation.h>
32 #include <dali-toolkit/internal/text/shaper.h>
33 #include <dali-toolkit/internal/text/text-io.h>
34 #include <dali-toolkit/internal/text/text-view.h>
35
36 namespace
37 {
38
39 #if defined(DEBUG_ENABLED)
40   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
41 #endif
42
43 /**
44  * @brief Some characters can be shaped in more than one glyph.
45  * This struct is used to retrieve metrics from these group of glyphs.
46  */
47 struct GlyphMetrics
48 {
49   GlyphMetrics()
50   : fontHeight( 0.f ),
51     advance( 0.f ),
52     ascender( 0.f ),
53     xBearing( 0.f )
54   {}
55
56   ~GlyphMetrics()
57   {}
58
59   float fontHeight; ///< The font's height of that glyphs.
60   float advance;    ///< The sum of all the advances of all the glyphs.
61   float ascender;   ///< The font's ascender.
62   float xBearing;   ///< The x bearing of the first glyph.
63 };
64
65 const std::string EMPTY_STRING("");
66
67 } // namespace
68
69 namespace Dali
70 {
71
72 namespace Toolkit
73 {
74
75 namespace Text
76 {
77
78 /**
79  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
80  *
81  * @param[in] glyphIndex The index to the first glyph.
82  * @param[in] numberOfGlyphs The number of glyphs.
83  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
84  * @param[in]
85  * @param[in]
86  */
87 void GetGlyphsMetrics( GlyphIndex glyphIndex,
88                        Length numberOfGlyphs,
89                        GlyphMetrics& glyphMetrics,
90                        VisualModelPtr visualModel,
91                        TextAbstraction::FontClient& fontClient )
92 {
93   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
94
95   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
96
97   Text::FontMetrics fontMetrics;
98   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
99
100   glyphMetrics.fontHeight = fontMetrics.height;
101   glyphMetrics.advance = firstGlyph.advance;
102   glyphMetrics.ascender = fontMetrics.ascender;
103   glyphMetrics.xBearing = firstGlyph.xBearing;
104
105   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
106   {
107     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
108
109     glyphMetrics.advance += glyphInfo.advance;
110   }
111 }
112
113 EventData::EventData( DecoratorPtr decorator )
114 : mDecorator( decorator ),
115   mPlaceholderTextActive(),
116   mPlaceholderTextInactive(),
117   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
118   mEventQueue(),
119   mScrollPosition(),
120   mState( INACTIVE ),
121   mPrimaryCursorPosition( 0u ),
122   mLeftSelectionPosition( 0u ),
123   mRightSelectionPosition( 0u ),
124   mPreEditStartPosition( 0u ),
125   mPreEditLength( 0u ),
126   mIsShowingPlaceholderText( false ),
127   mPreEditFlag( false ),
128   mDecoratorUpdated( false ),
129   mCursorBlinkEnabled( true ),
130   mGrabHandleEnabled( true ),
131   mGrabHandlePopupEnabled( true ),
132   mSelectionEnabled( 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::GRAB_HANDLE_PANNING );
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       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
546
547       if( handleNewPosition != mEventData->mLeftSelectionPosition )
548       {
549         mEventData->mLeftSelectionPosition = handleNewPosition;
550         mEventData->mUpdateLeftSelectionPosition = true;
551       }
552     }
553     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
554     {
555       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
556
557       if( handleNewPosition != mEventData->mRightSelectionPosition )
558       {
559         mEventData->mRightSelectionPosition = handleNewPosition;
560         mEventData->mUpdateRightSelectionPosition = true;
561       }
562     }
563   }
564   else if( ( HANDLE_RELEASED == state ) ||
565            ( HANDLE_STOP_SCROLLING == state ) )
566   {
567     if( Event::GRAB_HANDLE_EVENT == event.type )
568     {
569       mEventData->mUpdateCursorPosition = true;
570
571       ChangeState( EventData::EDITING_WITH_POPUP );
572
573       if( HANDLE_STOP_SCROLLING == state )
574       {
575         // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
576         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
577         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
578
579         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
580
581         mEventData->mScrollAfterUpdateCursorPosition = true;
582       }
583     }
584     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
585     {
586       ChangeState( EventData::SELECTING );
587     }
588     mEventData->mDecoratorUpdated = true;
589   }
590   else if( HANDLE_SCROLLING == state )
591   {
592     const float xSpeed = event.p2.mFloat;
593     const Vector2& actualSize = mVisualModel->GetActualSize();
594
595     mEventData->mScrollPosition.x += xSpeed;
596
597     ClampHorizontalScroll( actualSize );
598
599     if( Event::GRAB_HANDLE_EVENT == event.type )
600     {
601       ChangeState( EventData::GRAB_HANDLE_PANNING );
602     }
603     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
604     {
605       ChangeState( EventData::SELECTION_HANDLE_PANNING );
606     }
607
608     mEventData->mDecoratorUpdated = true;
609   }
610 }
611
612 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
613 {
614   if( NULL == mEventData )
615   {
616     // Nothing to do if there is no text input.
617     return;
618   }
619
620   // TODO - Find which word was selected
621
622   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
623   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
624
625   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
626   const Vector<Vector2>::SizeType positionCount = positions.Count();
627
628   // Guard against glyphs which did not fit inside the layout
629   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
630
631   if( count )
632   {
633     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
634     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
635
636     // TODO - multi-line selection
637     const Vector<LineRun>& lines = mVisualModel->mLines;
638     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
639
640     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
641     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
642
643     mEventData->mDecorator->ClearHighlights();
644     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
645   }
646 }
647
648 void Controller::Impl::ChangeState( EventData::State newState )
649 {
650   if( NULL == mEventData )
651   {
652     // Nothing to do if there is no text input.
653     return;
654   }
655
656   if( mEventData->mState != newState )
657   {
658     mEventData->mState = newState;
659
660     if( EventData::INACTIVE == mEventData->mState )
661     {
662       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
663       mEventData->mDecorator->StopCursorBlink();
664       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
665       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
666       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
667       mEventData->mDecorator->SetPopupActive( false );
668       mEventData->mDecoratorUpdated = true;
669     }
670     else if ( EventData::SELECTING == mEventData->mState )
671     {
672       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
673       mEventData->mDecorator->StopCursorBlink();
674       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
675       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
676       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
677       if( mEventData->mGrabHandlePopupEnabled )
678       {
679         mEventData->mDecorator->SetPopupActive( true );
680       }
681       mEventData->mDecoratorUpdated = true;
682     }
683     else if( EventData::EDITING == mEventData->mState )
684     {
685       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
686       if( mEventData->mCursorBlinkEnabled )
687       {
688         mEventData->mDecorator->StartCursorBlink();
689       }
690       // Grab handle is not shown until a tap is received whilst EDITING
691       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
692       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
693       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
694       if( mEventData->mGrabHandlePopupEnabled )
695       {
696         mEventData->mDecorator->SetPopupActive( false );
697       }
698       mEventData->mDecoratorUpdated = true;
699     }
700     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
701     {
702       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
703       if( mEventData->mCursorBlinkEnabled )
704       {
705         mEventData->mDecorator->StartCursorBlink();
706       }
707       if( mEventData->mSelectionEnabled )
708       {
709         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
710         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
711       }
712       else
713       {
714         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
715       }
716       if( mEventData->mGrabHandlePopupEnabled )
717       {
718         mEventData->mDecorator->SetPopupActive( true );
719       }
720       mEventData->mDecoratorUpdated = true;
721     }
722     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
723     {
724       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
725       mEventData->mDecorator->StopCursorBlink();
726       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
727       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
728       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
729       if( mEventData->mGrabHandlePopupEnabled )
730       {
731         mEventData->mDecorator->SetPopupActive( false );
732       }
733       mEventData->mDecoratorUpdated = true;
734     }
735     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
736     {
737       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
738       if( mEventData->mCursorBlinkEnabled )
739       {
740         mEventData->mDecorator->StartCursorBlink();
741       }
742       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
743       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
744       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
745       if( mEventData->mGrabHandlePopupEnabled )
746       {
747         mEventData->mDecorator->SetPopupActive( false );
748       }
749       mEventData->mDecoratorUpdated = true;
750     }
751   }
752 }
753
754 LineIndex Controller::Impl::GetClosestLine( float y ) const
755 {
756   float totalHeight = 0.f;
757   LineIndex lineIndex = 0u;
758
759   const Vector<LineRun>& lines = mVisualModel->mLines;
760   for( LineIndex endLine = lines.Count();
761        lineIndex < endLine;
762        ++lineIndex )
763   {
764     const LineRun& lineRun = lines[lineIndex];
765     totalHeight += lineRun.ascender + -lineRun.descender;
766     if( y < totalHeight )
767     {
768       return lineIndex;
769     }
770   }
771
772   if( lineIndex == 0 )
773   {
774     return 0;
775   }
776
777   return lineIndex-1;
778 }
779
780 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
781                                                         float visualY )
782 {
783   if( NULL == mEventData )
784   {
785     // Nothing to do if there is no text input.
786     return 0u;
787   }
788
789   CharacterIndex logicalIndex = 0u;
790
791   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
792   const Length numberOfLines  = mVisualModel->mLines.Count();
793   if( 0 == numberOfGlyphs ||
794       0 == numberOfLines )
795   {
796     return logicalIndex;
797   }
798
799   // Find which line is closest
800   const LineIndex lineIndex = GetClosestLine( visualY );
801   const LineRun& line = mVisualModel->mLines[lineIndex];
802
803   // Get the positions of the glyphs.
804   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
805   const Vector2* const positionsBuffer = positions.Begin();
806
807   // Get the visual to logical conversion tables.
808   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
809   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
810
811   // Get the character to glyph conversion table.
812   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
813
814   // Get the glyphs per character table.
815   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
816
817   // If the vector is void, there is no right to left characters.
818   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
819
820   const CharacterIndex startCharacter = line.characterRun.characterIndex;
821   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
822   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
823
824   // Whether there is a hit on a glyph.
825   bool matched = false;
826
827   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
828   CharacterIndex visualIndex = startCharacter;
829   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
830   {
831     // The character in logical order.
832     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
833
834     // The first glyph for that character in logical order.
835     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
836
837     // The number of glyphs for that character
838     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
839
840     // Get the metrics for the group of glyphs.
841     GlyphMetrics glyphMetrics;
842     GetGlyphsMetrics( glyphLogicalOrderIndex,
843                       numberOfGlyphs,
844                       glyphMetrics,
845                       mVisualModel,
846                       mFontClient );
847
848     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
849
850     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
851
852     if( visualX < glyphX )
853     {
854       matched = true;
855       break;
856     }
857   }
858
859   // Return the logical position of the cursor in characters.
860
861   if( !matched )
862   {
863     visualIndex = endCharacter;
864   }
865
866   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
867 }
868
869 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
870                                           CursorInfo& cursorInfo )
871 {
872   // TODO: Check for multiline with \n, etc...
873
874   // Check if the logical position is the first or the last one of the text.
875   const bool isFirstPosition = 0u == logical;
876   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
877
878   if( isFirstPosition && isLastPosition )
879   {
880     // There is zero characters. Get the default font.
881
882     FontId defaultFontId = 0u;
883     if( NULL == mFontDefaults )
884     {
885       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
886                                              EMPTY_STRING );
887     }
888     else
889     {
890       defaultFontId = mFontDefaults->GetFontId( mFontClient );
891     }
892
893     Text::FontMetrics fontMetrics;
894     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
895
896     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
897     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
898
899     cursorInfo.primaryPosition.x = 0.f;
900     cursorInfo.primaryPosition.y = 0.f;
901
902     // Nothing else to do.
903     return;
904   }
905
906   // Get the previous logical index.
907   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
908
909   // Decrease the logical index if it's the last one.
910   if( isLastPosition )
911   {
912     --logical;
913   }
914
915   // Get the direction of the character and the previous one.
916   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
917
918   CharacterDirection isCurrentRightToLeft = false;
919   CharacterDirection isPreviousRightToLeft = false;
920   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
921   {
922     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
923     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
924   }
925
926   // Get the line where the character is laid-out.
927   const LineRun* modelLines = mVisualModel->mLines.Begin();
928
929   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
930   const LineRun& line = *( modelLines + lineIndex );
931
932   // Get the paragraph's direction.
933   const CharacterDirection isRightToLeftParagraph = line.direction;
934
935   // Check whether there is an alternative position:
936
937   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
938     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
939
940   // Set the line height.
941   cursorInfo.lineHeight = line.ascender + -line.descender;
942
943   // Convert the cursor position into the glyph position.
944   CharacterIndex characterIndex = logical;
945   if( cursorInfo.isSecondaryCursor &&
946       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
947   {
948     characterIndex = previousLogical;
949   }
950
951   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
952   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
953   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
954
955   // Get the metrics for the group of glyphs.
956   GlyphMetrics glyphMetrics;
957   GetGlyphsMetrics( currentGlyphIndex,
958                     numberOfGlyphs,
959                     glyphMetrics,
960                     mVisualModel,
961                     mFontClient );
962
963   float interGlyphAdvance = 0.f;
964   if( !isLastPosition &&
965       ( numberOfCharacters > 1u ) )
966   {
967     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
968     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
969   }
970
971   // Get the glyph position and x bearing.
972   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
973
974   // Set the cursor's height.
975   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
976
977   // Set the position.
978   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
979   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
980
981   if( isLastPosition )
982   {
983     // The position of the cursor after the last character needs special
984     // care depending on its direction and the direction of the paragraph.
985
986     if( cursorInfo.isSecondaryCursor )
987     {
988       // Need to find the first character after the last character with the paragraph's direction.
989       // i.e l0 l1 l2 r0 r1 should find r0.
990
991       // TODO: check for more than one line!
992       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
993       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
994
995       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
996       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
997
998       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
999
1000       // Get the metrics for the group of glyphs.
1001       GlyphMetrics glyphMetrics;
1002       GetGlyphsMetrics( glyphIndex,
1003                         numberOfGlyphs,
1004                         glyphMetrics,
1005                         mVisualModel,
1006                         mFontClient );
1007
1008       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1009
1010       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1011     }
1012     else
1013     {
1014       if( !isCurrentRightToLeft )
1015       {
1016         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1017       }
1018       else
1019       {
1020         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1021       }
1022     }
1023   }
1024
1025   // Set the alternative cursor position.
1026   if( cursorInfo.isSecondaryCursor )
1027   {
1028     // Convert the cursor position into the glyph position.
1029     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1030     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1031     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1032
1033     // Get the glyph position.
1034     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1035
1036     // Get the metrics for the group of glyphs.
1037     GlyphMetrics glyphMetrics;
1038     GetGlyphsMetrics( previousGlyphIndex,
1039                       numberOfGlyphs,
1040                       glyphMetrics,
1041                       mVisualModel,
1042                       mFontClient );
1043
1044     // Set the cursor position and height.
1045     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1046                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1047
1048     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1049
1050     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1051
1052     // Update the primary cursor height as well.
1053     cursorInfo.primaryCursorHeight *= 0.5f;
1054   }
1055 }
1056
1057 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1058 {
1059   if( NULL == mEventData )
1060   {
1061     // Nothing to do if there is no text input.
1062     return 0u;
1063   }
1064
1065   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1066
1067   const Script script = mLogicalModel->GetScript( index );
1068   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1069   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1070
1071   Length numberOfCharacters = 0u;
1072   if( TextAbstraction::LATIN == script )
1073   {
1074     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1075     numberOfCharacters = 1u;
1076   }
1077   else
1078   {
1079     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1080     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1081
1082     while( 0u == numberOfCharacters )
1083     {
1084       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1085       ++glyphIndex;
1086     }
1087   }
1088
1089   if( index < mEventData->mPrimaryCursorPosition )
1090   {
1091     cursorIndex -= numberOfCharacters;
1092   }
1093   else
1094   {
1095     cursorIndex += numberOfCharacters;
1096   }
1097
1098   return cursorIndex;
1099 }
1100
1101 void Controller::Impl::UpdateCursorPosition()
1102 {
1103   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1104   if( NULL == mEventData )
1105   {
1106     // Nothing to do if there is no text input.
1107     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1108     return;
1109   }
1110
1111   if( IsShowingPlaceholderText() )
1112   {
1113     // Do not want to use the place-holder text to set the cursor position.
1114
1115     // Use the line's height of the font's family set to set the cursor's size.
1116     // If there is no font's family set, use the default font.
1117     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1118
1119     float lineHeight = 0.f;
1120
1121     FontId defaultFontId = 0u;
1122     if( NULL == mFontDefaults )
1123     {
1124       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1125                                              EMPTY_STRING );
1126     }
1127     else
1128     {
1129       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1130     }
1131
1132     Text::FontMetrics fontMetrics;
1133     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1134
1135     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1136
1137
1138     Vector2 cursorPosition;
1139
1140     switch( mLayoutEngine.GetHorizontalAlignment() )
1141     {
1142       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1143       {
1144         cursorPosition.x = 1.f;
1145         break;
1146       }
1147       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1148       {
1149         cursorPosition.x = floor( 0.5f * mControlSize.width );
1150         break;
1151       }
1152       case LayoutEngine::HORIZONTAL_ALIGN_END:
1153       {
1154         cursorPosition.x = mControlSize.width;
1155         break;
1156       }
1157     }
1158
1159     switch( mLayoutEngine.GetVerticalAlignment() )
1160     {
1161       case LayoutEngine::VERTICAL_ALIGN_TOP:
1162       {
1163         cursorPosition.y = 0.f;
1164         break;
1165       }
1166       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1167       {
1168         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1169         break;
1170       }
1171       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1172       {
1173         cursorPosition.y = mControlSize.height - lineHeight;
1174         break;
1175       }
1176     }
1177
1178     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1179                                          cursorPosition.x,
1180                                          cursorPosition.y,
1181                                          lineHeight,
1182                                          lineHeight );
1183   }
1184   else
1185   {
1186     CursorInfo cursorInfo;
1187     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1188                        cursorInfo );
1189
1190     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1191     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1192
1193     // Sets the cursor position.
1194     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1195                                          cursorPosition.x,
1196                                          cursorPosition.y,
1197                                          cursorInfo.primaryCursorHeight,
1198                                          cursorInfo.lineHeight );
1199     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1200
1201     // Sets the grab handle position.
1202     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1203                                          cursorPosition.x,
1204                                          cursorPosition.y,
1205                                          cursorInfo.lineHeight );
1206
1207     if( cursorInfo.isSecondaryCursor )
1208     {
1209       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1210       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1211                                            cursorInfo.secondaryPosition.x + offset.x,
1212                                            cursorInfo.secondaryPosition.y + offset.y,
1213                                            cursorInfo.secondaryCursorHeight,
1214                                            cursorInfo.lineHeight );
1215       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1216     }
1217     else
1218     {
1219       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1220     }
1221   }
1222   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1223 }
1224
1225 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1226 {
1227   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1228       ( RIGHT_SELECTION_HANDLE != handleType ) )
1229   {
1230     return;
1231   }
1232
1233   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1234   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1235
1236   CursorInfo cursorInfo;
1237   GetCursorPosition( index,
1238                      cursorInfo );
1239
1240   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1241   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1242
1243   // Sets the grab handle position.
1244   mEventData->mDecorator->SetPosition( handleType,
1245                                        cursorPosition.x,
1246                                        cursorPosition.y,
1247                                        cursorInfo.lineHeight );
1248 }
1249
1250 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1251 {
1252   // Clamp between -space & 0 (and the text alignment).
1253   if( actualSize.width > mControlSize.width )
1254   {
1255     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1256     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1257     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1258
1259     mEventData->mDecoratorUpdated = true;
1260   }
1261   else
1262   {
1263     mEventData->mScrollPosition.x = 0.f;
1264   }
1265 }
1266
1267 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1268 {
1269   // Clamp between -space & 0 (and the text alignment).
1270   if( actualSize.height > mControlSize.height )
1271   {
1272     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1273     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1274     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1275
1276     mEventData->mDecoratorUpdated = true;
1277   }
1278   else
1279   {
1280     mEventData->mScrollPosition.y = 0.f;
1281   }
1282 }
1283
1284 void Controller::Impl::ScrollToMakeCursorVisible()
1285 {
1286   if( NULL == mEventData )
1287   {
1288     // Nothing to do if there is no text input.
1289     return;
1290   }
1291
1292   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1293
1294   Vector2 offset;
1295   bool updateDecorator = false;
1296   if( primaryCursorPosition.x < 0.f )
1297   {
1298     offset.x = -primaryCursorPosition.x;
1299     mEventData->mScrollPosition.x += offset.x;
1300     updateDecorator = true;
1301   }
1302   else if( primaryCursorPosition.x > mControlSize.width )
1303   {
1304     offset.x = mControlSize.width - primaryCursorPosition.x;
1305     mEventData->mScrollPosition.x += offset.x;
1306     updateDecorator = true;
1307   }
1308
1309   if( updateDecorator && mEventData->mDecorator )
1310   {
1311     mEventData->mDecorator->UpdatePositions( offset );
1312   }
1313
1314   // TODO : calculate the vertical scroll.
1315 }
1316
1317 void Controller::Impl::ScrollTextToMatchCursor()
1318 {
1319   // Get the current cursor position in decorator coords.
1320   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1321
1322   // Calculate the new cursor position.
1323   CursorInfo cursorInfo;
1324   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1325                      cursorInfo );
1326
1327   // Calculate the offset to match the cursor position before the character was deleted.
1328   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1329
1330   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1331   bool updateCursorPosition = true;
1332
1333   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1334   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1335
1336   if( updateCursorPosition )
1337   {
1338     // Sets the cursor position.
1339     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1340                                          cursorPosition.x,
1341                                          cursorPosition.y,
1342                                          cursorInfo.primaryCursorHeight,
1343                                          cursorInfo.lineHeight );
1344
1345     // Sets the grab handle position.
1346     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1347                                          cursorPosition.x,
1348                                          cursorPosition.y,
1349                                          cursorInfo.lineHeight );
1350
1351     if( cursorInfo.isSecondaryCursor )
1352     {
1353       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1354       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1355                                            cursorInfo.secondaryPosition.x + offset.x,
1356                                            cursorInfo.secondaryPosition.y + offset.y,
1357                                            cursorInfo.secondaryCursorHeight,
1358                                            cursorInfo.lineHeight );
1359       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1360     }
1361     else
1362     {
1363       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1364     }
1365   }
1366 }
1367
1368 void Controller::Impl::RequestRelayout()
1369 {
1370   mControlInterface.RequestTextRelayout();
1371 }
1372
1373 } // namespace Text
1374
1375 } // namespace Toolkit
1376
1377 } // namespace Dali