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