f41bdddad64b15af4a19ed0bb847fe822f8565c0
[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( true ),
133   mHorizontalScrollingEnabled( true ),
134   mVerticalScrollingEnabled( false ),
135   mUpdateCursorPosition( false ),
136   mUpdateLeftSelectionPosition( false ),
137   mUpdateRightSelectionPosition( false ),
138   mScrollAfterUpdatePosition( 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->mScrollAfterUpdatePosition )
197     {
198       const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
199
200       ScrollToMakePositionVisible( primaryCursorPosition );
201       mEventData->mScrollAfterUpdatePosition = false;
202     }
203
204     mEventData->mDecoratorUpdated = true;
205     mEventData->mUpdateCursorPosition = false;
206   }
207   else if( mEventData->mScrollAfterDelete )
208   {
209     ScrollTextToMatchCursor();
210     mEventData->mDecoratorUpdated = true;
211     mEventData->mScrollAfterDelete = false;
212   }
213   else
214   {
215     bool leftScroll = false;
216     bool rightScroll = false;
217
218     if( mEventData->mUpdateLeftSelectionPosition )
219     {
220       UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
221
222       if( mEventData->mScrollAfterUpdatePosition )
223       {
224         const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
225
226         ScrollToMakePositionVisible( leftHandlePosition );
227         leftScroll = true;
228       }
229
230       mEventData->mDecoratorUpdated = true;
231       mEventData->mUpdateLeftSelectionPosition = false;
232     }
233
234     if( mEventData->mUpdateRightSelectionPosition )
235     {
236       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
237
238       if( mEventData->mScrollAfterUpdatePosition )
239       {
240         const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
241
242         ScrollToMakePositionVisible( rightHandlePosition );
243         rightScroll = true;
244       }
245
246       mEventData->mDecoratorUpdated = true;
247       mEventData->mUpdateRightSelectionPosition = false;
248     }
249
250     if( leftScroll || rightScroll )
251     {
252       mEventData->mScrollAfterUpdatePosition = false;
253     }
254   }
255
256   mEventData->mEventQueue.clear();
257
258   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
259
260   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
261   mEventData->mDecoratorUpdated = false;
262
263   return decoratorUpdated;
264 }
265
266 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
267 {
268   // Calculate the operations to be done.
269   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
270
271   Vector<Character>& utf32Characters = mLogicalModel->mText;
272
273   const Length numberOfCharacters = utf32Characters.Count();
274
275   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
276   if( GET_LINE_BREAKS & operations )
277   {
278     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
279     // calculate the bidirectional info for each 'paragraph'.
280     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
281     // is not shaped together).
282     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
283
284     SetLineBreakInfo( utf32Characters,
285                       lineBreakInfo );
286   }
287
288   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
289   if( GET_WORD_BREAKS & operations )
290   {
291     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
292     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
293
294     SetWordBreakInfo( utf32Characters,
295                       wordBreakInfo );
296   }
297
298   const bool getScripts = GET_SCRIPTS & operations;
299   const bool validateFonts = VALIDATE_FONTS & operations;
300
301   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
302   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
303
304   if( getScripts || validateFonts )
305   {
306     // Validates the fonts assigned by the application or assigns default ones.
307     // It makes sure all the characters are going to be rendered by the correct font.
308     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
309
310     if( getScripts )
311     {
312       // Retrieves the scripts used in the text.
313       multilanguageSupport.SetScripts( utf32Characters,
314                                        lineBreakInfo,
315                                        scripts );
316     }
317
318     if( validateFonts )
319     {
320       if( 0u == validFonts.Count() )
321       {
322         // Copy the requested font defaults received via the property system.
323         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
324         GetDefaultFonts( validFonts, numberOfCharacters );
325       }
326
327       // Validates the fonts. If there is a character with no assigned font it sets a default one.
328       // After this call, fonts are validated.
329       multilanguageSupport.ValidateFonts( utf32Characters,
330                                           scripts,
331                                           validFonts );
332     }
333   }
334
335   Vector<Character> mirroredUtf32Characters;
336   bool textMirrored = false;
337   if( BIDI_INFO & operations )
338   {
339     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
340     // bidirectional info.
341
342     Length numberOfParagraphs = 0u;
343
344     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
345     for( Length index = 0u; index < numberOfCharacters; ++index )
346     {
347       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
348       {
349         ++numberOfParagraphs;
350       }
351     }
352
353     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
354     bidirectionalInfo.Reserve( numberOfParagraphs );
355
356     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
357     SetBidirectionalInfo( utf32Characters,
358                           scripts,
359                           lineBreakInfo,
360                           bidirectionalInfo );
361
362     if( 0u != bidirectionalInfo.Count() )
363     {
364       // This paragraph has right to left text. Some characters may need to be mirrored.
365       // TODO: consider if the mirrored string can be stored as well.
366
367       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
368
369       // Only set the character directions if there is right to left characters.
370       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
371       directions.Resize( numberOfCharacters );
372
373       GetCharactersDirection( bidirectionalInfo,
374                               directions );
375     }
376     else
377     {
378       // There is no right to left characters. Clear the directions vector.
379       mLogicalModel->mCharacterDirections.Clear();
380     }
381
382    }
383
384   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
385   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
386   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
387   if( SHAPE_TEXT & operations )
388   {
389     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
390     // Shapes the text.
391     ShapeText( textToShape,
392                lineBreakInfo,
393                scripts,
394                validFonts,
395                glyphs,
396                glyphsToCharactersMap,
397                charactersPerGlyph );
398
399     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
400     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
401     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
402   }
403
404   const Length numberOfGlyphs = glyphs.Count();
405
406   if( GET_GLYPH_METRICS & operations )
407   {
408     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
409   }
410 }
411
412 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
413 {
414   if( mFontDefaults )
415   {
416     FontRun fontRun;
417     fontRun.characterRun.characterIndex = 0;
418     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
419     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
420     fontRun.isDefault = true;
421
422     fonts.PushBack( fontRun );
423   }
424 }
425
426 void Controller::Impl::OnCursorKeyEvent( const Event& event )
427 {
428   if( NULL == mEventData )
429   {
430     // Nothing to do if there is no text input.
431     return;
432   }
433
434   int keyCode = event.p1.mInt;
435
436   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
437   {
438     if( mEventData->mPrimaryCursorPosition > 0u )
439     {
440       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
441     }
442   }
443   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
444   {
445     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
446     {
447       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
448     }
449   }
450   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
451   {
452     // TODO
453   }
454   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
455   {
456     // TODO
457   }
458
459   mEventData->mUpdateCursorPosition = true;
460   mEventData->mScrollAfterUpdatePosition = true;
461 }
462
463 void Controller::Impl::OnTapEvent( const Event& event )
464 {
465   if( NULL != mEventData )
466   {
467     const unsigned int tapCount = event.p1.mUint;
468
469     if( 1u == tapCount )
470     {
471       if( ! IsShowingPlaceholderText() )
472       {
473         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
474         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
475
476         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
477                                                                     yPosition );
478       }
479       else
480       {
481         mEventData->mPrimaryCursorPosition = 0u;
482       }
483
484       mEventData->mUpdateCursorPosition = true;
485       mEventData->mScrollAfterUpdatePosition = true;
486     }
487     else if( mEventData->mSelectionEnabled &&
488              ( 2u == tapCount ) )
489     {
490       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
491       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
492       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
493
494       RepositionSelectionHandles( xPosition,
495                                   yPosition );
496
497       mEventData->mScrollAfterUpdatePosition = true;
498       mEventData->mUpdateLeftSelectionPosition = true;
499       mEventData->mUpdateRightSelectionPosition = true;
500     }
501   }
502 }
503
504 void Controller::Impl::OnPanEvent( const Event& event )
505 {
506   if( NULL == mEventData )
507   {
508     // Nothing to do if there is no text input.
509     return;
510   }
511
512   int state = event.p1.mInt;
513
514   if( Gesture::Started    == state ||
515       Gesture::Continuing == state )
516   {
517     const Vector2& actualSize = mVisualModel->GetActualSize();
518     const Vector2 currentScroll = mEventData->mScrollPosition;
519
520     if( mEventData->mHorizontalScrollingEnabled )
521     {
522       const float displacementX = event.p2.mFloat;
523       mEventData->mScrollPosition.x += displacementX;
524
525       ClampHorizontalScroll( actualSize );
526     }
527
528     if( mEventData->mVerticalScrollingEnabled )
529     {
530       const float displacementY = event.p3.mFloat;
531       mEventData->mScrollPosition.y += displacementY;
532
533       ClampVerticalScroll( actualSize );
534     }
535
536     if( mEventData->mDecorator )
537     {
538       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
539     }
540   }
541 }
542
543 void Controller::Impl::OnHandleEvent( const Event& event )
544 {
545   if( NULL == mEventData )
546   {
547     // Nothing to do if there is no text input.
548     return;
549   }
550
551   const unsigned int state = event.p1.mUint;
552   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
553
554   if( HANDLE_PRESSED == state )
555   {
556     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
557     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
558     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
559
560     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
561
562     if( Event::GRAB_HANDLE_EVENT == event.type )
563     {
564       ChangeState ( EventData::GRAB_HANDLE_PANNING );
565
566       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
567       {
568         mEventData->mPrimaryCursorPosition = handleNewPosition;
569         mEventData->mUpdateCursorPosition = true;
570       }
571     }
572     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
573     {
574       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
575
576       if( handleNewPosition != mEventData->mLeftSelectionPosition )
577       {
578         mEventData->mLeftSelectionPosition = handleNewPosition;
579
580         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
581                                     mEventData->mRightSelectionPosition );
582
583         mEventData->mUpdateLeftSelectionPosition = true;
584       }
585     }
586     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
587     {
588       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
589
590       if( handleNewPosition != mEventData->mRightSelectionPosition )
591       {
592         mEventData->mRightSelectionPosition = handleNewPosition;
593
594         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
595                                     mEventData->mRightSelectionPosition );
596
597         mEventData->mUpdateRightSelectionPosition = true;
598       }
599     }
600   } // end ( HANDLE_PRESSED == state )
601   else if( ( HANDLE_RELEASED == state ) ||
602            handleStopScrolling )
603   {
604     CharacterIndex handlePosition = 0u;
605     if( handleStopScrolling )
606     {
607       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
608       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
609       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
610
611       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
612     }
613
614     if( Event::GRAB_HANDLE_EVENT == event.type )
615     {
616       mEventData->mUpdateCursorPosition = true;
617
618       ChangeState( EventData::EDITING_WITH_POPUP );
619
620       if( handleStopScrolling )
621       {
622         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
623         mEventData->mPrimaryCursorPosition = handlePosition;
624       }
625     }
626     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
627     {
628       ChangeState( EventData::SELECTING );
629
630       if( handleStopScrolling )
631       {
632         mEventData->mUpdateLeftSelectionPosition = mEventData->mLeftSelectionPosition != handlePosition;
633         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
634         mEventData->mLeftSelectionPosition = handlePosition;
635
636         if( mEventData->mUpdateLeftSelectionPosition )
637         {
638           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
639                                       mEventData->mRightSelectionPosition );
640         }
641       }
642     }
643     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
644     {
645       ChangeState( EventData::SELECTING );
646
647       if( handleStopScrolling )
648       {
649         mEventData->mUpdateRightSelectionPosition = mEventData->mRightSelectionPosition != handlePosition;
650         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
651         mEventData->mRightSelectionPosition = handlePosition;
652
653         if( mEventData->mUpdateRightSelectionPosition )
654         {
655           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
656                                       mEventData->mRightSelectionPosition );
657         }
658       }
659     }
660
661     mEventData->mDecoratorUpdated = true;
662   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
663   else if( HANDLE_SCROLLING == state )
664   {
665     const float xSpeed = event.p2.mFloat;
666     const Vector2& actualSize = mVisualModel->GetActualSize();
667
668     mEventData->mScrollPosition.x += xSpeed;
669
670     ClampHorizontalScroll( actualSize );
671
672     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
673     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
674
675     if( Event::GRAB_HANDLE_EVENT == event.type )
676     {
677       ChangeState( EventData::GRAB_HANDLE_PANNING );
678     }
679     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
680     {
681       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
682       //       Think if something can be done to save power.
683
684       ChangeState( EventData::SELECTION_HANDLE_PANNING );
685
686       const Vector2& position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
687
688       // Get the new handle position.
689       // The selection handle's position is in decorator coords. Need to transforms to text coords.
690       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
691                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
692
693       if( leftSelectionHandleEvent )
694       {
695         mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
696         mEventData->mLeftSelectionPosition = handlePosition;
697       }
698       else
699       {
700         mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
701         mEventData->mRightSelectionPosition = handlePosition;
702       }
703
704       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
705       {
706         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
707                                     mEventData->mRightSelectionPosition );
708       }
709     }
710     mEventData->mDecoratorUpdated = true;
711   } // end ( HANDLE_SCROLLING == state )
712 }
713
714 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
715 {
716   mEventData->mDecorator->ClearHighlights();
717
718   mEventData->mLeftSelectionPosition = selectionStart;
719   mEventData->mRightSelectionPosition = selectionEnd;
720
721   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
722   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
723   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
724   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
725
726   // TODO: Better algorithm to create the highlight box.
727   // TODO: Multi-line.
728
729   const Vector<LineRun>& lines = mVisualModel->mLines;
730   const float height = lines[0].ascender + -lines[0].descender;
731
732   const bool indicesSwapped = selectionStart > selectionEnd;
733   if( indicesSwapped )
734   {
735     std::swap( selectionStart, selectionEnd );
736   }
737
738   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
739   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
740
741   mEventData->mDecorator->SwapSelectionHandlesEnabled( indicesSwapped );
742
743   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
744
745   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
746   {
747     const GlyphInfo& glyph = *( glyphsBuffer + index );
748     const Vector2& position = *( positionsBuffer + index );
749
750     const float xPosition = position.x - glyph.xBearing + offset.x;
751     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
752   }
753
754   CursorInfo primaryCursorInfo;
755   GetCursorPosition( mEventData->mLeftSelectionPosition,
756                      primaryCursorInfo );
757
758   CursorInfo secondaryCursorInfo;
759   GetCursorPosition( mEventData->mRightSelectionPosition,
760                      secondaryCursorInfo );
761
762   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
763   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
764
765   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
766
767   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
768
769   // Set the flag to update the decorator.
770   mEventData->mDecoratorUpdated = true;
771 }
772
773 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
774 {
775   if( NULL == mEventData )
776   {
777     // Nothing to do if there is no text input.
778     return;
779   }
780
781   if( IsShowingPlaceholderText() )
782   {
783     // Nothing to do if there is the place-holder text.
784     return;
785   }
786
787   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
788   const Length numberOfLines  = mVisualModel->mLines.Count();
789   if( 0 == numberOfGlyphs ||
790       0 == numberOfLines )
791   {
792     // Nothing to do if there is no text.
793     return;
794   }
795
796   // Find which word was selected
797   CharacterIndex selectionStart( 0 );
798   CharacterIndex selectionEnd( 0 );
799   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
800   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
801
802   if( selectionStart == selectionEnd )
803   {
804     ChangeState( EventData::EDITING );
805     // Nothing to select. i.e. a white space, out of bounds
806     return;
807   }
808
809   RepositionSelectionHandles( selectionStart, selectionEnd );
810 }
811
812 void Controller::Impl::ChangeState( EventData::State newState )
813 {
814   if( NULL == mEventData )
815   {
816     // Nothing to do if there is no text input.
817     return;
818   }
819
820   if( mEventData->mState != newState )
821   {
822     mEventData->mState = newState;
823
824     if( EventData::INACTIVE == mEventData->mState )
825     {
826       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
827       mEventData->mDecorator->StopCursorBlink();
828       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
829       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
830       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
831       mEventData->mDecorator->SetPopupActive( false );
832       mEventData->mDecoratorUpdated = true;
833     }
834     else if ( EventData::SELECTING == mEventData->mState )
835     {
836       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
837       mEventData->mDecorator->StopCursorBlink();
838       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
839       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
840       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
841       if( mEventData->mGrabHandlePopupEnabled )
842       {
843         mEventData->mDecorator->SetPopupActive( true );
844       }
845       mEventData->mDecoratorUpdated = true;
846     }
847     else if( EventData::EDITING == mEventData->mState )
848     {
849       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
850       if( mEventData->mCursorBlinkEnabled )
851       {
852         mEventData->mDecorator->StartCursorBlink();
853       }
854       // Grab handle is not shown until a tap is received whilst EDITING
855       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
856       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
857       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
858       if( mEventData->mGrabHandlePopupEnabled )
859       {
860         mEventData->mDecorator->SetPopupActive( false );
861       }
862       mEventData->mDecoratorUpdated = true;
863     }
864     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
865     {
866       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
867       if( mEventData->mCursorBlinkEnabled )
868       {
869         mEventData->mDecorator->StartCursorBlink();
870       }
871       if( mEventData->mSelectionEnabled )
872       {
873         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
874         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
875       }
876       else
877       {
878         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
879       }
880       if( mEventData->mGrabHandlePopupEnabled )
881       {
882         mEventData->mDecorator->SetPopupActive( true );
883       }
884       mEventData->mDecoratorUpdated = true;
885     }
886     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
887     {
888       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
889       mEventData->mDecorator->StopCursorBlink();
890       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
891       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
892       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
893       if( mEventData->mGrabHandlePopupEnabled )
894       {
895         mEventData->mDecorator->SetPopupActive( false );
896       }
897       mEventData->mDecoratorUpdated = true;
898     }
899     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
900     {
901       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
902       if( mEventData->mCursorBlinkEnabled )
903       {
904         mEventData->mDecorator->StartCursorBlink();
905       }
906       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
907       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
908       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
909       if( mEventData->mGrabHandlePopupEnabled )
910       {
911         mEventData->mDecorator->SetPopupActive( false );
912       }
913       mEventData->mDecoratorUpdated = true;
914     }
915   }
916 }
917
918 LineIndex Controller::Impl::GetClosestLine( float y ) const
919 {
920   float totalHeight = 0.f;
921   LineIndex lineIndex = 0u;
922
923   const Vector<LineRun>& lines = mVisualModel->mLines;
924   for( LineIndex endLine = lines.Count();
925        lineIndex < endLine;
926        ++lineIndex )
927   {
928     const LineRun& lineRun = lines[lineIndex];
929     totalHeight += lineRun.ascender + -lineRun.descender;
930     if( y < totalHeight )
931     {
932       return lineIndex;
933     }
934   }
935
936   if( lineIndex == 0 )
937   {
938     return 0;
939   }
940
941   return lineIndex-1;
942 }
943
944 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
945 {
946   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
947   if( hitCharacter >= mLogicalModel->mText.Count() )
948   {
949     // Selection out of bounds.
950     return;
951   }
952
953   startIndex = hitCharacter;
954   endIndex = hitCharacter;
955
956   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
957   {
958     // Find the start and end of the text
959     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
960     {
961       Character charCode = mLogicalModel->mText[ startIndex-1 ];
962       if( TextAbstraction::IsWhiteSpace( charCode ) )
963       {
964         break;
965       }
966     }
967     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
968     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
969     {
970       Character charCode = mLogicalModel->mText[ endIndex ];
971       if( TextAbstraction::IsWhiteSpace( charCode ) )
972       {
973         break;
974       }
975     }
976   }
977 }
978
979 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
980                                                         float visualY )
981 {
982   if( NULL == mEventData )
983   {
984     // Nothing to do if there is no text input.
985     return 0u;
986   }
987
988   CharacterIndex logicalIndex = 0u;
989
990   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
991   const Length numberOfLines  = mVisualModel->mLines.Count();
992   if( 0 == numberOfGlyphs ||
993       0 == numberOfLines )
994   {
995     return logicalIndex;
996   }
997
998   // Find which line is closest
999   const LineIndex lineIndex = GetClosestLine( visualY );
1000   const LineRun& line = mVisualModel->mLines[lineIndex];
1001
1002   // Get the positions of the glyphs.
1003   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1004   const Vector2* const positionsBuffer = positions.Begin();
1005
1006   // Get the visual to logical conversion tables.
1007   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1008   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1009
1010   // Get the character to glyph conversion table.
1011   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1012
1013   // Get the glyphs per character table.
1014   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1015
1016   // If the vector is void, there is no right to left characters.
1017   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1018
1019   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1020   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1021   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1022
1023   // Whether there is a hit on a glyph.
1024   bool matched = false;
1025
1026   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1027   CharacterIndex visualIndex = startCharacter;
1028   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1029   {
1030     // The character in logical order.
1031     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1032
1033     // The first glyph for that character in logical order.
1034     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1035
1036     // The number of glyphs for that character
1037     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1038
1039     // Get the metrics for the group of glyphs.
1040     GlyphMetrics glyphMetrics;
1041     GetGlyphsMetrics( glyphLogicalOrderIndex,
1042                       numberOfGlyphs,
1043                       glyphMetrics,
1044                       mVisualModel,
1045                       mFontClient );
1046
1047     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1048
1049     // Find the mid-point of the area containing the glyph
1050     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1051
1052     if( visualX < glyphCenter )
1053     {
1054       matched = true;
1055       break;
1056     }
1057   }
1058
1059   // Return the logical position of the cursor in characters.
1060
1061   if( !matched )
1062   {
1063     visualIndex = endCharacter;
1064   }
1065
1066   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1067   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1068   return logicalIndex;
1069 }
1070
1071 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1072                                           CursorInfo& cursorInfo )
1073 {
1074   // TODO: Check for multiline with \n, etc...
1075
1076   // Check if the logical position is the first or the last one of the text.
1077   const bool isFirstPosition = 0u == logical;
1078   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
1079
1080   if( isFirstPosition && isLastPosition )
1081   {
1082     // There is zero characters. Get the default font.
1083
1084     FontId defaultFontId = 0u;
1085     if( NULL == mFontDefaults )
1086     {
1087       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1088                                              EMPTY_STRING );
1089     }
1090     else
1091     {
1092       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1093     }
1094
1095     Text::FontMetrics fontMetrics;
1096     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1097
1098     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
1099     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1100
1101     cursorInfo.primaryPosition.x = 0.f;
1102     cursorInfo.primaryPosition.y = 0.f;
1103
1104     // Nothing else to do.
1105     return;
1106   }
1107
1108   // Get the previous logical index.
1109   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1110
1111   // Decrease the logical index if it's the last one.
1112   if( isLastPosition )
1113   {
1114     --logical;
1115   }
1116
1117   // Get the direction of the character and the previous one.
1118   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1119
1120   CharacterDirection isCurrentRightToLeft = false;
1121   CharacterDirection isPreviousRightToLeft = false;
1122   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1123   {
1124     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1125     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1126   }
1127
1128   // Get the line where the character is laid-out.
1129   const LineRun* modelLines = mVisualModel->mLines.Begin();
1130
1131   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1132   const LineRun& line = *( modelLines + lineIndex );
1133
1134   // Get the paragraph's direction.
1135   const CharacterDirection isRightToLeftParagraph = line.direction;
1136
1137   // Check whether there is an alternative position:
1138
1139   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1140     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1141
1142   // Set the line height.
1143   cursorInfo.lineHeight = line.ascender + -line.descender;
1144
1145   // Convert the cursor position into the glyph position.
1146   CharacterIndex characterIndex = logical;
1147   if( cursorInfo.isSecondaryCursor &&
1148       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1149   {
1150     characterIndex = previousLogical;
1151   }
1152
1153   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1154   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1155   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1156
1157   // Get the metrics for the group of glyphs.
1158   GlyphMetrics glyphMetrics;
1159   GetGlyphsMetrics( currentGlyphIndex,
1160                     numberOfGlyphs,
1161                     glyphMetrics,
1162                     mVisualModel,
1163                     mFontClient );
1164
1165   float interGlyphAdvance = 0.f;
1166   if( !isLastPosition &&
1167       ( numberOfCharacters > 1u ) )
1168   {
1169     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1170     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1171   }
1172
1173   // Get the glyph position and x bearing.
1174   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1175
1176   // Set the cursor's height.
1177   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1178
1179   // Set the position.
1180   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1181   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1182
1183   if( isLastPosition )
1184   {
1185     // The position of the cursor after the last character needs special
1186     // care depending on its direction and the direction of the paragraph.
1187
1188     if( cursorInfo.isSecondaryCursor )
1189     {
1190       // Need to find the first character after the last character with the paragraph's direction.
1191       // i.e l0 l1 l2 r0 r1 should find r0.
1192
1193       // TODO: check for more than one line!
1194       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1195       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1196
1197       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1198       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1199
1200       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1201
1202       // Get the metrics for the group of glyphs.
1203       GlyphMetrics glyphMetrics;
1204       GetGlyphsMetrics( glyphIndex,
1205                         numberOfGlyphs,
1206                         glyphMetrics,
1207                         mVisualModel,
1208                         mFontClient );
1209
1210       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1211
1212       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1213     }
1214     else
1215     {
1216       if( !isCurrentRightToLeft )
1217       {
1218         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1219       }
1220       else
1221       {
1222         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1223       }
1224     }
1225   }
1226
1227   // Set the alternative cursor position.
1228   if( cursorInfo.isSecondaryCursor )
1229   {
1230     // Convert the cursor position into the glyph position.
1231     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1232     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1233     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1234
1235     // Get the glyph position.
1236     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1237
1238     // Get the metrics for the group of glyphs.
1239     GlyphMetrics glyphMetrics;
1240     GetGlyphsMetrics( previousGlyphIndex,
1241                       numberOfGlyphs,
1242                       glyphMetrics,
1243                       mVisualModel,
1244                       mFontClient );
1245
1246     // Set the cursor position and height.
1247     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1248                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1249
1250     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1251
1252     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1253
1254     // Update the primary cursor height as well.
1255     cursorInfo.primaryCursorHeight *= 0.5f;
1256   }
1257 }
1258
1259 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1260 {
1261   if( NULL == mEventData )
1262   {
1263     // Nothing to do if there is no text input.
1264     return 0u;
1265   }
1266
1267   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1268
1269   const Script script = mLogicalModel->GetScript( index );
1270   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1271   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1272
1273   Length numberOfCharacters = 0u;
1274   if( TextAbstraction::LATIN == script )
1275   {
1276     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1277     numberOfCharacters = 1u;
1278   }
1279   else
1280   {
1281     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1282     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1283
1284     while( 0u == numberOfCharacters )
1285     {
1286       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1287       ++glyphIndex;
1288     }
1289   }
1290
1291   if( index < mEventData->mPrimaryCursorPosition )
1292   {
1293     cursorIndex -= numberOfCharacters;
1294   }
1295   else
1296   {
1297     cursorIndex += numberOfCharacters;
1298   }
1299
1300   return cursorIndex;
1301 }
1302
1303 void Controller::Impl::UpdateCursorPosition()
1304 {
1305   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1306   if( NULL == mEventData )
1307   {
1308     // Nothing to do if there is no text input.
1309     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1310     return;
1311   }
1312
1313   if( IsShowingPlaceholderText() )
1314   {
1315     // Do not want to use the place-holder text to set the cursor position.
1316
1317     // Use the line's height of the font's family set to set the cursor's size.
1318     // If there is no font's family set, use the default font.
1319     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1320
1321     float lineHeight = 0.f;
1322
1323     FontId defaultFontId = 0u;
1324     if( NULL == mFontDefaults )
1325     {
1326       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1327                                              EMPTY_STRING );
1328     }
1329     else
1330     {
1331       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1332     }
1333
1334     Text::FontMetrics fontMetrics;
1335     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1336
1337     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1338
1339
1340     Vector2 cursorPosition;
1341
1342     switch( mLayoutEngine.GetHorizontalAlignment() )
1343     {
1344       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1345       {
1346         cursorPosition.x = 1.f;
1347         break;
1348       }
1349       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1350       {
1351         cursorPosition.x = floor( 0.5f * mControlSize.width );
1352         break;
1353       }
1354       case LayoutEngine::HORIZONTAL_ALIGN_END:
1355       {
1356         cursorPosition.x = mControlSize.width;
1357         break;
1358       }
1359     }
1360
1361     switch( mLayoutEngine.GetVerticalAlignment() )
1362     {
1363       case LayoutEngine::VERTICAL_ALIGN_TOP:
1364       {
1365         cursorPosition.y = 0.f;
1366         break;
1367       }
1368       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1369       {
1370         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1371         break;
1372       }
1373       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1374       {
1375         cursorPosition.y = mControlSize.height - lineHeight;
1376         break;
1377       }
1378     }
1379
1380     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1381                                          cursorPosition.x,
1382                                          cursorPosition.y,
1383                                          lineHeight,
1384                                          lineHeight );
1385   }
1386   else
1387   {
1388     CursorInfo cursorInfo;
1389     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1390                        cursorInfo );
1391
1392     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1393     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1394
1395     // Sets the cursor position.
1396     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1397                                          cursorPosition.x,
1398                                          cursorPosition.y,
1399                                          cursorInfo.primaryCursorHeight,
1400                                          cursorInfo.lineHeight );
1401     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1402
1403     // Sets the grab handle position.
1404     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1405                                          cursorPosition.x,
1406                                          cursorPosition.y,
1407                                          cursorInfo.lineHeight );
1408
1409     if( cursorInfo.isSecondaryCursor )
1410     {
1411       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1412       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1413                                            cursorInfo.secondaryPosition.x + offset.x,
1414                                            cursorInfo.secondaryPosition.y + offset.y,
1415                                            cursorInfo.secondaryCursorHeight,
1416                                            cursorInfo.lineHeight );
1417       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1418     }
1419     else
1420     {
1421       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1422     }
1423   }
1424   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1425 }
1426
1427 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1428 {
1429   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1430       ( RIGHT_SELECTION_HANDLE != handleType ) )
1431   {
1432     return;
1433   }
1434
1435   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1436   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1437
1438   CursorInfo cursorInfo;
1439   GetCursorPosition( index,
1440                      cursorInfo );
1441
1442   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1443   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1444
1445   // Sets the grab handle position.
1446   mEventData->mDecorator->SetPosition( handleType,
1447                                        cursorPosition.x,
1448                                        cursorPosition.y,
1449                                        cursorInfo.lineHeight );
1450 }
1451
1452 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1453 {
1454   // Clamp between -space & 0 (and the text alignment).
1455   if( actualSize.width > mControlSize.width )
1456   {
1457     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1458     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1459     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1460
1461     mEventData->mDecoratorUpdated = true;
1462   }
1463   else
1464   {
1465     mEventData->mScrollPosition.x = 0.f;
1466   }
1467 }
1468
1469 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1470 {
1471   // Clamp between -space & 0 (and the text alignment).
1472   if( actualSize.height > mControlSize.height )
1473   {
1474     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1475     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1476     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1477
1478     mEventData->mDecoratorUpdated = true;
1479   }
1480   else
1481   {
1482     mEventData->mScrollPosition.y = 0.f;
1483   }
1484 }
1485
1486 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1487 {
1488   Vector2 offset;
1489   bool updateDecorator = false;
1490   if( position.x < 0.f )
1491   {
1492     offset.x = -position.x;
1493     mEventData->mScrollPosition.x += offset.x;
1494     updateDecorator = true;
1495   }
1496   else if( position.x > mControlSize.width )
1497   {
1498     offset.x = mControlSize.width - position.x;
1499     mEventData->mScrollPosition.x += offset.x;
1500     updateDecorator = true;
1501   }
1502
1503   if( updateDecorator && mEventData->mDecorator )
1504   {
1505     mEventData->mDecorator->UpdatePositions( offset );
1506   }
1507
1508   // TODO : calculate the vertical scroll.
1509 }
1510
1511 void Controller::Impl::ScrollTextToMatchCursor()
1512 {
1513   // Get the current cursor position in decorator coords.
1514   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1515
1516   // Calculate the new cursor position.
1517   CursorInfo cursorInfo;
1518   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1519                      cursorInfo );
1520
1521   // Calculate the offset to match the cursor position before the character was deleted.
1522   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1523
1524   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1525   bool updateCursorPosition = true;
1526
1527   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1528   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1529
1530   if( updateCursorPosition )
1531   {
1532     // Sets the cursor position.
1533     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1534                                          cursorPosition.x,
1535                                          cursorPosition.y,
1536                                          cursorInfo.primaryCursorHeight,
1537                                          cursorInfo.lineHeight );
1538
1539     // Sets the grab handle position.
1540     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1541                                          cursorPosition.x,
1542                                          cursorPosition.y,
1543                                          cursorInfo.lineHeight );
1544
1545     if( cursorInfo.isSecondaryCursor )
1546     {
1547       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1548       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1549                                            cursorInfo.secondaryPosition.x + offset.x,
1550                                            cursorInfo.secondaryPosition.y + offset.y,
1551                                            cursorInfo.secondaryCursorHeight,
1552                                            cursorInfo.lineHeight );
1553       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1554     }
1555     else
1556     {
1557       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1558     }
1559   }
1560 }
1561
1562 void Controller::Impl::RequestRelayout()
1563 {
1564   mControlInterface.RequestTextRelayout();
1565 }
1566
1567 } // namespace Text
1568
1569 } // namespace Toolkit
1570
1571 } // namespace Dali