412a3175d2f704bb9e9f0ad2381dd5c7c2b599b1
[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/multi-language-support.h>
29 #include <dali-toolkit/internal/text/segmentation.h>
30 #include <dali-toolkit/internal/text/shaper.h>
31
32 namespace
33 {
34
35 #if defined(DEBUG_ENABLED)
36   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
37 #endif
38
39 /**
40  * @brief Some characters can be shaped in more than one glyph.
41  * This struct is used to retrieve metrics from these group of glyphs.
42  */
43 struct GlyphMetrics
44 {
45   GlyphMetrics()
46   : fontHeight( 0.f ),
47     advance( 0.f ),
48     ascender( 0.f ),
49     xBearing( 0.f )
50   {}
51
52   ~GlyphMetrics()
53   {}
54
55   float fontHeight; ///< The font's height of that glyphs.
56   float advance;    ///< The sum of all the advances of all the glyphs.
57   float ascender;   ///< The font's ascender.
58   float xBearing;   ///< The x bearing of the first glyph.
59 };
60
61 } // namespace
62
63 namespace Dali
64 {
65
66 namespace Toolkit
67 {
68
69 namespace Text
70 {
71
72 /**
73  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
74  *
75  * @param[in] glyphIndex The index to the first glyph.
76  * @param[in] numberOfGlyphs The number of glyphs.
77  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
78  * @param[in] visualModel The visual model.
79  * @param[in] metrics Used to access metrics from FontClient.
80  */
81 void GetGlyphsMetrics( GlyphIndex glyphIndex,
82                        Length numberOfGlyphs,
83                        GlyphMetrics& glyphMetrics,
84                        VisualModelPtr& visualModel,
85                        MetricsPtr& metrics )
86 {
87   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
88
89   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
90
91   Text::FontMetrics fontMetrics;
92   metrics->GetFontMetrics( firstGlyph.fontId, fontMetrics );
93
94   glyphMetrics.fontHeight = fontMetrics.height;
95   glyphMetrics.advance = firstGlyph.advance;
96   glyphMetrics.ascender = fontMetrics.ascender;
97   glyphMetrics.xBearing = firstGlyph.xBearing;
98
99   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
100   {
101     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
102
103     glyphMetrics.advance += glyphInfo.advance;
104   }
105 }
106
107 EventData::EventData( DecoratorPtr decorator )
108 : mDecorator( decorator ),
109   mPlaceholderTextActive(),
110   mPlaceholderTextInactive(),
111   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
112   mEventQueue(),
113   mScrollPosition(),
114   mState( INACTIVE ),
115   mPrimaryCursorPosition( 0u ),
116   mLeftSelectionPosition( 0u ),
117   mRightSelectionPosition( 0u ),
118   mPreEditStartPosition( 0u ),
119   mPreEditLength( 0u ),
120   mIsShowingPlaceholderText( false ),
121   mPreEditFlag( false ),
122   mDecoratorUpdated( false ),
123   mCursorBlinkEnabled( true ),
124   mGrabHandleEnabled( true ),
125   mGrabHandlePopupEnabled( true ),
126   mSelectionEnabled( true ),
127   mHorizontalScrollingEnabled( true ),
128   mVerticalScrollingEnabled( false ),
129   mUpdateCursorPosition( false ),
130   mUpdateLeftSelectionPosition( false ),
131   mUpdateRightSelectionPosition( false ),
132   mScrollAfterUpdatePosition( false ),
133   mScrollAfterDelete( false ),
134   mAllTextSelected( false )
135 {}
136
137 EventData::~EventData()
138 {}
139
140 bool Controller::Impl::ProcessInputEvents()
141 {
142   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
143   if( NULL == mEventData )
144   {
145     // Nothing to do if there is no text input.
146     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
147     return false;
148   }
149
150   if( mEventData->mDecorator )
151   {
152     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
153          iter != mEventData->mEventQueue.end();
154          ++iter )
155     {
156       switch( iter->type )
157       {
158         case Event::CURSOR_KEY_EVENT:
159         {
160           OnCursorKeyEvent( *iter );
161           break;
162         }
163         case Event::TAP_EVENT:
164         {
165           OnTapEvent( *iter );
166           break;
167         }
168         case Event::LONG_PRESS_EVENT:
169         {
170           OnLongPressEvent( *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   // The cursor must also be repositioned after inserts into the model
200   if( mEventData->mUpdateCursorPosition )
201   {
202     // Updates the cursor position and scrolls the text to make it visible.
203     CursorInfo cursorInfo;
204     GetCursorPosition( mEventData->mPrimaryCursorPosition,
205                        cursorInfo );
206
207     if( mEventData->mScrollAfterUpdatePosition )
208     {
209       ScrollToMakePositionVisible( cursorInfo.primaryPosition );
210       mEventData->mScrollAfterUpdatePosition = false;
211     }
212     else if( mEventData->mScrollAfterDelete )
213     {
214       ScrollTextToMatchCursor( cursorInfo );
215       mEventData->mScrollAfterDelete = false;
216     }
217
218     UpdateCursorPosition( cursorInfo );
219
220     mEventData->mDecoratorUpdated = true;
221     mEventData->mUpdateCursorPosition = false;
222   }
223   else
224   {
225     bool leftScroll = false;
226     bool rightScroll = false;
227
228     CursorInfo leftHandleInfo;
229     CursorInfo rightHandleInfo;
230
231     if( mEventData->mUpdateLeftSelectionPosition )
232     {
233       GetCursorPosition( mEventData->mLeftSelectionPosition,
234                          leftHandleInfo );
235
236       if( mEventData->mScrollAfterUpdatePosition )
237       {
238         ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
239         leftScroll = true;
240       }
241     }
242
243     if( mEventData->mUpdateRightSelectionPosition )
244     {
245       GetCursorPosition( mEventData->mRightSelectionPosition,
246                          rightHandleInfo );
247
248       if( mEventData->mScrollAfterUpdatePosition )
249       {
250         ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
251         rightScroll = true;
252       }
253     }
254
255     if( mEventData->mUpdateLeftSelectionPosition )
256     {
257       UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
258                              leftHandleInfo );
259
260       SetPopupButtons();
261       mEventData->mDecoratorUpdated = true;
262     }
263
264     if( mEventData->mUpdateRightSelectionPosition )
265     {
266       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
267                              rightHandleInfo );
268
269       SetPopupButtons();
270       mEventData->mDecoratorUpdated = true;
271     }
272
273     if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
274     {
275       RepositionSelectionHandles();
276
277       mEventData->mUpdateLeftSelectionPosition = false;
278       mEventData->mUpdateRightSelectionPosition = false;
279     }
280
281     if( leftScroll || rightScroll )
282     {
283       mEventData->mScrollAfterUpdatePosition = false;
284     }
285   }
286
287   mEventData->mEventQueue.clear();
288
289   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
290
291   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
292   mEventData->mDecoratorUpdated = false;
293
294   return decoratorUpdated;
295 }
296
297 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
298 {
299   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
300
301   // Calculate the operations to be done.
302   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
303
304   Vector<Character>& utf32Characters = mLogicalModel->mText;
305
306   const Length numberOfCharacters = utf32Characters.Count();
307
308   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
309   if( GET_LINE_BREAKS & operations )
310   {
311     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
312     // calculate the bidirectional info for each 'paragraph'.
313     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
314     // is not shaped together).
315     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
316
317     SetLineBreakInfo( utf32Characters,
318                       lineBreakInfo );
319   }
320
321   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
322   if( GET_WORD_BREAKS & operations )
323   {
324     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
325     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
326
327     SetWordBreakInfo( utf32Characters,
328                       wordBreakInfo );
329   }
330
331   const bool getScripts = GET_SCRIPTS & operations;
332   const bool validateFonts = VALIDATE_FONTS & operations;
333
334   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
335   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
336
337   if( getScripts || validateFonts )
338   {
339     // Validates the fonts assigned by the application or assigns default ones.
340     // It makes sure all the characters are going to be rendered by the correct font.
341     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
342
343     if( getScripts )
344     {
345       // Retrieves the scripts used in the text.
346       multilanguageSupport.SetScripts( utf32Characters,
347                                        scripts );
348     }
349
350     if( validateFonts )
351     {
352       if( 0u == validFonts.Count() )
353       {
354         // Copy the requested font defaults received via the property system.
355         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
356         GetDefaultFonts( validFonts, numberOfCharacters );
357       }
358
359       // Validates the fonts. If there is a character with no assigned font it sets a default one.
360       // After this call, fonts are validated.
361       multilanguageSupport.ValidateFonts( utf32Characters,
362                                           scripts,
363                                           validFonts );
364     }
365   }
366
367   Vector<Character> mirroredUtf32Characters;
368   bool textMirrored = false;
369   Length numberOfParagraphs = 0u;
370   if( BIDI_INFO & operations )
371   {
372     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
373     // bidirectional info.
374
375     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
376     for( Length index = 0u; index < numberOfCharacters; ++index )
377     {
378       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
379       {
380         ++numberOfParagraphs;
381       }
382     }
383
384     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
385     bidirectionalInfo.Reserve( numberOfParagraphs );
386
387     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
388     SetBidirectionalInfo( utf32Characters,
389                           scripts,
390                           lineBreakInfo,
391                           bidirectionalInfo );
392
393     if( 0u != bidirectionalInfo.Count() )
394     {
395       // This paragraph has right to left text. Some characters may need to be mirrored.
396       // TODO: consider if the mirrored string can be stored as well.
397
398       textMirrored = GetMirroredText( utf32Characters,
399                                       mirroredUtf32Characters,
400                                       bidirectionalInfo );
401
402       // Only set the character directions if there is right to left characters.
403       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
404       directions.Resize( numberOfCharacters );
405
406       GetCharactersDirection( bidirectionalInfo,
407                               directions );
408     }
409     else
410     {
411       // There is no right to left characters. Clear the directions vector.
412       mLogicalModel->mCharacterDirections.Clear();
413     }
414   }
415
416   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
417   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
418   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
419   Vector<GlyphIndex> newParagraphGlyphs;
420   newParagraphGlyphs.Reserve( numberOfParagraphs );
421
422   if( SHAPE_TEXT & operations )
423   {
424     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
425     // Shapes the text.
426     ShapeText( textToShape,
427                lineBreakInfo,
428                scripts,
429                validFonts,
430                glyphs,
431                glyphsToCharactersMap,
432                charactersPerGlyph,
433                newParagraphGlyphs );
434
435     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
436     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
437     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
438   }
439
440   const Length numberOfGlyphs = glyphs.Count();
441
442   if( GET_GLYPH_METRICS & operations )
443   {
444     GlyphInfo* glyphsBuffer = glyphs.Begin();
445     mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
446
447     // Update the width and advance of all new paragraph characters.
448     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
449     {
450       const GlyphIndex index = *it;
451       GlyphInfo& glyph = *( glyphsBuffer + index );
452
453       glyph.xBearing = 0.f;
454       glyph.width = 0.f;
455       glyph.advance = 0.f;
456     }
457   }
458
459   if( mEventData &&
460       mEventData->mPreEditFlag &&
461       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
462   {
463     // Add the underline for the pre-edit text.
464     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
465     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
466
467     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
468     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
469     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
470     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
471
472     GlyphRun underlineRun;
473     underlineRun.glyphIndex = glyphStart;
474     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
475
476     // TODO: At the moment the underline runs are only for pre-edit.
477     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
478   }
479 }
480
481 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
482 {
483   if( mFontDefaults )
484   {
485     DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::GetDefaultFonts font family(%s)\n", mFontDefaults->mFontDescription.family.c_str() );
486     FontRun fontRun;
487     fontRun.characterRun.characterIndex = 0;
488     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
489     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
490     fontRun.isDefault = true;
491
492     fonts.PushBack( fontRun );
493   }
494 }
495
496 float Controller::Impl::GetDefaultFontLineHeight()
497 {
498   FontId defaultFontId = 0u;
499   if( NULL == mFontDefaults )
500   {
501     TextAbstraction::FontDescription fontDescription;
502     defaultFontId = mFontClient.GetFontId( fontDescription );
503   }
504   else
505   {
506     defaultFontId = mFontDefaults->GetFontId( mFontClient );
507   }
508
509   Text::FontMetrics fontMetrics;
510   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
511
512   return( fontMetrics.ascender - fontMetrics.descender );
513 }
514
515 void Controller::Impl::OnCursorKeyEvent( const Event& event )
516 {
517   if( NULL == mEventData )
518   {
519     // Nothing to do if there is no text input.
520     return;
521   }
522
523   int keyCode = event.p1.mInt;
524
525   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
526   {
527     if( mEventData->mPrimaryCursorPosition > 0u )
528     {
529       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
530     }
531   }
532   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
533   {
534     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
535     {
536       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
537     }
538   }
539   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
540   {
541     // TODO
542   }
543   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
544   {
545     // TODO
546   }
547
548   mEventData->mUpdateCursorPosition = true;
549   mEventData->mScrollAfterUpdatePosition = true;
550 }
551
552 void Controller::Impl::OnTapEvent( const Event& event )
553 {
554   if( NULL != mEventData )
555   {
556     const unsigned int tapCount = event.p1.mUint;
557
558     if( 1u == tapCount )
559     {
560       if( IsShowingRealText() )
561       {
562         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
563         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
564
565         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
566                                                                     yPosition );
567
568         // When the cursor position is changing, delay cursor blinking
569         mEventData->mDecorator->DelayCursorBlink();
570       }
571       else
572       {
573         mEventData->mPrimaryCursorPosition = 0u;
574       }
575
576       mEventData->mUpdateCursorPosition = true;
577       mEventData->mScrollAfterUpdatePosition = true;
578     }
579   }
580 }
581
582 void Controller::Impl::OnPanEvent( const Event& event )
583 {
584   if( NULL == mEventData )
585   {
586     // Nothing to do if there is no text input.
587     return;
588   }
589
590   int state = event.p1.mInt;
591
592   if( Gesture::Started    == state ||
593       Gesture::Continuing == state )
594   {
595     const Vector2& actualSize = mVisualModel->GetActualSize();
596     const Vector2 currentScroll = mEventData->mScrollPosition;
597
598     if( mEventData->mHorizontalScrollingEnabled )
599     {
600       const float displacementX = event.p2.mFloat;
601       mEventData->mScrollPosition.x += displacementX;
602
603       ClampHorizontalScroll( actualSize );
604     }
605
606     if( mEventData->mVerticalScrollingEnabled )
607     {
608       const float displacementY = event.p3.mFloat;
609       mEventData->mScrollPosition.y += displacementY;
610
611       ClampVerticalScroll( actualSize );
612     }
613
614     if( mEventData->mDecorator )
615     {
616       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
617     }
618   }
619 }
620
621 void Controller::Impl::OnLongPressEvent( const Event& event )
622 {
623   if  ( EventData::EDITING == mEventData->mState )
624   {
625     ChangeState ( EventData::EDITING_WITH_POPUP );
626     mEventData->mDecoratorUpdated = true;
627   }
628 }
629
630 void Controller::Impl::OnHandleEvent( const Event& event )
631 {
632   if( NULL == mEventData )
633   {
634     // Nothing to do if there is no text input.
635     return;
636   }
637
638   const unsigned int state = event.p1.mUint;
639   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
640
641   if( HANDLE_PRESSED == state )
642   {
643     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
644     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
645     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
646
647     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
648
649     if( Event::GRAB_HANDLE_EVENT == event.type )
650     {
651       ChangeState ( EventData::GRAB_HANDLE_PANNING );
652
653       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
654       {
655         mEventData->mPrimaryCursorPosition = handleNewPosition;
656         mEventData->mUpdateCursorPosition = true;
657       }
658     }
659     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
660     {
661       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
662
663       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
664           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
665       {
666         mEventData->mLeftSelectionPosition = handleNewPosition;
667
668         mEventData->mUpdateLeftSelectionPosition = true;
669       }
670     }
671     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
672     {
673       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
674
675       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
676           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
677       {
678         mEventData->mRightSelectionPosition = handleNewPosition;
679
680         mEventData->mUpdateRightSelectionPosition = true;
681       }
682     }
683   } // end ( HANDLE_PRESSED == state )
684   else if( ( HANDLE_RELEASED == state ) ||
685            handleStopScrolling )
686   {
687     CharacterIndex handlePosition = 0u;
688     if( handleStopScrolling )
689     {
690       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
691       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
692       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
693
694       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
695     }
696
697     if( Event::GRAB_HANDLE_EVENT == event.type )
698     {
699       mEventData->mUpdateCursorPosition = true;
700
701       ChangeState( EventData::EDITING_WITH_POPUP );
702
703       if( handleStopScrolling )
704       {
705         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
706         mEventData->mPrimaryCursorPosition = handlePosition;
707       }
708     }
709     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
710     {
711       ChangeState( EventData::SELECTING );
712
713       if( handleStopScrolling )
714       {
715         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
716         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
717
718         if( mEventData->mUpdateLeftSelectionPosition )
719         {
720           mEventData->mLeftSelectionPosition = handlePosition;
721         }
722       }
723     }
724     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
725     {
726       ChangeState( EventData::SELECTING );
727
728       if( handleStopScrolling )
729       {
730         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
731         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
732         if( mEventData->mUpdateRightSelectionPosition )
733         {
734           mEventData->mRightSelectionPosition = handlePosition;
735         }
736       }
737     }
738
739     mEventData->mDecoratorUpdated = true;
740   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
741   else if( HANDLE_SCROLLING == state )
742   {
743     const float xSpeed = event.p2.mFloat;
744     const Vector2& actualSize = mVisualModel->GetActualSize();
745     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
746
747     mEventData->mScrollPosition.x += xSpeed;
748
749     ClampHorizontalScroll( actualSize );
750
751     bool endOfScroll = false;
752     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
753     {
754       // Notify the decorator there is no more text to scroll.
755       // The decorator won't send more scroll events.
756       mEventData->mDecorator->NotifyEndOfScroll();
757       // Still need to set the position of the handle.
758       endOfScroll = true;
759     }
760
761     // Set the position of the handle.
762     const bool scrollRightDirection = xSpeed > 0.f;
763     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
764     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
765
766     if( Event::GRAB_HANDLE_EVENT == event.type )
767     {
768       ChangeState( EventData::GRAB_HANDLE_PANNING );
769
770       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
771
772       // Position the grag handle close to either the left or right edge.
773       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
774
775       // Get the new handle position.
776       // The grab handle's position is in decorator coords. Need to transforms to text coords.
777       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
778                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
779
780       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
781       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
782       mEventData->mPrimaryCursorPosition = handlePosition;
783     }
784     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
785     {
786       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
787       //       Think if something can be done to save power.
788
789       ChangeState( EventData::SELECTION_HANDLE_PANNING );
790
791       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
792
793       // Position the selection handle close to either the left or right edge.
794       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
795
796       // Get the new handle position.
797       // The selection handle's position is in decorator coords. Need to transforms to text coords.
798       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
799                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
800
801       if( leftSelectionHandleEvent )
802       {
803         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
804         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
805         if( differentHandles )
806         {
807           mEventData->mLeftSelectionPosition = handlePosition;
808         }
809       }
810       else
811       {
812         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
813         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
814         if( differentHandles )
815         {
816           mEventData->mRightSelectionPosition = handlePosition;
817         }
818       }
819
820       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
821       {
822         RepositionSelectionHandles();
823
824         mEventData->mScrollAfterUpdatePosition = true;
825       }
826     }
827     mEventData->mDecoratorUpdated = true;
828   } // end ( HANDLE_SCROLLING == state )
829 }
830
831 void Controller::Impl::OnSelectEvent( const Event& event )
832 {
833   if( NULL == mEventData )
834   {
835     // Nothing to do if there is no text.
836     return;
837   }
838
839   if( mEventData->mSelectionEnabled )
840   {
841     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
842     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
843     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
844
845     // Calculates the logical position from the x,y coords.
846     RepositionSelectionHandles( xPosition,
847                                 yPosition );
848
849     mEventData->mUpdateLeftSelectionPosition = true;
850     mEventData->mUpdateRightSelectionPosition = true;
851
852     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
853   }
854 }
855
856 void Controller::Impl::OnSelectAllEvent()
857 {
858   if( NULL == mEventData )
859   {
860     // Nothing to do if there is no text.
861     return;
862   }
863
864   if( mEventData->mSelectionEnabled )
865   {
866     mEventData->mLeftSelectionPosition = 0u;
867     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
868
869     mEventData->mScrollAfterUpdatePosition = true;
870     mEventData->mUpdateLeftSelectionPosition = true;
871     mEventData->mUpdateRightSelectionPosition = true;
872   }
873 }
874
875 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
876 {
877   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
878   {
879     // Nothing to select if handles are in the same place.
880     selectedText="";
881     return;
882   }
883
884   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
885
886   //Get start and end position of selection
887   uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
888   uint32_t lengthOfSelectedText =  ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
889
890   // Validate the start and end selection points
891   if(  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() )
892   {
893     //Get text as a UTF8 string
894     Vector<Character>& utf32Characters = mLogicalModel->mText;
895
896     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
897
898     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
899     {
900       // Delete text between handles
901       Vector<Character>& currentText = mLogicalModel->mText;
902
903       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
904       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
905       currentText.Erase( first, last );
906
907       // Scroll after delete.
908       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
909       mEventData->mScrollAfterDelete = true;
910     }
911     // Udpade the cursor position and the decorator.
912     // Scroll after the position is updated if is not scrolling after delete.
913     mEventData->mUpdateCursorPosition = true;
914     mEventData->mScrollAfterUpdatePosition = !mEventData->mScrollAfterDelete;
915     mEventData->mDecoratorUpdated = true;
916   }
917 }
918
919 void Controller::Impl::ShowClipboard()
920 {
921   if ( mClipboard )
922   {
923     mClipboard.ShowClipboard();
924   }
925 }
926
927 void Controller::Impl::HideClipboard()
928 {
929   if ( mClipboard )
930   {
931     mClipboard.HideClipboard();
932   }
933 }
934
935 bool Controller::Impl::CopyStringToClipboard( std::string& source )
936 {
937   //Send string to clipboard
938   return ( mClipboard && mClipboard.SetItem( source ) );
939 }
940
941 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
942 {
943   std::string selectedText;
944   RetrieveSelection( selectedText, deleteAfterSending );
945   CopyStringToClipboard( selectedText );
946   ChangeState( EventData::EDITING );
947 }
948
949 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
950 {
951   if ( mClipboard )
952   {
953     retreivedString =  mClipboard.GetItem( itemIndex );
954   }
955 }
956
957 void Controller::Impl::RepositionSelectionHandles()
958 {
959   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
960   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
961
962   if( selectionStart == selectionEnd )
963   {
964     // Nothing to select if handles are in the same place.
965     return;
966   }
967
968   mEventData->mDecorator->ClearHighlights();
969
970   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
971   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
972   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
973   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
974   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
975   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
976   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
977
978   // TODO: Better algorithm to create the highlight box.
979   // TODO: Multi-line.
980
981   // Get the height of the line.
982   const Vector<LineRun>& lines = mVisualModel->mLines;
983   const LineRun& firstLine = *lines.Begin();
984   const float height = firstLine.ascender + -firstLine.descender;
985
986   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
987   const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
988   const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
989
990   // Swap the indices if the start is greater than the end.
991   const bool indicesSwapped = selectionStart > selectionEnd;
992
993   // Tell the decorator to flip the selection handles if needed.
994   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
995
996   if( indicesSwapped )
997   {
998     std::swap( selectionStart, selectionEnd );
999   }
1000
1001   // Get the indices to the first and last selected glyphs.
1002   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1003   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1004   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1005   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1006
1007   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1008   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1009   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1010
1011   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1012   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1013   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1014
1015   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1016
1017   // Traverse the glyphs.
1018   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1019   {
1020     const GlyphInfo& glyph = *( glyphsBuffer + index );
1021     const Vector2& position = *( positionsBuffer + index );
1022
1023     if( splitStartGlyph )
1024     {
1025       // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
1026
1027       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1028       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1029       // Get the direction of the character.
1030       CharacterDirection isCurrentRightToLeft = false;
1031       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1032       {
1033         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1034       }
1035
1036       // The end point could be in the middle of the ligature.
1037       // Calculate the number of characters selected.
1038       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1039
1040       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1041
1042       mEventData->mDecorator->AddHighlight( xPosition,
1043                                             offset.y,
1044                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1045                                             offset.y + height );
1046
1047       splitStartGlyph = false;
1048       continue;
1049     }
1050
1051     if( splitEndGlyph && ( index == glyphEnd ) )
1052     {
1053       // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
1054
1055       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1056       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1057       // Get the direction of the character.
1058       CharacterDirection isCurrentRightToLeft = false;
1059       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1060       {
1061         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1062       }
1063
1064       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1065
1066       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1067       mEventData->mDecorator->AddHighlight( xPosition,
1068                                             offset.y,
1069                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1070                                             offset.y + height );
1071
1072       splitEndGlyph = false;
1073       continue;
1074     }
1075
1076     const float xPosition = position.x - glyph.xBearing + offset.x;
1077     mEventData->mDecorator->AddHighlight( xPosition,
1078                                           offset.y,
1079                                           xPosition + glyph.advance,
1080                                           offset.y + height );
1081   }
1082
1083   CursorInfo primaryCursorInfo;
1084   GetCursorPosition( mEventData->mLeftSelectionPosition,
1085                      primaryCursorInfo );
1086
1087   CursorInfo secondaryCursorInfo;
1088   GetCursorPosition( mEventData->mRightSelectionPosition,
1089                      secondaryCursorInfo );
1090
1091   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1092   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1093
1094   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
1095
1096   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
1097
1098   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1099   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1100
1101   // Set the flag to update the decorator.
1102   mEventData->mDecoratorUpdated = true;
1103 }
1104
1105 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1106 {
1107   if( NULL == mEventData )
1108   {
1109     // Nothing to do if there is no text input.
1110     return;
1111   }
1112
1113   if( IsShowingPlaceholderText() )
1114   {
1115     // Nothing to do if there is the place-holder text.
1116     return;
1117   }
1118
1119   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1120   const Length numberOfLines  = mVisualModel->mLines.Count();
1121   if( 0 == numberOfGlyphs ||
1122       0 == numberOfLines )
1123   {
1124     // Nothing to do if there is no text.
1125     return;
1126   }
1127
1128   // Find which word was selected
1129   CharacterIndex selectionStart( 0 );
1130   CharacterIndex selectionEnd( 0 );
1131   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1132   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1133
1134   if( selectionStart == selectionEnd )
1135   {
1136     ChangeState( EventData::EDITING );
1137     // Nothing to select. i.e. a white space, out of bounds
1138     return;
1139   }
1140
1141   mEventData->mLeftSelectionPosition = selectionStart;
1142   mEventData->mRightSelectionPosition = selectionEnd;
1143 }
1144
1145 void Controller::Impl::SetPopupButtons()
1146 {
1147   /**
1148    *  Sets the Popup buttons to be shown depending on State.
1149    *
1150    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1151    *
1152    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1153    */
1154
1155   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1156
1157   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1158   {
1159     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1160
1161     if ( !IsClipboardEmpty() )
1162     {
1163       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1164       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1165     }
1166
1167     if ( !mEventData->mAllTextSelected )
1168     {
1169       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1170     }
1171   }
1172   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1173   {
1174     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1175     {
1176       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1177     }
1178
1179     if ( !IsClipboardEmpty() )
1180     {
1181       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1182       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1183     }
1184   }
1185
1186   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1187 }
1188
1189 void Controller::Impl::ChangeState( EventData::State newState )
1190 {
1191   if( NULL == mEventData )
1192   {
1193     // Nothing to do if there is no text input.
1194     return;
1195   }
1196
1197   if( mEventData->mState != newState )
1198   {
1199     mEventData->mState = newState;
1200
1201     if( EventData::INACTIVE == mEventData->mState )
1202     {
1203       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1204       mEventData->mDecorator->StopCursorBlink();
1205       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1206       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1207       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1208       mEventData->mDecorator->SetPopupActive( false );
1209       mEventData->mDecoratorUpdated = true;
1210       HideClipboard();
1211     }
1212     else if ( EventData::INTERRUPTED  == mEventData->mState)
1213     {
1214       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1215       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1216       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1217       mEventData->mDecorator->SetPopupActive( false );
1218       mEventData->mDecoratorUpdated = true;
1219       HideClipboard();
1220     }
1221     else if ( EventData::SELECTING == mEventData->mState )
1222     {
1223       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1224       mEventData->mDecorator->StopCursorBlink();
1225       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1226       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1227       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1228       if( mEventData->mGrabHandlePopupEnabled )
1229       {
1230         SetPopupButtons();
1231         mEventData->mDecorator->SetPopupActive( true );
1232       }
1233       mEventData->mDecoratorUpdated = true;
1234     }
1235     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1236     {
1237       if( mEventData->mGrabHandlePopupEnabled )
1238       {
1239         SetPopupButtons();
1240         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1241         mEventData->mDecorator->SetPopupActive( true );
1242       }
1243       mEventData->mDecoratorUpdated = true;
1244     }
1245     else if( EventData::EDITING == mEventData->mState )
1246     {
1247       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1248       if( mEventData->mCursorBlinkEnabled )
1249       {
1250         mEventData->mDecorator->StartCursorBlink();
1251       }
1252       // Grab handle is not shown until a tap is received whilst EDITING
1253       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1254       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1255       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1256       if( mEventData->mGrabHandlePopupEnabled )
1257       {
1258         mEventData->mDecorator->SetPopupActive( false );
1259       }
1260       mEventData->mDecoratorUpdated = true;
1261       HideClipboard();
1262     }
1263     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1264     {
1265       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1266       if( mEventData->mCursorBlinkEnabled )
1267       {
1268         mEventData->mDecorator->StartCursorBlink();
1269       }
1270       if( mEventData->mSelectionEnabled )
1271       {
1272         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1273         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1274       }
1275       else
1276       {
1277         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1278       }
1279       if( mEventData->mGrabHandlePopupEnabled )
1280       {
1281         SetPopupButtons();
1282         mEventData->mDecorator->SetPopupActive( true );
1283       }
1284       HideClipboard();
1285       mEventData->mDecoratorUpdated = true;
1286     }
1287     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1288     {
1289       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1290       if( mEventData->mCursorBlinkEnabled )
1291       {
1292         mEventData->mDecorator->StartCursorBlink();
1293       }
1294       // Grab handle is not shown until a tap is received whilst EDITING
1295       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1296       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1297       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1298       if( mEventData->mGrabHandlePopupEnabled )
1299       {
1300         mEventData->mDecorator->SetPopupActive( false );
1301       }
1302       mEventData->mDecoratorUpdated = true;
1303       HideClipboard();
1304     }
1305     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1306     {
1307       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1308       mEventData->mDecorator->StopCursorBlink();
1309       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1310       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1311       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1312       if( mEventData->mGrabHandlePopupEnabled )
1313       {
1314         mEventData->mDecorator->SetPopupActive( false );
1315       }
1316       mEventData->mDecoratorUpdated = true;
1317     }
1318     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1319     {
1320       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1321       if( mEventData->mCursorBlinkEnabled )
1322       {
1323         mEventData->mDecorator->StartCursorBlink();
1324       }
1325       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1326       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1327       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1328       if( mEventData->mGrabHandlePopupEnabled )
1329       {
1330         mEventData->mDecorator->SetPopupActive( false );
1331       }
1332       mEventData->mDecoratorUpdated = true;
1333     }
1334   }
1335 }
1336
1337 LineIndex Controller::Impl::GetClosestLine( float y ) const
1338 {
1339   float totalHeight = 0.f;
1340   LineIndex lineIndex = 0u;
1341
1342   const Vector<LineRun>& lines = mVisualModel->mLines;
1343   for( LineIndex endLine = lines.Count();
1344        lineIndex < endLine;
1345        ++lineIndex )
1346   {
1347     const LineRun& lineRun = lines[lineIndex];
1348     totalHeight += lineRun.ascender + -lineRun.descender;
1349     if( y < totalHeight )
1350     {
1351       return lineIndex;
1352     }
1353   }
1354
1355   if( lineIndex == 0 )
1356   {
1357     return 0;
1358   }
1359
1360   return lineIndex-1;
1361 }
1362
1363 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1364 {
1365   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1366   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
1367
1368   if ( mLogicalModel->mText.Count() == 0 )
1369   {
1370     return;  // if model empty
1371   }
1372
1373   if( hitCharacter >= mLogicalModel->mText.Count() )
1374   {
1375     // Closest hit character is the last character.
1376     if ( hitCharacter ==  mLogicalModel->mText.Count() )
1377     {
1378       hitCharacter--; //Hit character index set to last character in logical model
1379     }
1380     else
1381     {
1382       // hitCharacter is out of bounds
1383       return;
1384     }
1385   }
1386
1387   startIndex = hitCharacter;
1388   endIndex = hitCharacter;
1389
1390   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1391   {
1392     // Find the start and end of the text
1393     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1394     {
1395       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1396       if( TextAbstraction::IsWhiteSpace( charCode ) )
1397       {
1398         break;
1399       }
1400     }
1401     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1402     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1403     {
1404       Character charCode = mLogicalModel->mText[ endIndex ];
1405       if( TextAbstraction::IsWhiteSpace( charCode ) )
1406       {
1407         break;
1408       }
1409     }
1410   }
1411 }
1412
1413 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1414                                                         float visualY )
1415 {
1416   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
1417
1418   if( NULL == mEventData )
1419   {
1420     // Nothing to do if there is no text input.
1421     return 0u;
1422   }
1423
1424   CharacterIndex logicalIndex = 0u;
1425
1426   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1427   const Length numberOfLines  = mVisualModel->mLines.Count();
1428   if( 0 == numberOfGlyphs ||
1429       0 == numberOfLines )
1430   {
1431     return logicalIndex;
1432   }
1433
1434   // Find which line is closest
1435   const LineIndex lineIndex = GetClosestLine( visualY );
1436   const LineRun& line = mVisualModel->mLines[lineIndex];
1437
1438   // Get the positions of the glyphs.
1439   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1440   const Vector2* const positionsBuffer = positions.Begin();
1441
1442   // Get the visual to logical conversion tables.
1443   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1444   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1445
1446   // Get the character to glyph conversion table.
1447   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1448
1449   // Get the glyphs per character table.
1450   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1451   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1452
1453   // If the vector is void, there is no right to left characters.
1454   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1455
1456   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1457   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1458   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1459
1460   // Whether there is a hit on a glyph.
1461   bool matched = false;
1462
1463   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1464   CharacterIndex visualIndex = startCharacter;
1465   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1466   {
1467     // The character in logical order.
1468     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1469
1470     // Get the script of the character.
1471     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1472
1473     // The first glyph for that character in logical order.
1474     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1475     // The number of glyphs for that character
1476     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1477
1478     // Get the metrics for the group of glyphs.
1479     GlyphMetrics glyphMetrics;
1480     GetGlyphsMetrics( glyphLogicalOrderIndex,
1481                       numberOfGlyphs,
1482                       glyphMetrics,
1483                       mVisualModel,
1484                       mMetrics );
1485
1486     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1487
1488     // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ...
1489     const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1490     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1491
1492     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1493     {
1494       // Find the mid-point of the area containing the glyph
1495       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1496
1497       if( visualX < glyphCenter )
1498       {
1499         visualIndex += index;
1500         matched = true;
1501         break;
1502       }
1503     }
1504
1505     if( matched )
1506     {
1507       break;
1508     }
1509   }
1510
1511   // Return the logical position of the cursor in characters.
1512
1513   if( !matched )
1514   {
1515     visualIndex = endCharacter;
1516   }
1517
1518   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1519   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1520
1521   DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
1522
1523   return logicalIndex;
1524 }
1525
1526 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1527                                           CursorInfo& cursorInfo )
1528 {
1529   // TODO: Check for multiline with \n, etc...
1530
1531   const Length numberOfCharacters = mLogicalModel->mText.Count();
1532   if( !IsShowingRealText() )
1533   {
1534     // Do not want to use the place-holder text to set the cursor position.
1535
1536     // Use the line's height of the font's family set to set the cursor's size.
1537     // If there is no font's family set, use the default font.
1538     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1539
1540     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1541     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1542
1543     switch( mLayoutEngine.GetHorizontalAlignment() )
1544     {
1545       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1546       {
1547         cursorInfo.primaryPosition.x = 0.f;
1548         break;
1549       }
1550       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1551       {
1552         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
1553         break;
1554       }
1555       case LayoutEngine::HORIZONTAL_ALIGN_END:
1556       {
1557         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1558         break;
1559       }
1560     }
1561
1562     switch( mLayoutEngine.GetVerticalAlignment() )
1563     {
1564       case LayoutEngine::VERTICAL_ALIGN_TOP:
1565       {
1566         cursorInfo.primaryPosition.y = 0.f;
1567         break;
1568       }
1569       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1570       {
1571         cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
1572         break;
1573       }
1574       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1575       {
1576         cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
1577         break;
1578       }
1579     }
1580
1581     // Nothing else to do.
1582     return;
1583   }
1584
1585   // Check if the logical position is the first or the last one of the text.
1586   const bool isFirstPosition = 0u == logical;
1587   const bool isLastPosition = numberOfCharacters == logical;
1588
1589   // 'logical' is the logical 'cursor' index.
1590   // Get the next and current logical 'character' index.
1591   const CharacterIndex nextCharacterIndex = logical;
1592   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1593
1594   // Get the direction of the character and the next one.
1595   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1596
1597   CharacterDirection isCurrentRightToLeft = false;
1598   CharacterDirection isNextRightToLeft = false;
1599   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1600   {
1601     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1602     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1603   }
1604
1605   // Get the line where the character is laid-out.
1606   const LineRun* const modelLines = mVisualModel->mLines.Begin();
1607
1608   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1609   const LineRun& line = *( modelLines + lineIndex );
1610
1611   // Get the paragraph's direction.
1612   const CharacterDirection isRightToLeftParagraph = line.direction;
1613
1614   // Check whether there is an alternative position:
1615
1616   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1617                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1618
1619   // Set the line height.
1620   cursorInfo.lineHeight = line.ascender + -line.descender;
1621
1622   // Calculate the primary cursor.
1623
1624   CharacterIndex index = characterIndex;
1625   if( cursorInfo.isSecondaryCursor )
1626   {
1627     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1628
1629     if( isLastPosition )
1630     {
1631       // The position of the cursor after the last character needs special
1632       // care depending on its direction and the direction of the paragraph.
1633
1634       // Need to find the first character after the last character with the paragraph's direction.
1635       // i.e l0 l1 l2 r0 r1 should find r0.
1636
1637       // TODO: check for more than one line!
1638       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1639       index = mLogicalModel->GetLogicalCharacterIndex( index );
1640     }
1641     else
1642     {
1643       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1644     }
1645   }
1646
1647   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1648   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1649   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1650   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1651   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
1652
1653   // Convert the cursor position into the glyph position.
1654   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
1655   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1656   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
1657
1658   // Get the metrics for the group of glyphs.
1659   GlyphMetrics glyphMetrics;
1660   GetGlyphsMetrics( primaryGlyphIndex,
1661                     primaryNumberOfGlyphs,
1662                     glyphMetrics,
1663                     mVisualModel,
1664                     mMetrics );
1665
1666   // Whether to add the glyph's advance to the cursor position.
1667   // i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
1668   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
1669   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
1670   //
1671   // FLCP A
1672   // ------
1673   // 0000 1
1674   // 0001 1
1675   // 0010 0
1676   // 0011 0
1677   // 0100 1
1678   // 0101 0
1679   // 0110 1
1680   // 0111 0
1681   // 1000 0
1682   // 1001 x
1683   // 1010 x
1684   // 1011 1
1685   // 1100 x
1686   // 1101 x
1687   // 1110 x
1688   // 1111 x
1689   //
1690   // Where F -> isFirstPosition
1691   //       L -> isLastPosition
1692   //       C -> isCurrentRightToLeft
1693   //       P -> isRightToLeftParagraph
1694   //       A -> Whether to add the glyph's advance.
1695
1696   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
1697                                  ( isFirstPosition && isRightToLeftParagraph ) ||
1698                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
1699
1700   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
1701
1702   if( !isLastPosition &&
1703       ( primaryNumberOfCharacters > 1u ) )
1704   {
1705     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
1706
1707     bool isCurrentRightToLeft = false;
1708     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1709     {
1710       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
1711     }
1712
1713     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
1714     if( isCurrentRightToLeft )
1715     {
1716       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
1717     }
1718
1719     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1720   }
1721
1722   // Get the glyph position and x bearing.
1723   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
1724
1725   // Set the primary cursor's height.
1726   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1727
1728   // Set the primary cursor's position.
1729   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
1730   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1731
1732   // Calculate the secondary cursor.
1733
1734   if( cursorInfo.isSecondaryCursor )
1735   {
1736     // Set the secondary cursor's height.
1737     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1738
1739     CharacterIndex index = characterIndex;
1740     if( !isLastPosition )
1741     {
1742       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1743     }
1744
1745     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
1746     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
1747
1748     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
1749
1750     GetGlyphsMetrics( secondaryGlyphIndex,
1751                       secondaryNumberOfGlyphs,
1752                       glyphMetrics,
1753                       mVisualModel,
1754                       mMetrics );
1755
1756     // Set the secondary cursor's position.
1757     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1758     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1759   }
1760 }
1761
1762 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1763 {
1764   if( NULL == mEventData )
1765   {
1766     // Nothing to do if there is no text input.
1767     return 0u;
1768   }
1769
1770   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1771
1772   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1773   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1774
1775   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1776   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1777
1778   if( numberOfCharacters > 1u )
1779   {
1780     const Script script = mLogicalModel->GetScript( index );
1781     if( HasLigatureMustBreak( script ) )
1782     {
1783       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
1784       numberOfCharacters = 1u;
1785     }
1786   }
1787   else
1788   {
1789     while( 0u == numberOfCharacters )
1790     {
1791       ++glyphIndex;
1792       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1793     }
1794   }
1795
1796   if( index < mEventData->mPrimaryCursorPosition )
1797   {
1798     cursorIndex -= numberOfCharacters;
1799   }
1800   else
1801   {
1802     cursorIndex += numberOfCharacters;
1803   }
1804
1805   return cursorIndex;
1806 }
1807
1808 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1809 {
1810   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1811   if( NULL == mEventData )
1812   {
1813     // Nothing to do if there is no text input.
1814     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1815     return;
1816   }
1817
1818   const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
1819   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1820
1821   // Sets the cursor position.
1822   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1823                                        cursorPosition.x,
1824                                        cursorPosition.y,
1825                                        cursorInfo.primaryCursorHeight,
1826                                        cursorInfo.lineHeight );
1827   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1828
1829   // Sets the grab handle position.
1830   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1831                                        cursorPosition.x,
1832                                        cursorPosition.y,
1833                                        cursorInfo.lineHeight );
1834
1835   if( cursorInfo.isSecondaryCursor )
1836   {
1837     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1838                                          cursorInfo.secondaryPosition.x + offset.x,
1839                                          cursorInfo.secondaryPosition.y + offset.y,
1840                                          cursorInfo.secondaryCursorHeight,
1841                                          cursorInfo.lineHeight );
1842     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1843   }
1844
1845   // Set which cursors are active according the state.
1846   if( ( EventData::EDITING == mEventData->mState )                  ||
1847       ( EventData::EDITING_WITH_POPUP == mEventData->mState )       ||
1848       ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
1849       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1850   {
1851     if( cursorInfo.isSecondaryCursor )
1852     {
1853       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1854     }
1855     else
1856     {
1857       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1858     }
1859   }
1860   else
1861   {
1862     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1863   }
1864
1865   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1866 }
1867
1868 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
1869                                               const CursorInfo& cursorInfo )
1870 {
1871   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1872       ( RIGHT_SELECTION_HANDLE != handleType ) )
1873   {
1874     return;
1875   }
1876
1877   const Vector2 cursorPosition = cursorInfo.primaryPosition + mEventData->mScrollPosition + mAlignmentOffset;
1878
1879   // Sets the grab handle position.
1880   mEventData->mDecorator->SetPosition( handleType,
1881                                        cursorPosition.x,
1882                                        cursorPosition.y,
1883                                        cursorInfo.lineHeight );
1884
1885   // If selection handle at start of the text and other at end of the text then all text is selected.
1886   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1887   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1888   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1889 }
1890
1891 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1892 {
1893   // Clamp between -space & 0 (and the text alignment).
1894
1895   if( actualSize.width > mVisualModel->mControlSize.width )
1896   {
1897     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
1898     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1899     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1900
1901     mEventData->mDecoratorUpdated = true;
1902   }
1903   else
1904   {
1905     mEventData->mScrollPosition.x = 0.f;
1906   }
1907 }
1908
1909 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1910 {
1911   // Clamp between -space & 0 (and the text alignment).
1912   if( actualSize.height > mVisualModel->mControlSize.height )
1913   {
1914     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
1915     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1916     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1917
1918     mEventData->mDecoratorUpdated = true;
1919   }
1920   else
1921   {
1922     mEventData->mScrollPosition.y = 0.f;
1923   }
1924 }
1925
1926 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1927 {
1928   // position is in actor's coords.
1929   const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
1930
1931   // Transform the position to decorator coords.
1932   const float offset = mEventData->mScrollPosition.x + mAlignmentOffset.x;
1933   const float decoratorPositionBegin = position.x + offset;
1934   const float decoratorPositionEnd = positionEnd + offset;
1935
1936   if( decoratorPositionBegin < 0.f )
1937   {
1938     mEventData->mScrollPosition.x = -position.x - mAlignmentOffset.x;
1939   }
1940   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
1941   {
1942     mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - mAlignmentOffset.x;
1943   }
1944 }
1945
1946 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
1947 {
1948   // Get the current cursor position in decorator coords.
1949   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1950
1951   // Calculate the offset to match the cursor position before the character was deleted.
1952   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1953
1954   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1955 }
1956
1957 void Controller::Impl::RequestRelayout()
1958 {
1959   mControlInterface.RequestTextRelayout();
1960 }
1961
1962 } // namespace Text
1963
1964 } // namespace Toolkit
1965
1966 } // namespace Dali