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