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