Set label padding in case of ResizePolicy::USE_NATURAL_SIZE
[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
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/bidirectional-support.h>
26 #include <dali-toolkit/internal/text/character-set-conversion.h>
27 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
28 #include <dali-toolkit/internal/text/multi-language-support.h>
29 #include <dali-toolkit/internal/text/script-run.h>
30 #include <dali-toolkit/internal/text/segmentation.h>
31 #include <dali-toolkit/internal/text/shaper.h>
32 #include <dali-toolkit/internal/text/text-io.h>
33 #include <dali-toolkit/internal/text/text-view.h>
34
35 namespace
36 {
37
38 /**
39  * @brief Some characters can be shaped in more than one glyph.
40  * This struct is used to retrieve metrics from these group of glyphs.
41  */
42 struct GlyphMetrics
43 {
44   GlyphMetrics()
45   : fontHeight( 0.f ),
46     advance( 0.f ),
47     ascender( 0.f ),
48     xBearing( 0.f )
49   {}
50
51   ~GlyphMetrics()
52   {}
53
54   float fontHeight; ///< The font's height of that glyphs.
55   float advance;    ///< The sum of all the advances of all the glyphs.
56   float ascender;   ///< The font's ascender.
57   float xBearing;   ///< The x bearing of the first glyph.
58 };
59
60 const std::string EMPTY_STRING("");
61
62 } // namespace
63
64 namespace Dali
65 {
66
67 namespace Toolkit
68 {
69
70 namespace Text
71 {
72
73 /**
74  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
75  *
76  * @param[in] glyphIndex The index to the first glyph.
77  * @param[in] numberOfGlyphs The number of glyphs.
78  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
79  * @param[in]
80  * @param[in]
81  */
82 void GetGlyphsMetrics( GlyphIndex glyphIndex,
83                        Length numberOfGlyphs,
84                        GlyphMetrics& glyphMetrics,
85                        VisualModelPtr visualModel,
86                        TextAbstraction::FontClient& fontClient )
87 {
88   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
89
90   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
91
92   Text::FontMetrics fontMetrics;
93   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
94
95   glyphMetrics.fontHeight = fontMetrics.height;
96   glyphMetrics.advance = firstGlyph.advance;
97   glyphMetrics.ascender = fontMetrics.ascender;
98   glyphMetrics.xBearing = firstGlyph.xBearing;
99
100   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
101   {
102     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
103
104     glyphMetrics.advance += glyphInfo.advance;
105   }
106 }
107
108 EventData::EventData( DecoratorPtr decorator )
109 : mDecorator( decorator ),
110   mPlaceholderTextActive(),
111   mPlaceholderTextInactive(),
112   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
113   mEventQueue(),
114   mScrollPosition(),
115   mState( INACTIVE ),
116   mPrimaryCursorPosition( 0u ),
117   mLeftSelectionPosition( 0u ),
118   mRightSelectionPosition( 0u ),
119   mPreEditStartPosition( 0u ),
120   mPreEditLength( 0u ),
121   mIsShowingPlaceholderText( false ),
122   mPreEditFlag( false ),
123   mDecoratorUpdated( false ),
124   mCursorBlinkEnabled( true ),
125   mGrabHandleEnabled( true ),
126   mGrabHandlePopupEnabled( false ),
127   mSelectionEnabled( false ),
128   mHorizontalScrollingEnabled( true ),
129   mVerticalScrollingEnabled( false ),
130   mUpdateCursorPosition( false ),
131   mUpdateLeftSelectionPosition( false ),
132   mUpdateRightSelectionPosition( false ),
133   mScrollAfterUpdateCursorPosition( false )
134 {}
135
136 EventData::~EventData()
137 {}
138
139 bool Controller::Impl::ProcessInputEvents()
140 {
141   if( NULL == mEventData )
142   {
143     // Nothing to do if there is no text input.
144     return false;
145   }
146
147   mEventData->mDecoratorUpdated = false;
148
149   if( mEventData->mDecorator )
150   {
151     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
152          iter != mEventData->mEventQueue.end();
153          ++iter )
154     {
155       switch( iter->type )
156       {
157       case Event::KEYBOARD_FOCUS_GAIN_EVENT:
158       {
159         OnKeyboardFocus( true );
160         break;
161       }
162       case Event::KEYBOARD_FOCUS_LOST_EVENT:
163       {
164         OnKeyboardFocus( false );
165         break;
166       }
167       case Event::CURSOR_KEY_EVENT:
168       {
169         OnCursorKeyEvent( *iter );
170         break;
171       }
172       case Event::TAP_EVENT:
173       {
174         OnTapEvent( *iter );
175         break;
176       }
177       case Event::PAN_EVENT:
178       {
179         OnPanEvent( *iter );
180         break;
181       }
182       case Event::GRAB_HANDLE_EVENT:
183       case Event::LEFT_SELECTION_HANDLE_EVENT:
184       case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
185       {
186         OnHandleEvent( *iter );
187         break;
188       }
189       }
190     }
191   }
192
193   // The cursor must also be repositioned after inserts into the model
194   if( mEventData->mUpdateCursorPosition )
195   {
196     // Updates the cursor position and scrolls the text to make it visible.
197
198     UpdateCursorPosition();
199
200     if( mEventData->mScrollAfterUpdateCursorPosition )
201     {
202       ScrollToMakeCursorVisible();
203       mEventData->mScrollAfterUpdateCursorPosition = false;
204     }
205
206     mEventData->mDecoratorUpdated = true;
207     mEventData->mUpdateCursorPosition = false;
208   }
209   else if( mEventData->mUpdateLeftSelectionPosition )
210   {
211     UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
212
213     if( mEventData->mScrollAfterUpdateCursorPosition )
214     {
215       ScrollToMakeCursorVisible();
216       mEventData->mScrollAfterUpdateCursorPosition = false;
217     }
218
219     mEventData->mDecoratorUpdated = true;
220     mEventData->mUpdateLeftSelectionPosition = false;
221   }
222   else if( mEventData->mUpdateRightSelectionPosition )
223   {
224     UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
225
226     if( mEventData->mScrollAfterUpdateCursorPosition )
227     {
228       ScrollToMakeCursorVisible();
229       mEventData->mScrollAfterUpdateCursorPosition = false;
230     }
231
232     mEventData->mDecoratorUpdated = true;
233     mEventData->mUpdateRightSelectionPosition = false;
234   }
235
236   mEventData->mEventQueue.clear();
237
238   return mEventData->mDecoratorUpdated;
239 }
240
241 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
242 {
243   // Calculate the operations to be done.
244   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
245
246   Vector<Character>& utf32Characters = mLogicalModel->mText;
247
248   const Length numberOfCharacters = mLogicalModel->GetNumberOfCharacters();
249
250   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
251   if( GET_LINE_BREAKS & operations )
252   {
253     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
254     // calculate the bidirectional info for each 'paragraph'.
255     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
256     // is not shaped together).
257     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
258
259     SetLineBreakInfo( utf32Characters,
260                       lineBreakInfo );
261   }
262
263   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
264   if( GET_WORD_BREAKS & operations )
265   {
266     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
267     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
268
269     SetWordBreakInfo( utf32Characters,
270                       wordBreakInfo );
271   }
272
273   const bool getScripts = GET_SCRIPTS & operations;
274   const bool validateFonts = VALIDATE_FONTS & operations;
275
276   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
277   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
278
279   if( getScripts || validateFonts )
280   {
281     // Validates the fonts assigned by the application or assigns default ones.
282     // It makes sure all the characters are going to be rendered by the correct font.
283     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
284
285     if( getScripts )
286     {
287       // Retrieves the scripts used in the text.
288       multilanguageSupport.SetScripts( utf32Characters,
289                                        lineBreakInfo,
290                                        scripts );
291     }
292
293     if( validateFonts )
294     {
295       if( 0u == validFonts.Count() )
296       {
297         // Copy the requested font defaults received via the property system.
298         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
299         GetDefaultFonts( validFonts, numberOfCharacters );
300       }
301
302       // Validates the fonts. If there is a character with no assigned font it sets a default one.
303       // After this call, fonts are validated.
304       multilanguageSupport.ValidateFonts( utf32Characters,
305                                           scripts,
306                                           validFonts );
307     }
308   }
309
310   Vector<Character> mirroredUtf32Characters;
311   bool textMirrored = false;
312   if( BIDI_INFO & operations )
313   {
314     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
315     // bidirectional info.
316
317     Length numberOfParagraphs = 0u;
318
319     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
320     for( Length index = 0u; index < numberOfCharacters; ++index )
321     {
322       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
323       {
324         ++numberOfParagraphs;
325       }
326     }
327
328     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
329     bidirectionalInfo.Reserve( numberOfParagraphs );
330
331     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
332     SetBidirectionalInfo( utf32Characters,
333                           scripts,
334                           lineBreakInfo,
335                           bidirectionalInfo );
336
337     if( 0u != bidirectionalInfo.Count() )
338     {
339       // This paragraph has right to left text. Some characters may need to be mirrored.
340       // TODO: consider if the mirrored string can be stored as well.
341
342       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
343
344       // Only set the character directions if there is right to left characters.
345       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
346       directions.Resize( numberOfCharacters );
347
348       GetCharactersDirection( bidirectionalInfo,
349                               directions );
350     }
351     else
352     {
353       // There is no right to left characters. Clear the directions vector.
354       mLogicalModel->mCharacterDirections.Clear();
355     }
356
357    }
358
359   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
360   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
361   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
362   if( SHAPE_TEXT & operations )
363   {
364     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
365     // Shapes the text.
366     ShapeText( textToShape,
367                lineBreakInfo,
368                scripts,
369                validFonts,
370                glyphs,
371                glyphsToCharactersMap,
372                charactersPerGlyph );
373
374     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
375     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
376     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
377   }
378
379   const Length numberOfGlyphs = glyphs.Count();
380
381   if( GET_GLYPH_METRICS & operations )
382   {
383     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
384   }
385 }
386
387 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
388 {
389   if( mFontDefaults )
390   {
391     FontRun fontRun;
392     fontRun.characterRun.characterIndex = 0;
393     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
394     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
395     fontRun.isDefault = true;
396
397     fonts.PushBack( fontRun );
398   }
399 }
400
401 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
402 {
403   if( NULL == mEventData )
404   {
405     // Nothing to do if there is no text input.
406     return;
407   }
408
409   if( !hasFocus )
410   {
411     ChangeState( EventData::INACTIVE );
412   }
413   else
414   {
415     ChangeState( EventData::EDITING );
416   }
417 }
418
419 void Controller::Impl::OnCursorKeyEvent( const Event& event )
420 {
421   if( NULL == mEventData )
422   {
423     // Nothing to do if there is no text input.
424     return;
425   }
426
427   int keyCode = event.p1.mInt;
428
429   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
430   {
431     if( mEventData->mPrimaryCursorPosition > 0u )
432     {
433       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
434     }
435   }
436   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
437   {
438     if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
439     {
440       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
441     }
442   }
443   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
444   {
445     // TODO
446   }
447   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
448   {
449     // TODO
450   }
451
452   mEventData->mUpdateCursorPosition = true;
453   mEventData->mScrollAfterUpdateCursorPosition = true;
454 }
455
456 void Controller::Impl::OnTapEvent( const Event& event )
457 {
458   if( NULL == mEventData )
459   {
460     // Nothing to do if there is no text input.
461     return;
462   }
463
464   const unsigned int tapCount = event.p1.mUint;
465
466   if( 1u == tapCount )
467   {
468     // Grab handle is not shown until a tap is received whilst EDITING
469     if( EventData::EDITING == mEventData->mState &&
470         !IsShowingPlaceholderText() )
471     {
472       if( mEventData->mGrabHandleEnabled )
473       {
474         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
475       }
476       mEventData->mDecorator->SetPopupActive( false );
477     }
478
479     ChangeState( EventData::EDITING );
480
481     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
482     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
483
484     mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
485                                                                 yPosition );
486
487     mEventData->mUpdateCursorPosition = true;
488     mEventData->mScrollAfterUpdateCursorPosition = true;
489   }
490   else if( mEventData->mSelectionEnabled &&
491            ( 2u == tapCount ) )
492   {
493     ChangeState( EventData::SELECTING );
494
495     RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
496   }
497 }
498
499 void Controller::Impl::OnPanEvent( const Event& event )
500 {
501   if( NULL == mEventData )
502   {
503     // Nothing to do if there is no text input.
504     return;
505   }
506
507   int state = event.p1.mInt;
508
509   if( Gesture::Started    == state ||
510       Gesture::Continuing == state )
511   {
512     const Vector2& actualSize = mVisualModel->GetActualSize();
513     const Vector2 currentScroll = mEventData->mScrollPosition;
514
515     if( mEventData->mHorizontalScrollingEnabled )
516     {
517       const float displacementX = event.p2.mFloat;
518       mEventData->mScrollPosition.x += displacementX;
519
520       ClampHorizontalScroll( actualSize );
521     }
522
523     if( mEventData->mVerticalScrollingEnabled )
524     {
525       const float displacementY = event.p3.mFloat;
526       mEventData->mScrollPosition.y += displacementY;
527
528       ClampVerticalScroll( actualSize );
529     }
530
531     if( mEventData->mDecorator )
532     {
533       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
534     }
535   }
536 }
537
538 void Controller::Impl::OnHandleEvent( const Event& event )
539 {
540   if( NULL == mEventData )
541   {
542     // Nothing to do if there is no text input.
543     return;
544   }
545
546   const unsigned int state = event.p1.mUint;
547
548   if( HANDLE_PRESSED == state )
549   {
550     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
551     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
552     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
553
554     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
555
556     if( Event::GRAB_HANDLE_EVENT == event.type )
557     {
558       ChangeState ( EventData::EDITING );
559
560       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
561       {
562         mEventData->mPrimaryCursorPosition = handleNewPosition;
563         mEventData->mUpdateCursorPosition = true;
564       }
565     }
566     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
567     {
568       if( handleNewPosition != mEventData->mLeftSelectionPosition )
569       {
570         mEventData->mLeftSelectionPosition = handleNewPosition;
571         mEventData->mUpdateLeftSelectionPosition = true;
572       }
573     }
574     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
575     {
576       if( handleNewPosition != mEventData->mRightSelectionPosition )
577       {
578         mEventData->mRightSelectionPosition = handleNewPosition;
579         mEventData->mUpdateRightSelectionPosition = true;
580       }
581     }
582   }
583   else if( ( HANDLE_RELEASED == state ) ||
584            ( HANDLE_STOP_SCROLLING == state ) )
585   {
586     if( mEventData->mGrabHandlePopupEnabled )
587     {
588       ChangeState( EventData::EDITING_WITH_POPUP );
589     }
590     if( Event::GRAB_HANDLE_EVENT == event.type )
591     {
592       mEventData->mUpdateCursorPosition = true;
593
594       if( HANDLE_STOP_SCROLLING == state )
595       {
596         // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
597         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
598         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
599
600         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
601
602         mEventData->mScrollAfterUpdateCursorPosition = true;
603       }
604     }
605     mEventData->mDecoratorUpdated = true;
606   }
607   else if( HANDLE_SCROLLING == state )
608   {
609     const float xSpeed = event.p2.mFloat;
610     const Vector2& actualSize = mVisualModel->GetActualSize();
611
612     mEventData->mScrollPosition.x += xSpeed;
613
614     ClampHorizontalScroll( actualSize );
615
616    mEventData->mDecoratorUpdated = true;
617   }
618 }
619
620 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
621 {
622   if( NULL == mEventData )
623   {
624     // Nothing to do if there is no text input.
625     return;
626   }
627
628   // TODO - Find which word was selected
629
630   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
631   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
632
633   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
634   const Vector<Vector2>::SizeType positionCount = positions.Count();
635
636   // Guard against glyphs which did not fit inside the layout
637   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
638
639   if( count )
640   {
641     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
642     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
643
644     // TODO - multi-line selection
645     const Vector<LineRun>& lines = mVisualModel->mLines;
646     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
647
648     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
649     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
650
651     mEventData->mDecorator->ClearHighlights();
652     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
653   }
654 }
655
656 void Controller::Impl::ChangeState( EventData::State newState )
657 {
658   if( NULL == mEventData )
659   {
660     // Nothing to do if there is no text input.
661     return;
662   }
663
664   if( mEventData->mState != newState )
665   {
666     mEventData->mState = newState;
667
668     if( EventData::INACTIVE == mEventData->mState )
669     {
670       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
671       mEventData->mDecorator->StopCursorBlink();
672       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
673       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
674       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
675       mEventData->mDecorator->SetPopupActive( false );
676       mEventData->mDecoratorUpdated = true;
677     }
678     else if ( EventData::SELECTING == mEventData->mState )
679     {
680       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
681       mEventData->mDecorator->StopCursorBlink();
682       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
683       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
684       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
685       mEventData->mDecoratorUpdated = true;
686     }
687     else if( EventData::EDITING == mEventData->mState )
688     {
689       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
690       if( mEventData->mCursorBlinkEnabled )
691       {
692         mEventData->mDecorator->StartCursorBlink();
693       }
694       // Grab handle is not shown until a tap is received whilst EDITING
695       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
696       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
697       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
698       mEventData->mDecoratorUpdated = true;
699     }
700     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
701     {
702       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
703       if( mEventData->mCursorBlinkEnabled )
704       {
705         mEventData->mDecorator->StartCursorBlink();
706       }
707       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
708       if( mEventData->mSelectionEnabled )
709       {
710         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
711         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
712       }
713       if( mEventData->mGrabHandlePopupEnabled )
714       {
715         mEventData->mDecorator->SetPopupActive( true );
716       }
717       mEventData->mDecoratorUpdated = true;
718     }
719   }
720 }
721
722 LineIndex Controller::Impl::GetClosestLine( float y ) const
723 {
724   float totalHeight = 0.f;
725   LineIndex lineIndex = 0u;
726
727   const Vector<LineRun>& lines = mVisualModel->mLines;
728   for( LineIndex endLine = lines.Count();
729        lineIndex < endLine;
730        ++lineIndex )
731   {
732     const LineRun& lineRun = lines[lineIndex];
733     totalHeight += lineRun.ascender + -lineRun.descender;
734     if( y < totalHeight )
735     {
736       return lineIndex;
737     }
738   }
739
740   return lineIndex-1;
741 }
742
743 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
744                                                         float visualY )
745 {
746   if( NULL == mEventData )
747   {
748     // Nothing to do if there is no text input.
749     return 0u;
750   }
751
752   CharacterIndex logicalIndex = 0u;
753
754   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
755   const Length numberOfLines  = mVisualModel->mLines.Count();
756   if( 0 == numberOfGlyphs ||
757       0 == numberOfLines )
758   {
759     return logicalIndex;
760   }
761
762   // Find which line is closest
763   const LineIndex lineIndex = GetClosestLine( visualY );
764   const LineRun& line = mVisualModel->mLines[lineIndex];
765
766   // Get the positions of the glyphs.
767   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
768   const Vector2* const positionsBuffer = positions.Begin();
769
770   // Get the visual to logical conversion tables.
771   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
772   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
773
774   // Get the character to glyph conversion table.
775   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
776
777   // Get the glyphs per character table.
778   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
779
780   // If the vector is void, there is no right to left characters.
781   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
782
783   const CharacterIndex startCharacter = line.characterRun.characterIndex;
784   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
785   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
786
787   // Whether there is a hit on a glyph.
788   bool matched = false;
789
790   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
791   CharacterIndex visualIndex = startCharacter;
792   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
793   {
794     // The character in logical order.
795     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
796
797     // The first glyph for that character in logical order.
798     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
799
800     // The number of glyphs for that character
801     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
802
803     // Get the metrics for the group of glyphs.
804     GlyphMetrics glyphMetrics;
805     GetGlyphsMetrics( glyphLogicalOrderIndex,
806                       numberOfGlyphs,
807                       glyphMetrics,
808                       mVisualModel,
809                       mFontClient );
810
811     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
812
813     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
814
815     if( visualX < glyphX )
816     {
817       matched = true;
818       break;
819     }
820   }
821
822   // Return the logical position of the cursor in characters.
823
824   if( !matched )
825   {
826     visualIndex = endCharacter;
827   }
828
829   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
830 }
831
832 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
833                                           CursorInfo& cursorInfo )
834 {
835   // TODO: Check for multiline with \n, etc...
836
837   // Check if the logical position is the first or the last one of the text.
838   const bool isFirstPosition = 0u == logical;
839   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
840
841   if( isFirstPosition && isLastPosition )
842   {
843     // There is zero characters. Get the default font.
844
845     FontId defaultFontId = 0u;
846     if( NULL == mFontDefaults )
847     {
848       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
849                                              EMPTY_STRING );
850     }
851     else
852     {
853       defaultFontId = mFontDefaults->GetFontId( mFontClient );
854     }
855
856     Text::FontMetrics fontMetrics;
857     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
858
859     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
860     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
861
862     cursorInfo.primaryPosition.x = 0.f;
863     cursorInfo.primaryPosition.y = 0.f;
864
865     // Nothing else to do.
866     return;
867   }
868
869   // Get the previous logical index.
870   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
871
872   // Decrease the logical index if it's the last one.
873   if( isLastPosition )
874   {
875     --logical;
876   }
877
878   // Get the direction of the character and the previous one.
879   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
880
881   CharacterDirection isCurrentRightToLeft = false;
882   CharacterDirection isPreviousRightToLeft = false;
883   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
884   {
885     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
886     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
887   }
888
889   // Get the line where the character is laid-out.
890   const LineRun* modelLines = mVisualModel->mLines.Begin();
891
892   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
893   const LineRun& line = *( modelLines + lineIndex );
894
895   // Get the paragraph's direction.
896   const CharacterDirection isRightToLeftParagraph = line.direction;
897
898   // Check whether there is an alternative position:
899
900   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
901     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
902
903   // Set the line height.
904   cursorInfo.lineHeight = line.ascender + -line.descender;
905
906   // Convert the cursor position into the glyph position.
907   CharacterIndex characterIndex = logical;
908   if( cursorInfo.isSecondaryCursor &&
909       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
910   {
911     characterIndex = previousLogical;
912   }
913
914   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
915   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
916   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
917
918   // Get the metrics for the group of glyphs.
919   GlyphMetrics glyphMetrics;
920   GetGlyphsMetrics( currentGlyphIndex,
921                     numberOfGlyphs,
922                     glyphMetrics,
923                     mVisualModel,
924                     mFontClient );
925
926   float interGlyphAdvance = 0.f;
927   if( !isLastPosition &&
928       ( numberOfCharacters > 1u ) )
929   {
930     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
931     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
932   }
933
934   // Get the glyph position and x bearing.
935   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
936
937   // Set the cursor's height.
938   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
939
940   // Set the position.
941   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
942   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
943
944   if( isLastPosition )
945   {
946     // The position of the cursor after the last character needs special
947     // care depending on its direction and the direction of the paragraph.
948
949     if( cursorInfo.isSecondaryCursor )
950     {
951       // Need to find the first character after the last character with the paragraph's direction.
952       // i.e l0 l1 l2 r0 r1 should find r0.
953
954       // TODO: check for more than one line!
955       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
956       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
957
958       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
959       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
960
961       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
962
963       // Get the metrics for the group of glyphs.
964       GlyphMetrics glyphMetrics;
965       GetGlyphsMetrics( glyphIndex,
966                         numberOfGlyphs,
967                         glyphMetrics,
968                         mVisualModel,
969                         mFontClient );
970
971       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
972
973       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
974     }
975     else
976     {
977       if( !isCurrentRightToLeft )
978       {
979         cursorInfo.primaryPosition.x += glyphMetrics.advance;
980       }
981       else
982       {
983         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
984       }
985     }
986   }
987
988   // Set the alternative cursor position.
989   if( cursorInfo.isSecondaryCursor )
990   {
991     // Convert the cursor position into the glyph position.
992     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
993     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
994     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
995
996     // Get the glyph position.
997     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
998
999     // Get the metrics for the group of glyphs.
1000     GlyphMetrics glyphMetrics;
1001     GetGlyphsMetrics( previousGlyphIndex,
1002                       numberOfGlyphs,
1003                       glyphMetrics,
1004                       mVisualModel,
1005                       mFontClient );
1006
1007     // Set the cursor position and height.
1008     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1009                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1010
1011     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1012
1013     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1014
1015     // Update the primary cursor height as well.
1016     cursorInfo.primaryCursorHeight *= 0.5f;
1017   }
1018 }
1019
1020 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1021 {
1022   if( NULL == mEventData )
1023   {
1024     // Nothing to do if there is no text input.
1025     return 0u;
1026   }
1027
1028   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1029
1030   const Script script = mLogicalModel->GetScript( index );
1031   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1032   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1033
1034   Length numberOfCharacters = 0u;
1035   if( TextAbstraction::LATIN == script )
1036   {
1037     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1038     numberOfCharacters = 1u;
1039   }
1040   else
1041   {
1042     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1043     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1044
1045     while( 0u == numberOfCharacters )
1046     {
1047       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1048       ++glyphIndex;
1049     }
1050   }
1051
1052   if( index < mEventData->mPrimaryCursorPosition )
1053   {
1054     cursorIndex -= numberOfCharacters;
1055   }
1056   else
1057   {
1058     cursorIndex += numberOfCharacters;
1059   }
1060
1061   return cursorIndex;
1062 }
1063
1064 void Controller::Impl::UpdateCursorPosition()
1065 {
1066   if( NULL == mEventData )
1067   {
1068     // Nothing to do if there is no text input.
1069     return;
1070   }
1071
1072   CursorInfo cursorInfo;
1073   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1074                      cursorInfo );
1075
1076   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1077   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1078
1079   // Sets the cursor position.
1080   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1081                                        cursorPosition.x,
1082                                        cursorPosition.y,
1083                                        cursorInfo.primaryCursorHeight,
1084                                        cursorInfo.lineHeight );
1085
1086   // Sets the grab handle position.
1087   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1088                                        cursorPosition.x,
1089                                        cursorPosition.y,
1090                                        cursorInfo.lineHeight );
1091
1092   if( cursorInfo.isSecondaryCursor )
1093   {
1094     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1095     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1096                                          cursorInfo.secondaryPosition.x + offset.x,
1097                                          cursorInfo.secondaryPosition.y + offset.y,
1098                                          cursorInfo.secondaryCursorHeight,
1099                                          cursorInfo.lineHeight );
1100   }
1101   else
1102   {
1103     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1104   }
1105 }
1106
1107 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1108 {
1109   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1110       ( RIGHT_SELECTION_HANDLE != handleType ) )
1111   {
1112     return;
1113   }
1114
1115   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1116   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1117
1118   CursorInfo cursorInfo;
1119   GetCursorPosition( index,
1120                      cursorInfo );
1121
1122   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1123   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1124
1125   // Sets the grab handle position.
1126   mEventData->mDecorator->SetPosition( handleType,
1127                                        cursorPosition.x,
1128                                        cursorPosition.y,
1129                                        cursorInfo.lineHeight );
1130 }
1131
1132 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1133 {
1134   // Clamp between -space & 0 (and the text alignment).
1135   if( actualSize.width > mControlSize.width )
1136   {
1137     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1138     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1139     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1140
1141     mEventData->mDecoratorUpdated = true;
1142   }
1143   else
1144   {
1145     mEventData->mScrollPosition.x = 0.f;
1146   }
1147 }
1148
1149 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1150 {
1151   // Clamp between -space & 0 (and the text alignment).
1152   if( actualSize.height > mControlSize.height )
1153   {
1154     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1155     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1156     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1157
1158     mEventData->mDecoratorUpdated = true;
1159   }
1160   else
1161   {
1162     mEventData->mScrollPosition.y = 0.f;
1163   }
1164 }
1165
1166 void Controller::Impl::ScrollToMakeCursorVisible()
1167 {
1168   if( NULL == mEventData )
1169   {
1170     // Nothing to do if there is no text input.
1171     return;
1172   }
1173
1174   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1175
1176   Vector2 offset;
1177   bool updateDecorator = false;
1178   if( primaryCursorPosition.x < 0.f )
1179   {
1180     offset.x = -primaryCursorPosition.x;
1181     mEventData->mScrollPosition.x += offset.x;
1182     updateDecorator = true;
1183   }
1184   else if( primaryCursorPosition.x > mControlSize.width )
1185   {
1186     offset.x = mControlSize.width - primaryCursorPosition.x;
1187     mEventData->mScrollPosition.x += offset.x;
1188     updateDecorator = true;
1189   }
1190
1191   if( updateDecorator && mEventData->mDecorator )
1192   {
1193     mEventData->mDecorator->UpdatePositions( offset );
1194   }
1195
1196   // TODO : calculate the vertical scroll.
1197 }
1198
1199 void Controller::Impl::RequestRelayout()
1200 {
1201   mControlInterface.RequestTextRelayout();
1202 }
1203
1204 } // namespace Text
1205
1206 } // namespace Toolkit
1207
1208 } // namespace Dali