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