Merge "TET cases for Text." 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::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
803 {
804   if ( mClipboard )
805   {
806     retreivedString =  mClipboard.GetItem( itemIndex );
807   }
808 }
809
810 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
811 {
812   if( selectionStart == selectionEnd )
813   {
814     // Nothing to select if handles are in the same place.
815     return;
816   }
817
818   mEventData->mDecorator->ClearHighlights();
819
820   mEventData->mLeftSelectionPosition = selectionStart;
821   mEventData->mRightSelectionPosition = selectionEnd;
822
823   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
824   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
825   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
826   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
827
828   // TODO: Better algorithm to create the highlight box.
829   // TODO: Multi-line.
830
831   const Vector<LineRun>& lines = mVisualModel->mLines;
832   const LineRun& firstLine = *lines.Begin();
833   const float height = firstLine.ascender + -firstLine.descender;
834
835   const bool indicesSwapped = ( selectionStart > selectionEnd );
836   if( indicesSwapped )
837   {
838     std::swap( selectionStart, selectionEnd );
839   }
840
841   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
842   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
843
844   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
845
846   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
847
848   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
849   {
850     const GlyphInfo& glyph = *( glyphsBuffer + index );
851     const Vector2& position = *( positionsBuffer + index );
852
853     const float xPosition = position.x - glyph.xBearing + offset.x;
854     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
855   }
856
857   CursorInfo primaryCursorInfo;
858   GetCursorPosition( mEventData->mLeftSelectionPosition,
859                      primaryCursorInfo );
860
861   CursorInfo secondaryCursorInfo;
862   GetCursorPosition( mEventData->mRightSelectionPosition,
863                      secondaryCursorInfo );
864
865   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
866   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
867
868   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
869
870   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
871
872   // Set the flag to update the decorator.
873   mEventData->mDecoratorUpdated = true;
874 }
875
876 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
877 {
878   if( NULL == mEventData )
879   {
880     // Nothing to do if there is no text input.
881     return;
882   }
883
884   if( IsShowingPlaceholderText() )
885   {
886     // Nothing to do if there is the place-holder text.
887     return;
888   }
889
890   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
891   const Length numberOfLines  = mVisualModel->mLines.Count();
892   if( 0 == numberOfGlyphs ||
893       0 == numberOfLines )
894   {
895     // Nothing to do if there is no text.
896     return;
897   }
898
899   // Find which word was selected
900   CharacterIndex selectionStart( 0 );
901   CharacterIndex selectionEnd( 0 );
902   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
903   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
904
905   if( selectionStart == selectionEnd )
906   {
907     ChangeState( EventData::EDITING );
908     // Nothing to select. i.e. a white space, out of bounds
909     return;
910   }
911
912   RepositionSelectionHandles( selectionStart, selectionEnd );
913 }
914
915 void Controller::Impl::ChangeState( EventData::State newState )
916 {
917   if( NULL == mEventData )
918   {
919     // Nothing to do if there is no text input.
920     return;
921   }
922
923   if( mEventData->mState != newState )
924   {
925     mEventData->mState = newState;
926
927     if( EventData::INACTIVE == mEventData->mState )
928     {
929       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
930       mEventData->mDecorator->StopCursorBlink();
931       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
932       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
933       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
934       mEventData->mDecorator->SetPopupActive( false );
935       mEventData->mDecoratorUpdated = true;
936     }
937     else if ( EventData::SELECTING == mEventData->mState )
938     {
939       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
940       mEventData->mDecorator->StopCursorBlink();
941       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
942       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
943       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
944       if( mEventData->mGrabHandlePopupEnabled )
945       {
946         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
947         if ( !IsClipboardEmpty() )
948         {
949           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
950         }
951
952         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
953         mEventData->mDecorator->SetPopupActive( true );
954       }
955       mEventData->mDecoratorUpdated = true;
956     }
957     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
958     {
959       if( mEventData->mGrabHandlePopupEnabled )
960       {
961         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
962         if (  !IsClipboardEmpty() )
963         {
964           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
965         }
966         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
967         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
968         mEventData->mDecorator->SetPopupActive( true );
969       }
970       mEventData->mDecoratorUpdated = true;
971     }
972     else if( EventData::EDITING == mEventData->mState )
973     {
974       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
975       if( mEventData->mCursorBlinkEnabled )
976       {
977         mEventData->mDecorator->StartCursorBlink();
978       }
979       // Grab handle is not shown until a tap is received whilst EDITING
980       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
981       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
982       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
983       if( mEventData->mGrabHandlePopupEnabled )
984       {
985         mEventData->mDecorator->SetPopupActive( false );
986       }
987       mEventData->mDecoratorUpdated = true;
988     }
989     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
990     {
991       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
992       if( mEventData->mCursorBlinkEnabled )
993       {
994         mEventData->mDecorator->StartCursorBlink();
995       }
996       if( mEventData->mSelectionEnabled )
997       {
998         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
999         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1000       }
1001       else
1002       {
1003         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1004       }
1005       if( mEventData->mGrabHandlePopupEnabled )
1006       {
1007         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1008
1009         if ( !IsClipboardEmpty() )
1010         {
1011           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1012         }
1013
1014         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1015         mEventData->mDecorator->SetPopupActive( true );
1016       }
1017       mEventData->mDecoratorUpdated = true;
1018     }
1019     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1020     {
1021       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1022       mEventData->mDecorator->StopCursorBlink();
1023       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1024       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1025       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1026       if( mEventData->mGrabHandlePopupEnabled )
1027       {
1028         mEventData->mDecorator->SetPopupActive( false );
1029       }
1030       mEventData->mDecoratorUpdated = true;
1031     }
1032     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1033     {
1034       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1035       if( mEventData->mCursorBlinkEnabled )
1036       {
1037         mEventData->mDecorator->StartCursorBlink();
1038       }
1039       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1040       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1041       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1042       if( mEventData->mGrabHandlePopupEnabled )
1043       {
1044         mEventData->mDecorator->SetPopupActive( false );
1045       }
1046       mEventData->mDecoratorUpdated = true;
1047     }
1048   }
1049 }
1050
1051 LineIndex Controller::Impl::GetClosestLine( float y ) const
1052 {
1053   float totalHeight = 0.f;
1054   LineIndex lineIndex = 0u;
1055
1056   const Vector<LineRun>& lines = mVisualModel->mLines;
1057   for( LineIndex endLine = lines.Count();
1058        lineIndex < endLine;
1059        ++lineIndex )
1060   {
1061     const LineRun& lineRun = lines[lineIndex];
1062     totalHeight += lineRun.ascender + -lineRun.descender;
1063     if( y < totalHeight )
1064     {
1065       return lineIndex;
1066     }
1067   }
1068
1069   if( lineIndex == 0 )
1070   {
1071     return 0;
1072   }
1073
1074   return lineIndex-1;
1075 }
1076
1077 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1078 {
1079   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1080   if( hitCharacter >= mLogicalModel->mText.Count() )
1081   {
1082     // Selection out of bounds.
1083     return;
1084   }
1085
1086   startIndex = hitCharacter;
1087   endIndex = hitCharacter;
1088
1089   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1090   {
1091     // Find the start and end of the text
1092     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1093     {
1094       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1095       if( TextAbstraction::IsWhiteSpace( charCode ) )
1096       {
1097         break;
1098       }
1099     }
1100     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1101     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1102     {
1103       Character charCode = mLogicalModel->mText[ endIndex ];
1104       if( TextAbstraction::IsWhiteSpace( charCode ) )
1105       {
1106         break;
1107       }
1108     }
1109   }
1110 }
1111
1112 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1113                                                         float visualY )
1114 {
1115   if( NULL == mEventData )
1116   {
1117     // Nothing to do if there is no text input.
1118     return 0u;
1119   }
1120
1121   CharacterIndex logicalIndex = 0u;
1122
1123   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1124   const Length numberOfLines  = mVisualModel->mLines.Count();
1125   if( 0 == numberOfGlyphs ||
1126       0 == numberOfLines )
1127   {
1128     return logicalIndex;
1129   }
1130
1131   // Find which line is closest
1132   const LineIndex lineIndex = GetClosestLine( visualY );
1133   const LineRun& line = mVisualModel->mLines[lineIndex];
1134
1135   // Get the positions of the glyphs.
1136   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1137   const Vector2* const positionsBuffer = positions.Begin();
1138
1139   // Get the visual to logical conversion tables.
1140   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1141   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1142
1143   // Get the character to glyph conversion table.
1144   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1145
1146   // Get the glyphs per character table.
1147   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1148
1149   // If the vector is void, there is no right to left characters.
1150   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1151
1152   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1153   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1154   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1155
1156   // Whether there is a hit on a glyph.
1157   bool matched = false;
1158
1159   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1160   CharacterIndex visualIndex = startCharacter;
1161   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1162   {
1163     // The character in logical order.
1164     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1165
1166     // The first glyph for that character in logical order.
1167     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1168
1169     // The number of glyphs for that character
1170     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1171
1172     // Get the metrics for the group of glyphs.
1173     GlyphMetrics glyphMetrics;
1174     GetGlyphsMetrics( glyphLogicalOrderIndex,
1175                       numberOfGlyphs,
1176                       glyphMetrics,
1177                       mVisualModel,
1178                       mFontClient );
1179
1180     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1181
1182     // Find the mid-point of the area containing the glyph
1183     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1184
1185     if( visualX < glyphCenter )
1186     {
1187       matched = true;
1188       break;
1189     }
1190   }
1191
1192   // Return the logical position of the cursor in characters.
1193
1194   if( !matched )
1195   {
1196     visualIndex = endCharacter;
1197   }
1198
1199   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1200   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1201   return logicalIndex;
1202 }
1203
1204 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1205                                           CursorInfo& cursorInfo )
1206 {
1207   // TODO: Check for multiline with \n, etc...
1208
1209   // Check if the logical position is the first or the last one of the text.
1210   const bool isFirstPosition = 0u == logical;
1211   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1212
1213   if( isFirstPosition && isLastPosition )
1214   {
1215     // There is zero characters. Get the default font.
1216
1217     FontId defaultFontId = 0u;
1218     if( NULL == mFontDefaults )
1219     {
1220       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1221                                              EMPTY_STRING );
1222     }
1223     else
1224     {
1225       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1226     }
1227
1228     Text::FontMetrics fontMetrics;
1229     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1230
1231     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
1232     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1233
1234     cursorInfo.primaryPosition.x = 0.f;
1235     cursorInfo.primaryPosition.y = 0.f;
1236
1237     // Nothing else to do.
1238     return;
1239   }
1240
1241   // Get the previous logical index.
1242   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1243
1244   // Decrease the logical index if it's the last one.
1245   if( isLastPosition )
1246   {
1247     --logical;
1248   }
1249
1250   // Get the direction of the character and the previous one.
1251   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1252
1253   CharacterDirection isCurrentRightToLeft = false;
1254   CharacterDirection isPreviousRightToLeft = false;
1255   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1256   {
1257     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1258     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1259   }
1260
1261   // Get the line where the character is laid-out.
1262   const LineRun* modelLines = mVisualModel->mLines.Begin();
1263
1264   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1265   const LineRun& line = *( modelLines + lineIndex );
1266
1267   // Get the paragraph's direction.
1268   const CharacterDirection isRightToLeftParagraph = line.direction;
1269
1270   // Check whether there is an alternative position:
1271
1272   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1273     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1274
1275   // Set the line height.
1276   cursorInfo.lineHeight = line.ascender + -line.descender;
1277
1278   // Convert the cursor position into the glyph position.
1279   CharacterIndex characterIndex = logical;
1280   if( cursorInfo.isSecondaryCursor &&
1281       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1282   {
1283     characterIndex = previousLogical;
1284   }
1285
1286   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1287   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1288   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1289
1290   // Get the metrics for the group of glyphs.
1291   GlyphMetrics glyphMetrics;
1292   GetGlyphsMetrics( currentGlyphIndex,
1293                     numberOfGlyphs,
1294                     glyphMetrics,
1295                     mVisualModel,
1296                     mFontClient );
1297
1298   float interGlyphAdvance = 0.f;
1299   if( !isLastPosition &&
1300       ( numberOfCharacters > 1u ) )
1301   {
1302     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1303     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1304   }
1305
1306   // Get the glyph position and x bearing.
1307   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1308
1309   // Set the cursor's height.
1310   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1311
1312   // Set the position.
1313   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1314   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1315
1316   if( isLastPosition )
1317   {
1318     // The position of the cursor after the last character needs special
1319     // care depending on its direction and the direction of the paragraph.
1320
1321     if( cursorInfo.isSecondaryCursor )
1322     {
1323       // Need to find the first character after the last character with the paragraph's direction.
1324       // i.e l0 l1 l2 r0 r1 should find r0.
1325
1326       // TODO: check for more than one line!
1327       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1328       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1329
1330       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1331       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1332
1333       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1334
1335       // Get the metrics for the group of glyphs.
1336       GlyphMetrics glyphMetrics;
1337       GetGlyphsMetrics( glyphIndex,
1338                         numberOfGlyphs,
1339                         glyphMetrics,
1340                         mVisualModel,
1341                         mFontClient );
1342
1343       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1344
1345       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1346     }
1347     else
1348     {
1349       if( !isCurrentRightToLeft )
1350       {
1351         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1352       }
1353       else
1354       {
1355         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1356       }
1357     }
1358   }
1359
1360   // Set the alternative cursor position.
1361   if( cursorInfo.isSecondaryCursor )
1362   {
1363     // Convert the cursor position into the glyph position.
1364     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1365     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1366     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1367
1368     // Get the glyph position.
1369     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1370
1371     // Get the metrics for the group of glyphs.
1372     GlyphMetrics glyphMetrics;
1373     GetGlyphsMetrics( previousGlyphIndex,
1374                       numberOfGlyphs,
1375                       glyphMetrics,
1376                       mVisualModel,
1377                       mFontClient );
1378
1379     // Set the cursor position and height.
1380     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1381                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1382
1383     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1384
1385     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1386
1387     // Update the primary cursor height as well.
1388     cursorInfo.primaryCursorHeight *= 0.5f;
1389   }
1390 }
1391
1392 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1393 {
1394   if( NULL == mEventData )
1395   {
1396     // Nothing to do if there is no text input.
1397     return 0u;
1398   }
1399
1400   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1401
1402   const Script script = mLogicalModel->GetScript( index );
1403   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1404   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1405
1406   Length numberOfCharacters = 0u;
1407   if( TextAbstraction::LATIN == script )
1408   {
1409     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1410     numberOfCharacters = 1u;
1411   }
1412   else
1413   {
1414     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1415     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1416
1417     while( 0u == numberOfCharacters )
1418     {
1419       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1420       ++glyphIndex;
1421     }
1422   }
1423
1424   if( index < mEventData->mPrimaryCursorPosition )
1425   {
1426     cursorIndex -= numberOfCharacters;
1427   }
1428   else
1429   {
1430     cursorIndex += numberOfCharacters;
1431   }
1432
1433   return cursorIndex;
1434 }
1435
1436 void Controller::Impl::UpdateCursorPosition()
1437 {
1438   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1439   if( NULL == mEventData )
1440   {
1441     // Nothing to do if there is no text input.
1442     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1443     return;
1444   }
1445
1446   if( IsShowingPlaceholderText() )
1447   {
1448     // Do not want to use the place-holder text to set the cursor position.
1449
1450     // Use the line's height of the font's family set to set the cursor's size.
1451     // If there is no font's family set, use the default font.
1452     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1453
1454     float lineHeight = 0.f;
1455
1456     FontId defaultFontId = 0u;
1457     if( NULL == mFontDefaults )
1458     {
1459       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1460                                              EMPTY_STRING );
1461     }
1462     else
1463     {
1464       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1465     }
1466
1467     Text::FontMetrics fontMetrics;
1468     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1469
1470     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1471
1472
1473     Vector2 cursorPosition;
1474
1475     switch( mLayoutEngine.GetHorizontalAlignment() )
1476     {
1477       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1478       {
1479         cursorPosition.x = 1.f;
1480         break;
1481       }
1482       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1483       {
1484         cursorPosition.x = floor( 0.5f * mControlSize.width );
1485         break;
1486       }
1487       case LayoutEngine::HORIZONTAL_ALIGN_END:
1488       {
1489         cursorPosition.x = mControlSize.width;
1490         break;
1491       }
1492     }
1493
1494     switch( mLayoutEngine.GetVerticalAlignment() )
1495     {
1496       case LayoutEngine::VERTICAL_ALIGN_TOP:
1497       {
1498         cursorPosition.y = 0.f;
1499         break;
1500       }
1501       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1502       {
1503         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1504         break;
1505       }
1506       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1507       {
1508         cursorPosition.y = mControlSize.height - lineHeight;
1509         break;
1510       }
1511     }
1512
1513     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1514                                          cursorPosition.x,
1515                                          cursorPosition.y,
1516                                          lineHeight,
1517                                          lineHeight );
1518   }
1519   else
1520   {
1521     CursorInfo cursorInfo;
1522     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1523                        cursorInfo );
1524
1525     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1526     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1527
1528     // Sets the cursor position.
1529     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1530                                          cursorPosition.x,
1531                                          cursorPosition.y,
1532                                          cursorInfo.primaryCursorHeight,
1533                                          cursorInfo.lineHeight );
1534     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1535
1536     // Sets the grab handle position.
1537     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1538                                          cursorPosition.x,
1539                                          cursorPosition.y,
1540                                          cursorInfo.lineHeight );
1541
1542     if( cursorInfo.isSecondaryCursor )
1543     {
1544       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1545       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1546                                            cursorInfo.secondaryPosition.x + offset.x,
1547                                            cursorInfo.secondaryPosition.y + offset.y,
1548                                            cursorInfo.secondaryCursorHeight,
1549                                            cursorInfo.lineHeight );
1550       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1551     }
1552     else
1553     {
1554       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1555     }
1556   }
1557   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1558 }
1559
1560 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1561 {
1562   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1563       ( RIGHT_SELECTION_HANDLE != handleType ) )
1564   {
1565     return;
1566   }
1567
1568   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1569   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1570
1571   CursorInfo cursorInfo;
1572   GetCursorPosition( index,
1573                      cursorInfo );
1574
1575   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1576   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1577
1578   // Sets the grab handle position.
1579   mEventData->mDecorator->SetPosition( handleType,
1580                                        cursorPosition.x,
1581                                        cursorPosition.y,
1582                                        cursorInfo.lineHeight );
1583 }
1584
1585 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1586 {
1587   // Clamp between -space & 0 (and the text alignment).
1588   if( actualSize.width > mControlSize.width )
1589   {
1590     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1591     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1592     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1593
1594     mEventData->mDecoratorUpdated = true;
1595   }
1596   else
1597   {
1598     mEventData->mScrollPosition.x = 0.f;
1599   }
1600 }
1601
1602 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1603 {
1604   // Clamp between -space & 0 (and the text alignment).
1605   if( actualSize.height > mControlSize.height )
1606   {
1607     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1608     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1609     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1610
1611     mEventData->mDecoratorUpdated = true;
1612   }
1613   else
1614   {
1615     mEventData->mScrollPosition.y = 0.f;
1616   }
1617 }
1618
1619 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1620 {
1621   Vector2 offset;
1622   bool updateDecorator = false;
1623   if( position.x < 0.f )
1624   {
1625     offset.x = -position.x;
1626     mEventData->mScrollPosition.x += offset.x;
1627     updateDecorator = true;
1628   }
1629   else if( position.x > mControlSize.width )
1630   {
1631     offset.x = mControlSize.width - position.x;
1632     mEventData->mScrollPosition.x += offset.x;
1633     updateDecorator = true;
1634   }
1635
1636   if( updateDecorator && mEventData->mDecorator )
1637   {
1638     mEventData->mDecorator->UpdatePositions( offset );
1639   }
1640
1641   // TODO : calculate the vertical scroll.
1642 }
1643
1644 void Controller::Impl::ScrollTextToMatchCursor()
1645 {
1646   // Get the current cursor position in decorator coords.
1647   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1648
1649   // Calculate the new cursor position.
1650   CursorInfo cursorInfo;
1651   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1652                      cursorInfo );
1653
1654   // Calculate the offset to match the cursor position before the character was deleted.
1655   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1656
1657   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1658   bool updateCursorPosition = true;
1659
1660   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1661   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1662
1663   if( updateCursorPosition )
1664   {
1665     // Sets the cursor position.
1666     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1667                                          cursorPosition.x,
1668                                          cursorPosition.y,
1669                                          cursorInfo.primaryCursorHeight,
1670                                          cursorInfo.lineHeight );
1671
1672     // Sets the grab handle position.
1673     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1674                                          cursorPosition.x,
1675                                          cursorPosition.y,
1676                                          cursorInfo.lineHeight );
1677
1678     if( cursorInfo.isSecondaryCursor )
1679     {
1680       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1681       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1682                                            cursorInfo.secondaryPosition.x + offset.x,
1683                                            cursorInfo.secondaryPosition.y + offset.y,
1684                                            cursorInfo.secondaryCursorHeight,
1685                                            cursorInfo.lineHeight );
1686       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1687     }
1688     else
1689     {
1690       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1691     }
1692   }
1693 }
1694
1695 void Controller::Impl::RequestRelayout()
1696 {
1697   mControlInterface.RequestTextRelayout();
1698 }
1699
1700 } // namespace Text
1701
1702 } // namespace Toolkit
1703
1704 } // namespace Dali