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