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