2c59665530f3db844e6fd1a9b1435b6646a39edd
[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   return lineIndex-1;
773 }
774
775 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
776                                                         float visualY )
777 {
778   if( NULL == mEventData )
779   {
780     // Nothing to do if there is no text input.
781     return 0u;
782   }
783
784   CharacterIndex logicalIndex = 0u;
785
786   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
787   const Length numberOfLines  = mVisualModel->mLines.Count();
788   if( 0 == numberOfGlyphs ||
789       0 == numberOfLines )
790   {
791     return logicalIndex;
792   }
793
794   // Find which line is closest
795   const LineIndex lineIndex = GetClosestLine( visualY );
796   const LineRun& line = mVisualModel->mLines[lineIndex];
797
798   // Get the positions of the glyphs.
799   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
800   const Vector2* const positionsBuffer = positions.Begin();
801
802   // Get the visual to logical conversion tables.
803   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
804   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
805
806   // Get the character to glyph conversion table.
807   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
808
809   // Get the glyphs per character table.
810   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
811
812   // If the vector is void, there is no right to left characters.
813   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
814
815   const CharacterIndex startCharacter = line.characterRun.characterIndex;
816   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
817   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
818
819   // Whether there is a hit on a glyph.
820   bool matched = false;
821
822   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
823   CharacterIndex visualIndex = startCharacter;
824   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
825   {
826     // The character in logical order.
827     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
828
829     // The first glyph for that character in logical order.
830     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
831
832     // The number of glyphs for that character
833     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
834
835     // Get the metrics for the group of glyphs.
836     GlyphMetrics glyphMetrics;
837     GetGlyphsMetrics( glyphLogicalOrderIndex,
838                       numberOfGlyphs,
839                       glyphMetrics,
840                       mVisualModel,
841                       mFontClient );
842
843     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
844
845     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
846
847     if( visualX < glyphX )
848     {
849       matched = true;
850       break;
851     }
852   }
853
854   // Return the logical position of the cursor in characters.
855
856   if( !matched )
857   {
858     visualIndex = endCharacter;
859   }
860
861   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
862 }
863
864 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
865                                           CursorInfo& cursorInfo )
866 {
867   // TODO: Check for multiline with \n, etc...
868
869   // Check if the logical position is the first or the last one of the text.
870   const bool isFirstPosition = 0u == logical;
871   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
872
873   if( isFirstPosition && isLastPosition )
874   {
875     // There is zero characters. Get the default font.
876
877     FontId defaultFontId = 0u;
878     if( NULL == mFontDefaults )
879     {
880       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
881                                              EMPTY_STRING );
882     }
883     else
884     {
885       defaultFontId = mFontDefaults->GetFontId( mFontClient );
886     }
887
888     Text::FontMetrics fontMetrics;
889     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
890
891     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
892     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
893
894     cursorInfo.primaryPosition.x = 0.f;
895     cursorInfo.primaryPosition.y = 0.f;
896
897     // Nothing else to do.
898     return;
899   }
900
901   // Get the previous logical index.
902   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
903
904   // Decrease the logical index if it's the last one.
905   if( isLastPosition )
906   {
907     --logical;
908   }
909
910   // Get the direction of the character and the previous one.
911   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
912
913   CharacterDirection isCurrentRightToLeft = false;
914   CharacterDirection isPreviousRightToLeft = false;
915   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
916   {
917     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
918     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
919   }
920
921   // Get the line where the character is laid-out.
922   const LineRun* modelLines = mVisualModel->mLines.Begin();
923
924   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
925   const LineRun& line = *( modelLines + lineIndex );
926
927   // Get the paragraph's direction.
928   const CharacterDirection isRightToLeftParagraph = line.direction;
929
930   // Check whether there is an alternative position:
931
932   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
933     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
934
935   // Set the line height.
936   cursorInfo.lineHeight = line.ascender + -line.descender;
937
938   // Convert the cursor position into the glyph position.
939   CharacterIndex characterIndex = logical;
940   if( cursorInfo.isSecondaryCursor &&
941       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
942   {
943     characterIndex = previousLogical;
944   }
945
946   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
947   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
948   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
949
950   // Get the metrics for the group of glyphs.
951   GlyphMetrics glyphMetrics;
952   GetGlyphsMetrics( currentGlyphIndex,
953                     numberOfGlyphs,
954                     glyphMetrics,
955                     mVisualModel,
956                     mFontClient );
957
958   float interGlyphAdvance = 0.f;
959   if( !isLastPosition &&
960       ( numberOfCharacters > 1u ) )
961   {
962     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
963     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
964   }
965
966   // Get the glyph position and x bearing.
967   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
968
969   // Set the cursor's height.
970   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
971
972   // Set the position.
973   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
974   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
975
976   if( isLastPosition )
977   {
978     // The position of the cursor after the last character needs special
979     // care depending on its direction and the direction of the paragraph.
980
981     if( cursorInfo.isSecondaryCursor )
982     {
983       // Need to find the first character after the last character with the paragraph's direction.
984       // i.e l0 l1 l2 r0 r1 should find r0.
985
986       // TODO: check for more than one line!
987       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
988       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
989
990       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
991       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
992
993       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
994
995       // Get the metrics for the group of glyphs.
996       GlyphMetrics glyphMetrics;
997       GetGlyphsMetrics( glyphIndex,
998                         numberOfGlyphs,
999                         glyphMetrics,
1000                         mVisualModel,
1001                         mFontClient );
1002
1003       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1004
1005       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1006     }
1007     else
1008     {
1009       if( !isCurrentRightToLeft )
1010       {
1011         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1012       }
1013       else
1014       {
1015         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1016       }
1017     }
1018   }
1019
1020   // Set the alternative cursor position.
1021   if( cursorInfo.isSecondaryCursor )
1022   {
1023     // Convert the cursor position into the glyph position.
1024     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1025     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1026     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1027
1028     // Get the glyph position.
1029     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1030
1031     // Get the metrics for the group of glyphs.
1032     GlyphMetrics glyphMetrics;
1033     GetGlyphsMetrics( previousGlyphIndex,
1034                       numberOfGlyphs,
1035                       glyphMetrics,
1036                       mVisualModel,
1037                       mFontClient );
1038
1039     // Set the cursor position and height.
1040     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1041                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1042
1043     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1044
1045     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1046
1047     // Update the primary cursor height as well.
1048     cursorInfo.primaryCursorHeight *= 0.5f;
1049   }
1050 }
1051
1052 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1053 {
1054   if( NULL == mEventData )
1055   {
1056     // Nothing to do if there is no text input.
1057     return 0u;
1058   }
1059
1060   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1061
1062   const Script script = mLogicalModel->GetScript( index );
1063   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1064   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1065
1066   Length numberOfCharacters = 0u;
1067   if( TextAbstraction::LATIN == script )
1068   {
1069     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1070     numberOfCharacters = 1u;
1071   }
1072   else
1073   {
1074     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1075     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1076
1077     while( 0u == numberOfCharacters )
1078     {
1079       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1080       ++glyphIndex;
1081     }
1082   }
1083
1084   if( index < mEventData->mPrimaryCursorPosition )
1085   {
1086     cursorIndex -= numberOfCharacters;
1087   }
1088   else
1089   {
1090     cursorIndex += numberOfCharacters;
1091   }
1092
1093   return cursorIndex;
1094 }
1095
1096 void Controller::Impl::UpdateCursorPosition()
1097 {
1098   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1099   if( NULL == mEventData )
1100   {
1101     // Nothing to do if there is no text input.
1102     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1103     return;
1104   }
1105
1106   if( IsShowingPlaceholderText() )
1107   {
1108     // Do not want to use the place-holder text to set the cursor position.
1109
1110     // Use the line's height of the font's family set to set the cursor's size.
1111     // If there is no font's family set, use the default font.
1112     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1113
1114     float lineHeight = 0.f;
1115
1116     FontId defaultFontId = 0u;
1117     if( NULL == mFontDefaults )
1118     {
1119       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1120                                              EMPTY_STRING );
1121     }
1122     else
1123     {
1124       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1125     }
1126
1127     Text::FontMetrics fontMetrics;
1128     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1129
1130     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1131
1132
1133     Vector2 cursorPosition;
1134
1135     switch( mLayoutEngine.GetHorizontalAlignment() )
1136     {
1137       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1138       {
1139         cursorPosition.x = 1.f;
1140         break;
1141       }
1142       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1143       {
1144         cursorPosition.x = floor( 0.5f * mControlSize.width );
1145         break;
1146       }
1147       case LayoutEngine::HORIZONTAL_ALIGN_END:
1148       {
1149         cursorPosition.x = mControlSize.width;
1150         break;
1151       }
1152     }
1153
1154     switch( mLayoutEngine.GetVerticalAlignment() )
1155     {
1156       case LayoutEngine::VERTICAL_ALIGN_TOP:
1157       {
1158         cursorPosition.y = 0.f;
1159         break;
1160       }
1161       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1162       {
1163         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1164         break;
1165       }
1166       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1167       {
1168         cursorPosition.y = mControlSize.height - lineHeight;
1169         break;
1170       }
1171     }
1172
1173     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1174                                          cursorPosition.x,
1175                                          cursorPosition.y,
1176                                          lineHeight,
1177                                          lineHeight );
1178   }
1179   else
1180   {
1181     CursorInfo cursorInfo;
1182     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1183                        cursorInfo );
1184
1185     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1186     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1187
1188     // Sets the cursor position.
1189     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1190                                          cursorPosition.x,
1191                                          cursorPosition.y,
1192                                          cursorInfo.primaryCursorHeight,
1193                                          cursorInfo.lineHeight );
1194     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1195
1196     // Sets the grab handle position.
1197     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1198                                          cursorPosition.x,
1199                                          cursorPosition.y,
1200                                          cursorInfo.lineHeight );
1201
1202     if( cursorInfo.isSecondaryCursor )
1203     {
1204       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1205       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1206                                            cursorInfo.secondaryPosition.x + offset.x,
1207                                            cursorInfo.secondaryPosition.y + offset.y,
1208                                            cursorInfo.secondaryCursorHeight,
1209                                            cursorInfo.lineHeight );
1210       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1211     }
1212     else
1213     {
1214       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1215     }
1216   }
1217   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1218 }
1219
1220 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1221 {
1222   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1223       ( RIGHT_SELECTION_HANDLE != handleType ) )
1224   {
1225     return;
1226   }
1227
1228   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1229   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1230
1231   CursorInfo cursorInfo;
1232   GetCursorPosition( index,
1233                      cursorInfo );
1234
1235   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1236   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1237
1238   // Sets the grab handle position.
1239   mEventData->mDecorator->SetPosition( handleType,
1240                                        cursorPosition.x,
1241                                        cursorPosition.y,
1242                                        cursorInfo.lineHeight );
1243 }
1244
1245 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1246 {
1247   // Clamp between -space & 0 (and the text alignment).
1248   if( actualSize.width > mControlSize.width )
1249   {
1250     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1251     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1252     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1253
1254     mEventData->mDecoratorUpdated = true;
1255   }
1256   else
1257   {
1258     mEventData->mScrollPosition.x = 0.f;
1259   }
1260 }
1261
1262 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1263 {
1264   // Clamp between -space & 0 (and the text alignment).
1265   if( actualSize.height > mControlSize.height )
1266   {
1267     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1268     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1269     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1270
1271     mEventData->mDecoratorUpdated = true;
1272   }
1273   else
1274   {
1275     mEventData->mScrollPosition.y = 0.f;
1276   }
1277 }
1278
1279 void Controller::Impl::ScrollToMakeCursorVisible()
1280 {
1281   if( NULL == mEventData )
1282   {
1283     // Nothing to do if there is no text input.
1284     return;
1285   }
1286
1287   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1288
1289   Vector2 offset;
1290   bool updateDecorator = false;
1291   if( primaryCursorPosition.x < 0.f )
1292   {
1293     offset.x = -primaryCursorPosition.x;
1294     mEventData->mScrollPosition.x += offset.x;
1295     updateDecorator = true;
1296   }
1297   else if( primaryCursorPosition.x > mControlSize.width )
1298   {
1299     offset.x = mControlSize.width - primaryCursorPosition.x;
1300     mEventData->mScrollPosition.x += offset.x;
1301     updateDecorator = true;
1302   }
1303
1304   if( updateDecorator && mEventData->mDecorator )
1305   {
1306     mEventData->mDecorator->UpdatePositions( offset );
1307   }
1308
1309   // TODO : calculate the vertical scroll.
1310 }
1311
1312 void Controller::Impl::ScrollTextToMatchCursor()
1313 {
1314   // Get the current cursor position in decorator coords.
1315   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1316
1317   // Calculate the new cursor position.
1318   CursorInfo cursorInfo;
1319   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1320                      cursorInfo );
1321
1322   // Calculate the offset to match the cursor position before the character was deleted.
1323   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1324
1325   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1326   bool updateCursorPosition = true;
1327
1328   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1329   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1330
1331   if( updateCursorPosition )
1332   {
1333     // Sets the cursor position.
1334     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1335                                          cursorPosition.x,
1336                                          cursorPosition.y,
1337                                          cursorInfo.primaryCursorHeight,
1338                                          cursorInfo.lineHeight );
1339
1340     // Sets the grab handle position.
1341     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1342                                          cursorPosition.x,
1343                                          cursorPosition.y,
1344                                          cursorInfo.lineHeight );
1345
1346     if( cursorInfo.isSecondaryCursor )
1347     {
1348       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1349       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1350                                            cursorInfo.secondaryPosition.x + offset.x,
1351                                            cursorInfo.secondaryPosition.y + offset.y,
1352                                            cursorInfo.secondaryCursorHeight,
1353                                            cursorInfo.lineHeight );
1354       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1355     }
1356     else
1357     {
1358       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1359     }
1360   }
1361 }
1362
1363 void Controller::Impl::RequestRelayout()
1364 {
1365   mControlInterface.RequestTextRelayout();
1366 }
1367
1368 } // namespace Text
1369
1370 } // namespace Toolkit
1371
1372 } // namespace Dali