Merge "Add more shared C++/JavaScript docs and add JavaScript wrapping guide" into...
[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 = utf32Characters.Count();
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     const unsigned int tapCount = event.p1.mUint;
461
462     if( 1u == tapCount )
463     {
464       if( ! IsShowingPlaceholderText() )
465       {
466         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
467         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
468
469         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
470                                                                     yPosition );
471       }
472       else
473       {
474         mEventData->mPrimaryCursorPosition = 0u;
475       }
476
477       mEventData->mUpdateCursorPosition = true;
478       mEventData->mScrollAfterUpdateCursorPosition = true;
479     }
480     else if( mEventData->mSelectionEnabled &&
481              ( 2u == tapCount ) )
482     {
483       RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
484     }
485   }
486 }
487
488 void Controller::Impl::OnPanEvent( const Event& event )
489 {
490   if( NULL == mEventData )
491   {
492     // Nothing to do if there is no text input.
493     return;
494   }
495
496   int state = event.p1.mInt;
497
498   if( Gesture::Started    == state ||
499       Gesture::Continuing == state )
500   {
501     const Vector2& actualSize = mVisualModel->GetActualSize();
502     const Vector2 currentScroll = mEventData->mScrollPosition;
503
504     if( mEventData->mHorizontalScrollingEnabled )
505     {
506       const float displacementX = event.p2.mFloat;
507       mEventData->mScrollPosition.x += displacementX;
508
509       ClampHorizontalScroll( actualSize );
510     }
511
512     if( mEventData->mVerticalScrollingEnabled )
513     {
514       const float displacementY = event.p3.mFloat;
515       mEventData->mScrollPosition.y += displacementY;
516
517       ClampVerticalScroll( actualSize );
518     }
519
520     if( mEventData->mDecorator )
521     {
522       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
523     }
524   }
525 }
526
527 void Controller::Impl::OnHandleEvent( const Event& event )
528 {
529   if( NULL == mEventData )
530   {
531     // Nothing to do if there is no text input.
532     return;
533   }
534
535   const unsigned int state = event.p1.mUint;
536
537   if( HANDLE_PRESSED == state )
538   {
539     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
540     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
541     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
542
543     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
544
545     if( Event::GRAB_HANDLE_EVENT == event.type )
546     {
547       ChangeState ( EventData::EDITING );
548
549       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
550       {
551         mEventData->mPrimaryCursorPosition = handleNewPosition;
552         mEventData->mUpdateCursorPosition = true;
553       }
554     }
555     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
556     {
557       if( handleNewPosition != mEventData->mLeftSelectionPosition )
558       {
559         mEventData->mLeftSelectionPosition = handleNewPosition;
560         mEventData->mUpdateLeftSelectionPosition = true;
561       }
562     }
563     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
564     {
565       if( handleNewPosition != mEventData->mRightSelectionPosition )
566       {
567         mEventData->mRightSelectionPosition = handleNewPosition;
568         mEventData->mUpdateRightSelectionPosition = true;
569       }
570     }
571   }
572   else if( ( HANDLE_RELEASED == state ) ||
573            ( HANDLE_STOP_SCROLLING == state ) )
574   {
575     if( mEventData->mGrabHandlePopupEnabled )
576     {
577       ChangeState( EventData::EDITING_WITH_POPUP );
578     }
579     if( Event::GRAB_HANDLE_EVENT == event.type )
580     {
581       mEventData->mUpdateCursorPosition = true;
582
583       if( HANDLE_STOP_SCROLLING == state )
584       {
585         // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
586         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
587         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
588
589         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
590
591         mEventData->mScrollAfterUpdateCursorPosition = true;
592       }
593     }
594     mEventData->mDecoratorUpdated = true;
595   }
596   else if( HANDLE_SCROLLING == state )
597   {
598     const float xSpeed = event.p2.mFloat;
599     const Vector2& actualSize = mVisualModel->GetActualSize();
600
601     mEventData->mScrollPosition.x += xSpeed;
602
603     ClampHorizontalScroll( actualSize );
604
605    mEventData->mDecoratorUpdated = true;
606   }
607 }
608
609 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
610 {
611   if( NULL == mEventData )
612   {
613     // Nothing to do if there is no text input.
614     return;
615   }
616
617   // TODO - Find which word was selected
618
619   const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
620   const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
621
622   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
623   const Vector<Vector2>::SizeType positionCount = positions.Count();
624
625   // Guard against glyphs which did not fit inside the layout
626   const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
627
628   if( count )
629   {
630     float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
631     float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
632
633     // TODO - multi-line selection
634     const Vector<LineRun>& lines = mVisualModel->mLines;
635     float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
636
637     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
638     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
639
640     mEventData->mDecorator->ClearHighlights();
641     mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
642   }
643 }
644
645 void Controller::Impl::ChangeState( EventData::State newState )
646 {
647   if( NULL == mEventData )
648   {
649     // Nothing to do if there is no text input.
650     return;
651   }
652
653   if( mEventData->mState != newState )
654   {
655     mEventData->mState = newState;
656
657     if( EventData::INACTIVE == mEventData->mState )
658     {
659       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
660       mEventData->mDecorator->StopCursorBlink();
661       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
662       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
663       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
664       mEventData->mDecorator->SetPopupActive( false );
665       mEventData->mDecoratorUpdated = true;
666     }
667     else if ( EventData::SELECTING == mEventData->mState )
668     {
669       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
670       mEventData->mDecorator->StopCursorBlink();
671       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
672       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
673       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
674       mEventData->mDecoratorUpdated = true;
675     }
676     else if( EventData::EDITING == mEventData->mState )
677     {
678       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
679       if( mEventData->mCursorBlinkEnabled )
680       {
681         mEventData->mDecorator->StartCursorBlink();
682       }
683       // Grab handle is not shown until a tap is received whilst EDITING
684       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
685       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
686       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
687       mEventData->mDecoratorUpdated = true;
688     }
689     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
690     {
691       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
692       if( mEventData->mCursorBlinkEnabled )
693       {
694         mEventData->mDecorator->StartCursorBlink();
695       }
696       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
697       if( mEventData->mSelectionEnabled )
698       {
699         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
700         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
701       }
702       if( mEventData->mGrabHandlePopupEnabled )
703       {
704         mEventData->mDecorator->SetPopupActive( true );
705       }
706       mEventData->mDecoratorUpdated = true;
707     }
708   }
709 }
710
711 LineIndex Controller::Impl::GetClosestLine( float y ) const
712 {
713   float totalHeight = 0.f;
714   LineIndex lineIndex = 0u;
715
716   const Vector<LineRun>& lines = mVisualModel->mLines;
717   for( LineIndex endLine = lines.Count();
718        lineIndex < endLine;
719        ++lineIndex )
720   {
721     const LineRun& lineRun = lines[lineIndex];
722     totalHeight += lineRun.ascender + -lineRun.descender;
723     if( y < totalHeight )
724     {
725       return lineIndex;
726     }
727   }
728
729   return lineIndex-1;
730 }
731
732 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
733                                                         float visualY )
734 {
735   if( NULL == mEventData )
736   {
737     // Nothing to do if there is no text input.
738     return 0u;
739   }
740
741   CharacterIndex logicalIndex = 0u;
742
743   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
744   const Length numberOfLines  = mVisualModel->mLines.Count();
745   if( 0 == numberOfGlyphs ||
746       0 == numberOfLines )
747   {
748     return logicalIndex;
749   }
750
751   // Find which line is closest
752   const LineIndex lineIndex = GetClosestLine( visualY );
753   const LineRun& line = mVisualModel->mLines[lineIndex];
754
755   // Get the positions of the glyphs.
756   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
757   const Vector2* const positionsBuffer = positions.Begin();
758
759   // Get the visual to logical conversion tables.
760   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
761   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
762
763   // Get the character to glyph conversion table.
764   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
765
766   // Get the glyphs per character table.
767   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
768
769   // If the vector is void, there is no right to left characters.
770   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
771
772   const CharacterIndex startCharacter = line.characterRun.characterIndex;
773   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
774   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
775
776   // Whether there is a hit on a glyph.
777   bool matched = false;
778
779   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
780   CharacterIndex visualIndex = startCharacter;
781   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
782   {
783     // The character in logical order.
784     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
785
786     // The first glyph for that character in logical order.
787     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
788
789     // The number of glyphs for that character
790     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
791
792     // Get the metrics for the group of glyphs.
793     GlyphMetrics glyphMetrics;
794     GetGlyphsMetrics( glyphLogicalOrderIndex,
795                       numberOfGlyphs,
796                       glyphMetrics,
797                       mVisualModel,
798                       mFontClient );
799
800     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
801
802     const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
803
804     if( visualX < glyphX )
805     {
806       matched = true;
807       break;
808     }
809   }
810
811   // Return the logical position of the cursor in characters.
812
813   if( !matched )
814   {
815     visualIndex = endCharacter;
816   }
817
818   return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
819 }
820
821 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
822                                           CursorInfo& cursorInfo )
823 {
824   // TODO: Check for multiline with \n, etc...
825
826   // Check if the logical position is the first or the last one of the text.
827   const bool isFirstPosition = 0u == logical;
828   const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
829
830   if( isFirstPosition && isLastPosition )
831   {
832     // There is zero characters. Get the default font.
833
834     FontId defaultFontId = 0u;
835     if( NULL == mFontDefaults )
836     {
837       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
838                                              EMPTY_STRING );
839     }
840     else
841     {
842       defaultFontId = mFontDefaults->GetFontId( mFontClient );
843     }
844
845     Text::FontMetrics fontMetrics;
846     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
847
848     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
849     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
850
851     cursorInfo.primaryPosition.x = 0.f;
852     cursorInfo.primaryPosition.y = 0.f;
853
854     // Nothing else to do.
855     return;
856   }
857
858   // Get the previous logical index.
859   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
860
861   // Decrease the logical index if it's the last one.
862   if( isLastPosition )
863   {
864     --logical;
865   }
866
867   // Get the direction of the character and the previous one.
868   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
869
870   CharacterDirection isCurrentRightToLeft = false;
871   CharacterDirection isPreviousRightToLeft = false;
872   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
873   {
874     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
875     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
876   }
877
878   // Get the line where the character is laid-out.
879   const LineRun* modelLines = mVisualModel->mLines.Begin();
880
881   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
882   const LineRun& line = *( modelLines + lineIndex );
883
884   // Get the paragraph's direction.
885   const CharacterDirection isRightToLeftParagraph = line.direction;
886
887   // Check whether there is an alternative position:
888
889   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
890     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
891
892   // Set the line height.
893   cursorInfo.lineHeight = line.ascender + -line.descender;
894
895   // Convert the cursor position into the glyph position.
896   CharacterIndex characterIndex = logical;
897   if( cursorInfo.isSecondaryCursor &&
898       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
899   {
900     characterIndex = previousLogical;
901   }
902
903   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
904   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
905   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
906
907   // Get the metrics for the group of glyphs.
908   GlyphMetrics glyphMetrics;
909   GetGlyphsMetrics( currentGlyphIndex,
910                     numberOfGlyphs,
911                     glyphMetrics,
912                     mVisualModel,
913                     mFontClient );
914
915   float interGlyphAdvance = 0.f;
916   if( !isLastPosition &&
917       ( numberOfCharacters > 1u ) )
918   {
919     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
920     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
921   }
922
923   // Get the glyph position and x bearing.
924   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
925
926   // Set the cursor's height.
927   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
928
929   // Set the position.
930   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
931   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
932
933   if( isLastPosition )
934   {
935     // The position of the cursor after the last character needs special
936     // care depending on its direction and the direction of the paragraph.
937
938     if( cursorInfo.isSecondaryCursor )
939     {
940       // Need to find the first character after the last character with the paragraph's direction.
941       // i.e l0 l1 l2 r0 r1 should find r0.
942
943       // TODO: check for more than one line!
944       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
945       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
946
947       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
948       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
949
950       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
951
952       // Get the metrics for the group of glyphs.
953       GlyphMetrics glyphMetrics;
954       GetGlyphsMetrics( glyphIndex,
955                         numberOfGlyphs,
956                         glyphMetrics,
957                         mVisualModel,
958                         mFontClient );
959
960       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
961
962       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
963     }
964     else
965     {
966       if( !isCurrentRightToLeft )
967       {
968         cursorInfo.primaryPosition.x += glyphMetrics.advance;
969       }
970       else
971       {
972         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
973       }
974     }
975   }
976
977   // Set the alternative cursor position.
978   if( cursorInfo.isSecondaryCursor )
979   {
980     // Convert the cursor position into the glyph position.
981     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
982     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
983     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
984
985     // Get the glyph position.
986     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
987
988     // Get the metrics for the group of glyphs.
989     GlyphMetrics glyphMetrics;
990     GetGlyphsMetrics( previousGlyphIndex,
991                       numberOfGlyphs,
992                       glyphMetrics,
993                       mVisualModel,
994                       mFontClient );
995
996     // Set the cursor position and height.
997     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
998                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
999
1000     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1001
1002     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1003
1004     // Update the primary cursor height as well.
1005     cursorInfo.primaryCursorHeight *= 0.5f;
1006   }
1007 }
1008
1009 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1010 {
1011   if( NULL == mEventData )
1012   {
1013     // Nothing to do if there is no text input.
1014     return 0u;
1015   }
1016
1017   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1018
1019   const Script script = mLogicalModel->GetScript( index );
1020   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1021   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1022
1023   Length numberOfCharacters = 0u;
1024   if( TextAbstraction::LATIN == script )
1025   {
1026     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1027     numberOfCharacters = 1u;
1028   }
1029   else
1030   {
1031     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1032     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1033
1034     while( 0u == numberOfCharacters )
1035     {
1036       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1037       ++glyphIndex;
1038     }
1039   }
1040
1041   if( index < mEventData->mPrimaryCursorPosition )
1042   {
1043     cursorIndex -= numberOfCharacters;
1044   }
1045   else
1046   {
1047     cursorIndex += numberOfCharacters;
1048   }
1049
1050   return cursorIndex;
1051 }
1052
1053 void Controller::Impl::UpdateCursorPosition()
1054 {
1055   if( NULL == mEventData )
1056   {
1057     // Nothing to do if there is no text input.
1058     return;
1059   }
1060
1061   CursorInfo cursorInfo;
1062   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1063                      cursorInfo );
1064
1065   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1066   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1067
1068   // Sets the cursor position.
1069   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1070                                        cursorPosition.x,
1071                                        cursorPosition.y,
1072                                        cursorInfo.primaryCursorHeight,
1073                                        cursorInfo.lineHeight );
1074
1075   // Sets the grab handle position.
1076   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1077                                        cursorPosition.x,
1078                                        cursorPosition.y,
1079                                        cursorInfo.lineHeight );
1080
1081   if( cursorInfo.isSecondaryCursor )
1082   {
1083     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1084     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1085                                          cursorInfo.secondaryPosition.x + offset.x,
1086                                          cursorInfo.secondaryPosition.y + offset.y,
1087                                          cursorInfo.secondaryCursorHeight,
1088                                          cursorInfo.lineHeight );
1089   }
1090   else
1091   {
1092     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1093   }
1094 }
1095
1096 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1097 {
1098   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1099       ( RIGHT_SELECTION_HANDLE != handleType ) )
1100   {
1101     return;
1102   }
1103
1104   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1105   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1106
1107   CursorInfo cursorInfo;
1108   GetCursorPosition( index,
1109                      cursorInfo );
1110
1111   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1112   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1113
1114   // Sets the grab handle position.
1115   mEventData->mDecorator->SetPosition( handleType,
1116                                        cursorPosition.x,
1117                                        cursorPosition.y,
1118                                        cursorInfo.lineHeight );
1119 }
1120
1121 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1122 {
1123   // Clamp between -space & 0 (and the text alignment).
1124   if( actualSize.width > mControlSize.width )
1125   {
1126     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1127     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1128     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1129
1130     mEventData->mDecoratorUpdated = true;
1131   }
1132   else
1133   {
1134     mEventData->mScrollPosition.x = 0.f;
1135   }
1136 }
1137
1138 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1139 {
1140   // Clamp between -space & 0 (and the text alignment).
1141   if( actualSize.height > mControlSize.height )
1142   {
1143     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1144     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1145     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1146
1147     mEventData->mDecoratorUpdated = true;
1148   }
1149   else
1150   {
1151     mEventData->mScrollPosition.y = 0.f;
1152   }
1153 }
1154
1155 void Controller::Impl::ScrollToMakeCursorVisible()
1156 {
1157   if( NULL == mEventData )
1158   {
1159     // Nothing to do if there is no text input.
1160     return;
1161   }
1162
1163   const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1164
1165   Vector2 offset;
1166   bool updateDecorator = false;
1167   if( primaryCursorPosition.x < 0.f )
1168   {
1169     offset.x = -primaryCursorPosition.x;
1170     mEventData->mScrollPosition.x += offset.x;
1171     updateDecorator = true;
1172   }
1173   else if( primaryCursorPosition.x > mControlSize.width )
1174   {
1175     offset.x = mControlSize.width - primaryCursorPosition.x;
1176     mEventData->mScrollPosition.x += offset.x;
1177     updateDecorator = true;
1178   }
1179
1180   if( updateDecorator && mEventData->mDecorator )
1181   {
1182     mEventData->mDecorator->UpdatePositions( offset );
1183   }
1184
1185   // TODO : calculate the vertical scroll.
1186 }
1187
1188 void Controller::Impl::RequestRelayout()
1189 {
1190   mControlInterface.RequestTextRelayout();
1191 }
1192
1193 } // namespace Text
1194
1195 } // namespace Toolkit
1196
1197 } // namespace Dali