Merge "[3.0] Remove dali csharp codes" into tizen_3.0
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2016 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 #include <limits>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
36
37 namespace
38 {
39
40 /**
41  * @brief Struct used to calculate the selection box.
42  */
43 struct SelectionBoxInfo
44 {
45   float lineOffset;
46   float lineHeight;
47   float minX;
48   float maxX;
49 };
50
51 #if defined(DEBUG_ENABLED)
52   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
53 #endif
54
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
58
59 } // namespace
60
61 namespace Dali
62 {
63
64 namespace Toolkit
65 {
66
67 namespace Text
68 {
69
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
72   mImfManager(),
73   mPlaceholderTextActive(),
74   mPlaceholderTextInactive(),
75   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
76   mEventQueue(),
77   mInputStyleChangedQueue(),
78   mState( INACTIVE ),
79   mPrimaryCursorPosition( 0u ),
80   mLeftSelectionPosition( 0u ),
81   mRightSelectionPosition( 0u ),
82   mPreEditStartPosition( 0u ),
83   mPreEditLength( 0u ),
84   mCursorHookPositionX( 0.f ),
85   mIsShowingPlaceholderText( false ),
86   mPreEditFlag( false ),
87   mDecoratorUpdated( false ),
88   mCursorBlinkEnabled( true ),
89   mGrabHandleEnabled( true ),
90   mGrabHandlePopupEnabled( true ),
91   mSelectionEnabled( true ),
92   mUpdateCursorHookPosition( false ),
93   mUpdateCursorPosition( false ),
94   mUpdateGrabHandlePosition( false ),
95   mUpdateLeftSelectionPosition( false ),
96   mUpdateRightSelectionPosition( false ),
97   mIsLeftHandleSelected( false ),
98   mUpdateHighlightBox( false ),
99   mScrollAfterUpdatePosition( false ),
100   mScrollAfterDelete( false ),
101   mAllTextSelected( false ),
102   mUpdateInputStyle( false )
103 {
104   mImfManager = ImfManager::Get();
105 }
106
107 EventData::~EventData()
108 {}
109
110 bool Controller::Impl::ProcessInputEvents()
111 {
112   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
113   if( NULL == mEventData )
114   {
115     // Nothing to do if there is no text input.
116     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
117     return false;
118   }
119
120   if( mEventData->mDecorator )
121   {
122     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
123          iter != mEventData->mEventQueue.end();
124          ++iter )
125     {
126       switch( iter->type )
127       {
128         case Event::CURSOR_KEY_EVENT:
129         {
130           OnCursorKeyEvent( *iter );
131           break;
132         }
133         case Event::TAP_EVENT:
134         {
135           OnTapEvent( *iter );
136           break;
137         }
138         case Event::LONG_PRESS_EVENT:
139         {
140           OnLongPressEvent( *iter );
141           break;
142         }
143         case Event::PAN_EVENT:
144         {
145           OnPanEvent( *iter );
146           break;
147         }
148         case Event::GRAB_HANDLE_EVENT:
149         case Event::LEFT_SELECTION_HANDLE_EVENT:
150         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
151         {
152           OnHandleEvent( *iter );
153           break;
154         }
155         case Event::SELECT:
156         {
157           OnSelectEvent( *iter );
158           break;
159         }
160         case Event::SELECT_ALL:
161         {
162           OnSelectAllEvent();
163           break;
164         }
165       }
166     }
167   }
168
169   if( mEventData->mUpdateCursorPosition ||
170       mEventData->mUpdateHighlightBox )
171   {
172     NotifyImfManager();
173   }
174
175   // The cursor must also be repositioned after inserts into the model
176   if( mEventData->mUpdateCursorPosition )
177   {
178     // Updates the cursor position and scrolls the text to make it visible.
179     CursorInfo cursorInfo;
180     // Calculate the cursor position from the new cursor index.
181     GetCursorPosition( mEventData->mPrimaryCursorPosition,
182                        cursorInfo );
183
184     if( mEventData->mUpdateCursorHookPosition )
185     {
186       // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
187       mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
188       mEventData->mUpdateCursorHookPosition = false;
189     }
190
191     // Scroll first the text after delete ...
192     if( mEventData->mScrollAfterDelete )
193     {
194       ScrollTextToMatchCursor( cursorInfo );
195     }
196
197     // ... then, text can be scrolled to make the cursor visible.
198     if( mEventData->mScrollAfterUpdatePosition )
199     {
200       const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
201       ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
202     }
203     mEventData->mScrollAfterUpdatePosition = false;
204     mEventData->mScrollAfterDelete = false;
205
206     UpdateCursorPosition( cursorInfo );
207
208     mEventData->mDecoratorUpdated = true;
209     mEventData->mUpdateCursorPosition = false;
210     mEventData->mUpdateGrabHandlePosition = false;
211   }
212   else
213   {
214     CursorInfo leftHandleInfo;
215     CursorInfo rightHandleInfo;
216
217     if( mEventData->mUpdateHighlightBox )
218     {
219       GetCursorPosition( mEventData->mLeftSelectionPosition,
220                          leftHandleInfo );
221
222       GetCursorPosition( mEventData->mRightSelectionPosition,
223                          rightHandleInfo );
224
225       if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
226       {
227         CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
228
229         const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
230         ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
231       }
232     }
233
234     if( mEventData->mUpdateLeftSelectionPosition )
235     {
236       UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
237                              leftHandleInfo );
238
239       SetPopupButtons();
240       mEventData->mDecoratorUpdated = true;
241       mEventData->mUpdateLeftSelectionPosition = false;
242     }
243
244     if( mEventData->mUpdateRightSelectionPosition )
245     {
246       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
247                              rightHandleInfo );
248
249       SetPopupButtons();
250       mEventData->mDecoratorUpdated = true;
251       mEventData->mUpdateRightSelectionPosition = false;
252     }
253
254     if( mEventData->mUpdateHighlightBox )
255     {
256       RepositionSelectionHandles();
257
258       mEventData->mUpdateLeftSelectionPosition = false;
259       mEventData->mUpdateRightSelectionPosition = false;
260       mEventData->mUpdateHighlightBox = false;
261     }
262
263     mEventData->mScrollAfterUpdatePosition = false;
264   }
265
266   if( mEventData->mUpdateInputStyle )
267   {
268     // Keep a copy of the current input style.
269     InputStyle currentInputStyle;
270     currentInputStyle.Copy( mEventData->mInputStyle );
271
272     // Set the default style first.
273     RetrieveDefaultInputStyle( mEventData->mInputStyle );
274
275     // Get the character index from the cursor index.
276     const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
277
278     // Retrieve the style from the style runs stored in the logical model.
279     mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
280
281     // Compare if the input style has changed.
282     const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
283
284     if( hasInputStyleChanged )
285     {
286       const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
287       // Queue the input style changed signal.
288       mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
289     }
290
291     mEventData->mUpdateInputStyle = false;
292   }
293
294   mEventData->mEventQueue.clear();
295
296   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
297
298   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
299   mEventData->mDecoratorUpdated = false;
300
301   return decoratorUpdated;
302 }
303
304 void Controller::Impl::NotifyImfManager()
305 {
306   if( mEventData && mEventData->mImfManager )
307   {
308     CharacterIndex cursorPosition = GetLogicalCursorPosition();
309
310     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
311
312     // Update the cursor position by removing the initial white spaces.
313     if( cursorPosition < numberOfWhiteSpaces )
314     {
315       cursorPosition = 0u;
316     }
317     else
318     {
319       cursorPosition -= numberOfWhiteSpaces;
320     }
321
322     mEventData->mImfManager.SetCursorPosition( cursorPosition );
323     mEventData->mImfManager.NotifyCursorPosition();
324   }
325 }
326
327 void Controller::Impl::NotifyImfMultiLineStatus()
328 {
329   if ( mEventData )
330   {
331     LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
332     mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
333   }
334 }
335
336 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
337 {
338   CharacterIndex cursorPosition = 0u;
339
340   if( mEventData )
341   {
342     if( ( EventData::SELECTING == mEventData->mState ) ||
343         ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
344     {
345       cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
346     }
347     else
348     {
349       cursorPosition = mEventData->mPrimaryCursorPosition;
350     }
351   }
352
353   return cursorPosition;
354 }
355
356 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
357 {
358   Length numberOfWhiteSpaces = 0u;
359
360   // Get the buffer to the text.
361   Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
362
363   const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
364   for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
365   {
366     if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
367     {
368       break;
369     }
370   }
371
372   return numberOfWhiteSpaces;
373 }
374
375 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
376 {
377   // Get the total number of characters.
378   Length numberOfCharacters = mLogicalModel->mText.Count();
379
380   // Retrieve the text.
381   if( 0u != numberOfCharacters )
382   {
383     Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
384   }
385 }
386
387 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
388 {
389   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
390   mTextUpdateInfo.mStartGlyphIndex = 0u;
391   mTextUpdateInfo.mStartLineIndex = 0u;
392   numberOfCharacters = 0u;
393
394   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
395   if( 0u == numberOfParagraphs )
396   {
397     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
398     numberOfCharacters = 0u;
399
400     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
401
402     // Nothing else to do if there are no paragraphs.
403     return;
404   }
405
406   // Find the paragraphs to be updated.
407   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
408   if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
409   {
410     // Text is being added at the end of the current text.
411     if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
412     {
413       // Text is being added in a new paragraph after the last character of the text.
414       mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
415       numberOfCharacters = 0u;
416       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
417
418       mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
419       mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
420
421       // Nothing else to do;
422       return;
423     }
424
425     paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
426   }
427   else
428   {
429     Length numberOfCharactersToUpdate = 0u;
430     if( mTextUpdateInfo.mFullRelayoutNeeded )
431     {
432       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
433     }
434     else
435     {
436       numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
437     }
438     mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
439                                    numberOfCharactersToUpdate,
440                                    paragraphsToBeUpdated );
441   }
442
443   if( 0u != paragraphsToBeUpdated.Count() )
444   {
445     const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
446     const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
447     mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
448
449     ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
450     const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
451
452     if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) &&                                            // Some character are removed.
453         ( lastParagraphIndex < numberOfParagraphs - 1u ) &&                                                // There is a next paragraph.
454         ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
455           ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
456     {
457       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
458       const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
459
460       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
461     }
462     else
463     {
464       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
465     }
466   }
467
468   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
469   mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
470 }
471
472 void Controller::Impl::ClearFullModelData( OperationsMask operations )
473 {
474   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
475   {
476     mLogicalModel->mLineBreakInfo.Clear();
477     mLogicalModel->mParagraphInfo.Clear();
478   }
479
480   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
481   {
482     mLogicalModel->mLineBreakInfo.Clear();
483   }
484
485   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
486   {
487     mLogicalModel->mScriptRuns.Clear();
488   }
489
490   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
491   {
492     mLogicalModel->mFontRuns.Clear();
493   }
494
495   if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
496   {
497     if( NO_OPERATION != ( BIDI_INFO & operations ) )
498     {
499       mLogicalModel->mBidirectionalParagraphInfo.Clear();
500       mLogicalModel->mCharacterDirections.Clear();
501     }
502
503     if( NO_OPERATION != ( REORDER & operations ) )
504     {
505       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
506       for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
507              endIt = mLogicalModel->mBidirectionalLineInfo.End();
508            it != endIt;
509            ++it )
510       {
511         BidirectionalLineInfoRun& bidiLineInfo = *it;
512
513         free( bidiLineInfo.visualToLogicalMap );
514         bidiLineInfo.visualToLogicalMap = NULL;
515       }
516       mLogicalModel->mBidirectionalLineInfo.Clear();
517     }
518   }
519
520   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
521   {
522     mVisualModel->mGlyphs.Clear();
523     mVisualModel->mGlyphsToCharacters.Clear();
524     mVisualModel->mCharactersToGlyph.Clear();
525     mVisualModel->mCharactersPerGlyph.Clear();
526     mVisualModel->mGlyphsPerCharacter.Clear();
527     mVisualModel->mGlyphPositions.Clear();
528   }
529
530   if( NO_OPERATION != ( LAYOUT & operations ) )
531   {
532     mVisualModel->mLines.Clear();
533   }
534
535   if( NO_OPERATION != ( COLOR & operations ) )
536   {
537     mVisualModel->mColorIndices.Clear();
538   }
539 }
540
541 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
542 {
543   const CharacterIndex endIndexPlusOne = endIndex + 1u;
544
545   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
546   {
547     // Clear the line break info.
548     LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
549
550     mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
551                                          lineBreakInfoBuffer + endIndexPlusOne );
552
553     // Clear the paragraphs.
554     ClearCharacterRuns( startIndex,
555                         endIndex,
556                         mLogicalModel->mParagraphInfo );
557   }
558
559   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
560   {
561     // Clear the word break info.
562     WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
563
564     mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
565                                          wordBreakInfoBuffer + endIndexPlusOne );
566   }
567
568   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
569   {
570     // Clear the scripts.
571     ClearCharacterRuns( startIndex,
572                         endIndex,
573                         mLogicalModel->mScriptRuns );
574   }
575
576   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
577   {
578     // Clear the fonts.
579     ClearCharacterRuns( startIndex,
580                         endIndex,
581                         mLogicalModel->mFontRuns );
582   }
583
584   if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
585   {
586     if( NO_OPERATION != ( BIDI_INFO & operations ) )
587     {
588       // Clear the bidirectional paragraph info.
589       ClearCharacterRuns( startIndex,
590                           endIndex,
591                           mLogicalModel->mBidirectionalParagraphInfo );
592
593       // Clear the character's directions.
594       CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
595
596       mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
597                                                  characterDirectionsBuffer + endIndexPlusOne );
598     }
599
600     if( NO_OPERATION != ( REORDER & operations ) )
601     {
602       uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
603       uint32_t endRemoveIndex = startRemoveIndex;
604       ClearCharacterRuns( startIndex,
605                           endIndex,
606                           mLogicalModel->mBidirectionalLineInfo,
607                           startRemoveIndex,
608                           endRemoveIndex );
609
610       BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
611
612       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
613       for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
614              endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
615            it != endIt;
616            ++it )
617       {
618         BidirectionalLineInfoRun& bidiLineInfo = *it;
619
620         free( bidiLineInfo.visualToLogicalMap );
621         bidiLineInfo.visualToLogicalMap = NULL;
622       }
623
624       mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
625                                                    bidirectionalLineInfoBuffer + endRemoveIndex );
626     }
627   }
628 }
629
630 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
631 {
632   const CharacterIndex endIndexPlusOne = endIndex + 1u;
633   const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
634
635   // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
636   GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
637   Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
638
639   const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
640   const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
641
642   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
643   {
644     // Update the character to glyph indices.
645     for( Vector<GlyphIndex>::Iterator it =  charactersToGlyphBuffer + endIndexPlusOne,
646            endIt =  charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
647          it != endIt;
648          ++it )
649     {
650       CharacterIndex& index = *it;
651       index -= numberOfGlyphsRemoved;
652     }
653
654     // Clear the character to glyph conversion table.
655     mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
656                                             charactersToGlyphBuffer + endIndexPlusOne );
657
658     // Clear the glyphs per character table.
659     mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
660                                              glyphsPerCharacterBuffer + endIndexPlusOne );
661
662     // Clear the glyphs buffer.
663     GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
664     mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
665                                  glyphsBuffer + endGlyphIndexPlusOne );
666
667     CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
668
669     // Update the glyph to character indices.
670     for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
671            endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
672          it != endIt;
673          ++it )
674     {
675       CharacterIndex& index = *it;
676       index -= numberOfCharactersRemoved;
677     }
678
679     // Clear the glyphs to characters buffer.
680     mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
681                                              glyphsToCharactersBuffer  + endGlyphIndexPlusOne );
682
683     // Clear the characters per glyph buffer.
684     Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
685     mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
686                                              charactersPerGlyphBuffer + endGlyphIndexPlusOne );
687
688     // Clear the positions buffer.
689     Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
690     mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
691                                          positionsBuffer + endGlyphIndexPlusOne );
692   }
693
694   if( NO_OPERATION != ( LAYOUT & operations ) )
695   {
696     // Clear the lines.
697     uint32_t startRemoveIndex = mVisualModel->mLines.Count();
698     uint32_t endRemoveIndex = startRemoveIndex;
699     ClearCharacterRuns( startIndex,
700                         endIndex,
701                         mVisualModel->mLines,
702                         startRemoveIndex,
703                         endRemoveIndex );
704
705     // Will update the glyph runs.
706     startRemoveIndex = mVisualModel->mLines.Count();
707     endRemoveIndex = startRemoveIndex;
708     ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
709                     endGlyphIndexPlusOne - 1u,
710                     mVisualModel->mLines,
711                     startRemoveIndex,
712                     endRemoveIndex );
713
714     // Set the line index from where to insert the new laid-out lines.
715     mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
716
717     LineRun* linesBuffer = mVisualModel->mLines.Begin();
718     mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
719                                 linesBuffer + endRemoveIndex );
720   }
721
722   if( NO_OPERATION != ( COLOR & operations ) )
723   {
724     if( 0u != mVisualModel->mColorIndices.Count() )
725     {
726       ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
727       mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
728                                          colorIndexBuffer + endGlyphIndexPlusOne );
729     }
730   }
731 }
732
733 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
734 {
735   if( mTextUpdateInfo.mClearAll ||
736       ( ( 0u == startIndex ) &&
737         ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
738   {
739     ClearFullModelData( operations );
740   }
741   else
742   {
743     // Clear the model data related with characters.
744     ClearCharacterModelData( startIndex, endIndex, operations );
745
746     // Clear the model data related with glyphs.
747     ClearGlyphModelData( startIndex, endIndex, operations );
748   }
749
750   // The estimated number of lines. Used to avoid reallocations when layouting.
751   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
752
753   mVisualModel->ClearCaches();
754 }
755
756 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
757 {
758   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
759
760   // Calculate the operations to be done.
761   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
762
763   if( NO_OPERATION == operations )
764   {
765     // Nothing to do if no operations are pending and required.
766     return false;
767   }
768
769   Vector<Character>& utf32Characters = mLogicalModel->mText;
770
771   const Length numberOfCharacters = utf32Characters.Count();
772
773   // Index to the first character of the first paragraph to be updated.
774   CharacterIndex startIndex = 0u;
775   // Number of characters of the paragraphs to be removed.
776   Length paragraphCharacters = 0u;
777
778   CalculateTextUpdateIndices( paragraphCharacters );
779   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
780
781   if( mTextUpdateInfo.mClearAll ||
782       ( 0u != paragraphCharacters ) )
783   {
784     ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
785   }
786
787   mTextUpdateInfo.mClearAll = false;
788
789   // Whether the model is updated.
790   bool updated = false;
791
792   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
793   const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
794
795   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
796   {
797     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
798     // calculate the bidirectional info for each 'paragraph'.
799     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
800     // is not shaped together).
801     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
802
803     SetLineBreakInfo( utf32Characters,
804                       startIndex,
805                       requestedNumberOfCharacters,
806                       lineBreakInfo );
807
808     // Create the paragraph info.
809     mLogicalModel->CreateParagraphInfo( startIndex,
810                                         requestedNumberOfCharacters );
811     updated = true;
812   }
813
814   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
815   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
816   {
817     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
818     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
819
820     SetWordBreakInfo( utf32Characters,
821                       startIndex,
822                       requestedNumberOfCharacters,
823                       wordBreakInfo );
824     updated = true;
825   }
826
827   const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
828   const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
829
830   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
831   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
832
833   if( getScripts || validateFonts )
834   {
835     // Validates the fonts assigned by the application or assigns default ones.
836     // It makes sure all the characters are going to be rendered by the correct font.
837     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
838
839     if( getScripts )
840     {
841       // Retrieves the scripts used in the text.
842       multilanguageSupport.SetScripts( utf32Characters,
843                                        startIndex,
844                                        requestedNumberOfCharacters,
845                                        scripts );
846     }
847
848     if( validateFonts )
849     {
850       // Validate the fonts set through the mark-up string.
851       Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
852
853       // Get the default font's description.
854       TextAbstraction::FontDescription defaultFontDescription;
855       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
856       if( NULL != mFontDefaults )
857       {
858         defaultFontDescription = mFontDefaults->mFontDescription;
859         defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
860       }
861
862       // Validates the fonts. If there is a character with no assigned font it sets a default one.
863       // After this call, fonts are validated.
864       multilanguageSupport.ValidateFonts( utf32Characters,
865                                           scripts,
866                                           fontDescriptionRuns,
867                                           defaultFontDescription,
868                                           defaultPointSize,
869                                           startIndex,
870                                           requestedNumberOfCharacters,
871                                           validFonts );
872     }
873     updated = true;
874   }
875
876   Vector<Character> mirroredUtf32Characters;
877   bool textMirrored = false;
878   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
879   if( NO_OPERATION != ( BIDI_INFO & operations ) )
880   {
881     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
882     bidirectionalInfo.Reserve( numberOfParagraphs );
883
884     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
885     SetBidirectionalInfo( utf32Characters,
886                           scripts,
887                           lineBreakInfo,
888                           startIndex,
889                           requestedNumberOfCharacters,
890                           bidirectionalInfo );
891
892     if( 0u != bidirectionalInfo.Count() )
893     {
894       // Only set the character directions if there is right to left characters.
895       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
896       GetCharactersDirection( bidirectionalInfo,
897                               numberOfCharacters,
898                               startIndex,
899                               requestedNumberOfCharacters,
900                               directions );
901
902       // This paragraph has right to left text. Some characters may need to be mirrored.
903       // TODO: consider if the mirrored string can be stored as well.
904
905       textMirrored = GetMirroredText( utf32Characters,
906                                       directions,
907                                       bidirectionalInfo,
908                                       startIndex,
909                                       requestedNumberOfCharacters,
910                                       mirroredUtf32Characters );
911     }
912     else
913     {
914       // There is no right to left characters. Clear the directions vector.
915       mLogicalModel->mCharacterDirections.Clear();
916     }
917     updated = true;
918   }
919
920   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
921   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
922   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
923   Vector<GlyphIndex> newParagraphGlyphs;
924   newParagraphGlyphs.Reserve( numberOfParagraphs );
925
926   const Length currentNumberOfGlyphs = glyphs.Count();
927   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
928   {
929     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
930     // Shapes the text.
931     ShapeText( textToShape,
932                lineBreakInfo,
933                scripts,
934                validFonts,
935                startIndex,
936                mTextUpdateInfo.mStartGlyphIndex,
937                requestedNumberOfCharacters,
938                glyphs,
939                glyphsToCharactersMap,
940                charactersPerGlyph,
941                newParagraphGlyphs );
942
943     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
944     mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
945     mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
946     updated = true;
947   }
948
949   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
950
951   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
952   {
953     GlyphInfo* glyphsBuffer = glyphs.Begin();
954     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
955
956     // Update the width and advance of all new paragraph characters.
957     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
958     {
959       const GlyphIndex index = *it;
960       GlyphInfo& glyph = *( glyphsBuffer + index );
961
962       glyph.xBearing = 0.f;
963       glyph.width = 0.f;
964       glyph.advance = 0.f;
965     }
966     updated = true;
967   }
968
969   if( NO_OPERATION != ( COLOR & operations ) )
970   {
971     // Set the color runs in glyphs.
972     SetColorSegmentationInfo( mLogicalModel->mColorRuns,
973                               mVisualModel->mCharactersToGlyph,
974                               mVisualModel->mGlyphsPerCharacter,
975                               startIndex,
976                               mTextUpdateInfo.mStartGlyphIndex,
977                               requestedNumberOfCharacters,
978                               mVisualModel->mColors,
979                               mVisualModel->mColorIndices );
980
981     updated = true;
982   }
983
984   if( ( NULL != mEventData ) &&
985       mEventData->mPreEditFlag &&
986       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
987   {
988     // Add the underline for the pre-edit text.
989     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
990     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
991
992     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
993     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
994     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
995     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
996
997     GlyphRun underlineRun;
998     underlineRun.glyphIndex = glyphStart;
999     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1000
1001     // TODO: At the moment the underline runs are only for pre-edit.
1002     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1003   }
1004
1005   // The estimated number of lines. Used to avoid reallocations when layouting.
1006   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
1007
1008   // Set the previous number of characters for the next time the text is updated.
1009   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1010
1011   return updated;
1012 }
1013
1014 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1015 {
1016   // Sets the default text's color.
1017   inputStyle.textColor = mTextColor;
1018   inputStyle.isDefaultColor = true;
1019
1020   inputStyle.familyName.clear();
1021   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1022   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1023   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1024   inputStyle.size = 0.f;
1025
1026   inputStyle.lineSpacing = 0.f;
1027
1028   inputStyle.underlineProperties.clear();
1029   inputStyle.shadowProperties.clear();
1030   inputStyle.embossProperties.clear();
1031   inputStyle.outlineProperties.clear();
1032
1033   inputStyle.isFamilyDefined = false;
1034   inputStyle.isWeightDefined = false;
1035   inputStyle.isWidthDefined = false;
1036   inputStyle.isSlantDefined = false;
1037   inputStyle.isSizeDefined = false;
1038
1039   inputStyle.isLineSpacingDefined = false;
1040
1041   inputStyle.isUnderlineDefined = false;
1042   inputStyle.isShadowDefined = false;
1043   inputStyle.isEmbossDefined = false;
1044   inputStyle.isOutlineDefined = false;
1045
1046   // Sets the default font's family name, weight, width, slant and size.
1047   if( mFontDefaults )
1048   {
1049     if( mFontDefaults->familyDefined )
1050     {
1051       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1052       inputStyle.isFamilyDefined = true;
1053     }
1054
1055     if( mFontDefaults->weightDefined )
1056     {
1057       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1058       inputStyle.isWeightDefined = true;
1059     }
1060
1061     if( mFontDefaults->widthDefined )
1062     {
1063       inputStyle.width = mFontDefaults->mFontDescription.width;
1064       inputStyle.isWidthDefined = true;
1065     }
1066
1067     if( mFontDefaults->slantDefined )
1068     {
1069       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1070       inputStyle.isSlantDefined = true;
1071     }
1072
1073     if( mFontDefaults->sizeDefined )
1074     {
1075       inputStyle.size = mFontDefaults->mDefaultPointSize;
1076       inputStyle.isSizeDefined = true;
1077     }
1078   }
1079 }
1080
1081 float Controller::Impl::GetDefaultFontLineHeight()
1082 {
1083   FontId defaultFontId = 0u;
1084   if( NULL == mFontDefaults )
1085   {
1086     TextAbstraction::FontDescription fontDescription;
1087     defaultFontId = mFontClient.GetFontId( fontDescription );
1088   }
1089   else
1090   {
1091     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1092   }
1093
1094   Text::FontMetrics fontMetrics;
1095   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1096
1097   return( fontMetrics.ascender - fontMetrics.descender );
1098 }
1099
1100 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1101 {
1102   if( NULL == mEventData )
1103   {
1104     // Nothing to do if there is no text input.
1105     return;
1106   }
1107
1108   int keyCode = event.p1.mInt;
1109
1110   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1111   {
1112     if( mEventData->mPrimaryCursorPosition > 0u )
1113     {
1114       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1115     }
1116   }
1117   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1118   {
1119     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1120     {
1121       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1122     }
1123   }
1124   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1125   {
1126     // Get first the line index of the current cursor position index.
1127     CharacterIndex characterIndex = 0u;
1128
1129     if( mEventData->mPrimaryCursorPosition > 0u )
1130     {
1131       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1132     }
1133
1134     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1135
1136     if( lineIndex > 0u )
1137     {
1138       // Retrieve the cursor position info.
1139       CursorInfo cursorInfo;
1140       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1141                          cursorInfo );
1142
1143       // Get the line above.
1144       const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1145
1146       // Get the next hit 'y' point.
1147       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1148
1149       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1150       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1151                                                                         mLogicalModel,
1152                                                                         mMetrics,
1153                                                                         mEventData->mCursorHookPositionX,
1154                                                                         hitPointY );
1155     }
1156   }
1157   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1158   {
1159     // Get first the line index of the current cursor position index.
1160     CharacterIndex characterIndex = 0u;
1161
1162     if( mEventData->mPrimaryCursorPosition > 0u )
1163     {
1164       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1165     }
1166
1167     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1168
1169     if( lineIndex + 1u < mVisualModel->mLines.Count() )
1170     {
1171       // Retrieve the cursor position info.
1172       CursorInfo cursorInfo;
1173       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1174                          cursorInfo );
1175
1176       // Get the line below.
1177       const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1178
1179       // Get the next hit 'y' point.
1180       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1181
1182       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1183       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1184                                                                         mLogicalModel,
1185                                                                         mMetrics,
1186                                                                         mEventData->mCursorHookPositionX,
1187                                                                         hitPointY );
1188     }
1189   }
1190
1191   mEventData->mUpdateCursorPosition = true;
1192   mEventData->mUpdateInputStyle = true;
1193   mEventData->mScrollAfterUpdatePosition = true;
1194 }
1195
1196 void Controller::Impl::OnTapEvent( const Event& event )
1197 {
1198   if( NULL != mEventData )
1199   {
1200     const unsigned int tapCount = event.p1.mUint;
1201
1202     if( 1u == tapCount )
1203     {
1204       if( IsShowingRealText() )
1205       {
1206         // Convert from control's coords to text's coords.
1207         const float xPosition = event.p2.mFloat - mScrollPosition.x;
1208         const float yPosition = event.p3.mFloat - mScrollPosition.y;
1209
1210         // Keep the tap 'x' position. Used to move the cursor.
1211         mEventData->mCursorHookPositionX = xPosition;
1212
1213         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1214                                                                           mLogicalModel,
1215                                                                           mMetrics,
1216                                                                           xPosition,
1217                                                                           yPosition );
1218
1219         // When the cursor position is changing, delay cursor blinking
1220         mEventData->mDecorator->DelayCursorBlink();
1221       }
1222       else
1223       {
1224         mEventData->mPrimaryCursorPosition = 0u;
1225       }
1226
1227       mEventData->mUpdateCursorPosition = true;
1228       mEventData->mUpdateGrabHandlePosition = true;
1229       mEventData->mScrollAfterUpdatePosition = true;
1230       mEventData->mUpdateInputStyle = true;
1231
1232       // Notify the cursor position to the imf manager.
1233       if( mEventData->mImfManager )
1234       {
1235         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1236         mEventData->mImfManager.NotifyCursorPosition();
1237       }
1238     }
1239   }
1240 }
1241
1242 void Controller::Impl::OnPanEvent( const Event& event )
1243 {
1244   if( NULL == mEventData )
1245   {
1246     // Nothing to do if there is no text input.
1247     return;
1248   }
1249
1250   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1251   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1252
1253   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1254   {
1255     // Nothing to do if scrolling is not enabled.
1256     return;
1257   }
1258
1259   const int state = event.p1.mInt;
1260
1261   switch( state )
1262   {
1263     case Gesture::Started:
1264     {
1265       // Will remove the cursor, handles or text's popup, ...
1266       ChangeState( EventData::TEXT_PANNING );
1267       break;
1268     }
1269     case Gesture::Continuing:
1270     {
1271       const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1272       const Vector2 currentScroll = mScrollPosition;
1273
1274       if( isHorizontalScrollEnabled )
1275       {
1276         const float displacementX = event.p2.mFloat;
1277         mScrollPosition.x += displacementX;
1278
1279         ClampHorizontalScroll( layoutSize );
1280       }
1281
1282       if( isVerticalScrollEnabled )
1283       {
1284         const float displacementY = event.p3.mFloat;
1285         mScrollPosition.y += displacementY;
1286
1287         ClampVerticalScroll( layoutSize );
1288       }
1289
1290       mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1291       break;
1292     }
1293     case Gesture::Finished:
1294     case Gesture::Cancelled: // FALLTHROUGH
1295     {
1296       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1297       ChangeState( mEventData->mPreviousState );
1298       break;
1299     }
1300     default:
1301       break;
1302   }
1303 }
1304
1305 void Controller::Impl::OnLongPressEvent( const Event& event )
1306 {
1307   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1308
1309   if( EventData::EDITING == mEventData->mState )
1310   {
1311     ChangeState ( EventData::EDITING_WITH_POPUP );
1312     mEventData->mDecoratorUpdated = true;
1313     mEventData->mUpdateInputStyle = true;
1314   }
1315 }
1316
1317 void Controller::Impl::OnHandleEvent( const Event& event )
1318 {
1319   if( NULL == mEventData )
1320   {
1321     // Nothing to do if there is no text input.
1322     return;
1323   }
1324
1325   const unsigned int state = event.p1.mUint;
1326   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1327   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1328
1329   if( HANDLE_PRESSED == state )
1330   {
1331     // Convert from decorator's coords to text's coords.
1332     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1333     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1334
1335     // Need to calculate the handle's new position.
1336     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1337                                                                           mLogicalModel,
1338                                                                           mMetrics,
1339                                                                           xPosition,
1340                                                                           yPosition );
1341
1342     if( Event::GRAB_HANDLE_EVENT == event.type )
1343     {
1344       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1345
1346       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1347       {
1348         // Updates the cursor position if the handle's new position is different than the current one.
1349         mEventData->mUpdateCursorPosition = true;
1350         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1351         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1352         mEventData->mPrimaryCursorPosition = handleNewPosition;
1353       }
1354
1355       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1356       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1357     }
1358     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1359     {
1360       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1361
1362       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1363           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1364       {
1365         // Updates the highlight box if the handle's new position is different than the current one.
1366         mEventData->mUpdateHighlightBox = true;
1367         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1368         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1369         mEventData->mLeftSelectionPosition = handleNewPosition;
1370       }
1371
1372       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1373       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1374
1375       // Will define the order to scroll the text to match the handle position.
1376       mEventData->mIsLeftHandleSelected = true;
1377     }
1378     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1379     {
1380       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1381
1382       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1383           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1384       {
1385         // Updates the highlight box if the handle's new position is different than the current one.
1386         mEventData->mUpdateHighlightBox = true;
1387         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1388         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1389         mEventData->mRightSelectionPosition = handleNewPosition;
1390       }
1391
1392       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1393       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1394
1395       // Will define the order to scroll the text to match the handle position.
1396       mEventData->mIsLeftHandleSelected = false;
1397     }
1398   } // end ( HANDLE_PRESSED == state )
1399   else if( ( HANDLE_RELEASED == state ) ||
1400            handleStopScrolling )
1401   {
1402     CharacterIndex handlePosition = 0u;
1403     if( handleStopScrolling || isSmoothHandlePanEnabled )
1404     {
1405       // Convert from decorator's coords to text's coords.
1406       const float xPosition = event.p2.mFloat - mScrollPosition.x;
1407       const float yPosition = event.p3.mFloat - mScrollPosition.y;
1408
1409       handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1410                                                     mLogicalModel,
1411                                                     mMetrics,
1412                                                     xPosition,
1413                                                     yPosition );
1414     }
1415
1416     if( Event::GRAB_HANDLE_EVENT == event.type )
1417     {
1418       mEventData->mUpdateCursorPosition = true;
1419       mEventData->mUpdateGrabHandlePosition = true;
1420       mEventData->mUpdateInputStyle = true;
1421
1422       if( !IsClipboardEmpty() )
1423       {
1424         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1425       }
1426
1427       if( handleStopScrolling || isSmoothHandlePanEnabled )
1428       {
1429         mEventData->mScrollAfterUpdatePosition = true;
1430         mEventData->mPrimaryCursorPosition = handlePosition;
1431       }
1432     }
1433     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1434     {
1435       ChangeState( EventData::SELECTING );
1436
1437       mEventData->mUpdateHighlightBox = true;
1438       mEventData->mUpdateLeftSelectionPosition = true;
1439       mEventData->mUpdateRightSelectionPosition = true;
1440
1441       if( handleStopScrolling || isSmoothHandlePanEnabled )
1442       {
1443         mEventData->mScrollAfterUpdatePosition = true;
1444
1445         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1446             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1447         {
1448           mEventData->mLeftSelectionPosition = handlePosition;
1449         }
1450       }
1451     }
1452     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1453     {
1454       ChangeState( EventData::SELECTING );
1455
1456       mEventData->mUpdateHighlightBox = true;
1457       mEventData->mUpdateRightSelectionPosition = true;
1458       mEventData->mUpdateLeftSelectionPosition = true;
1459
1460       if( handleStopScrolling || isSmoothHandlePanEnabled )
1461       {
1462         mEventData->mScrollAfterUpdatePosition = true;
1463         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1464             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1465         {
1466           mEventData->mRightSelectionPosition = handlePosition;
1467         }
1468       }
1469     }
1470
1471     mEventData->mDecoratorUpdated = true;
1472   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1473   else if( HANDLE_SCROLLING == state )
1474   {
1475     const float xSpeed = event.p2.mFloat;
1476     const float ySpeed = event.p3.mFloat;
1477     const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1478     const Vector2 currentScrollPosition = mScrollPosition;
1479
1480     mScrollPosition.x += xSpeed;
1481     mScrollPosition.y += ySpeed;
1482
1483     ClampHorizontalScroll( layoutSize );
1484     ClampVerticalScroll( layoutSize );
1485
1486     bool endOfScroll = false;
1487     if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1488     {
1489       // Notify the decorator there is no more text to scroll.
1490       // The decorator won't send more scroll events.
1491       mEventData->mDecorator->NotifyEndOfScroll();
1492       // Still need to set the position of the handle.
1493       endOfScroll = true;
1494     }
1495
1496     // Set the position of the handle.
1497     const bool scrollRightDirection = xSpeed > 0.f;
1498     const bool scrollBottomDirection = ySpeed > 0.f;
1499     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1500     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1501
1502     if( Event::GRAB_HANDLE_EVENT == event.type )
1503     {
1504       ChangeState( EventData::GRAB_HANDLE_PANNING );
1505
1506       // Get the grab handle position in decorator coords.
1507       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1508
1509       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1510       {
1511         // Position the grag handle close to either the left or right edge.
1512         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1513       }
1514
1515       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1516       {
1517         position.x = mEventData->mCursorHookPositionX;
1518
1519         // Position the grag handle close to either the top or bottom edge.
1520         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1521       }
1522
1523       // Get the new handle position.
1524       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1525       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1526                                                                          mLogicalModel,
1527                                                                          mMetrics,
1528                                                                          position.x - mScrollPosition.x,
1529                                                                          position.y - mScrollPosition.y );
1530
1531       if( mEventData->mPrimaryCursorPosition != handlePosition )
1532       {
1533         mEventData->mUpdateCursorPosition = true;
1534         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1535         mEventData->mScrollAfterUpdatePosition = true;
1536         mEventData->mPrimaryCursorPosition = handlePosition;
1537       }
1538       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1539
1540       // Updates the decorator if the soft handle panning is enabled.
1541       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1542     }
1543     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1544     {
1545       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1546
1547       // Get the selection handle position in decorator coords.
1548       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1549
1550       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1551       {
1552         // Position the selection handle close to either the left or right edge.
1553         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1554       }
1555
1556       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1557       {
1558         position.x = mEventData->mCursorHookPositionX;
1559
1560         // Position the grag handle close to either the top or bottom edge.
1561         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1562       }
1563
1564       // Get the new handle position.
1565       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1566       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1567                                                                          mLogicalModel,
1568                                                                          mMetrics,
1569                                                                          position.x - mScrollPosition.x,
1570                                                                          position.y - mScrollPosition.y );
1571
1572       if( leftSelectionHandleEvent )
1573       {
1574         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1575
1576         if( differentHandles || endOfScroll )
1577         {
1578           mEventData->mUpdateHighlightBox = true;
1579           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1580           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1581           mEventData->mLeftSelectionPosition = handlePosition;
1582         }
1583       }
1584       else
1585       {
1586         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1587         if( differentHandles || endOfScroll )
1588         {
1589           mEventData->mUpdateHighlightBox = true;
1590           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1591           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1592           mEventData->mRightSelectionPosition = handlePosition;
1593         }
1594       }
1595
1596       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1597       {
1598         RepositionSelectionHandles();
1599
1600         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1601       }
1602     }
1603     mEventData->mDecoratorUpdated = true;
1604   } // end ( HANDLE_SCROLLING == state )
1605 }
1606
1607 void Controller::Impl::OnSelectEvent( const Event& event )
1608 {
1609   if( NULL == mEventData )
1610   {
1611     // Nothing to do if there is no text.
1612     return;
1613   }
1614
1615   if( mEventData->mSelectionEnabled )
1616   {
1617     // Convert from control's coords to text's coords.
1618     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1619     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1620
1621     // Calculates the logical position from the x,y coords.
1622     RepositionSelectionHandles( xPosition,
1623                                 yPosition );
1624   }
1625 }
1626
1627 void Controller::Impl::OnSelectAllEvent()
1628 {
1629   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1630
1631   if( NULL == mEventData )
1632   {
1633     // Nothing to do if there is no text.
1634     return;
1635   }
1636
1637   if( mEventData->mSelectionEnabled )
1638   {
1639     ChangeState( EventData::SELECTING );
1640
1641     mEventData->mLeftSelectionPosition = 0u;
1642     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1643
1644     mEventData->mScrollAfterUpdatePosition = true;
1645     mEventData->mUpdateLeftSelectionPosition = true;
1646     mEventData->mUpdateRightSelectionPosition = true;
1647     mEventData->mUpdateHighlightBox = true;
1648   }
1649 }
1650
1651 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1652 {
1653   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1654   {
1655     // Nothing to select if handles are in the same place.
1656     selectedText.clear();
1657     return;
1658   }
1659
1660   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1661
1662   //Get start and end position of selection
1663   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1664   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1665
1666   Vector<Character>& utf32Characters = mLogicalModel->mText;
1667   const Length numberOfCharacters = utf32Characters.Count();
1668
1669   // Validate the start and end selection points
1670   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1671   {
1672     //Get text as a UTF8 string
1673     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1674
1675     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1676     {
1677       // Keep a copy of the current input style.
1678       InputStyle currentInputStyle;
1679       currentInputStyle.Copy( mEventData->mInputStyle );
1680
1681       // Set as input style the style of the first deleted character.
1682       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1683
1684       // Compare if the input style has changed.
1685       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1686
1687       if( hasInputStyleChanged )
1688       {
1689         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1690         // Queue the input style changed signal.
1691         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1692       }
1693
1694       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1695
1696       // Mark the paragraphs to be updated.
1697       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1698       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1699
1700       // Delete text between handles
1701       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1702       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1703       utf32Characters.Erase( first, last );
1704
1705       // Will show the cursor at the first character of the selection.
1706       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1707     }
1708     else
1709     {
1710       // Will show the cursor at the last character of the selection.
1711       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1712     }
1713
1714     mEventData->mDecoratorUpdated = true;
1715   }
1716 }
1717
1718 void Controller::Impl::ShowClipboard()
1719 {
1720   if( mClipboard )
1721   {
1722     mClipboard.ShowClipboard();
1723   }
1724 }
1725
1726 void Controller::Impl::HideClipboard()
1727 {
1728   if( mClipboard && mClipboardHideEnabled )
1729   {
1730     mClipboard.HideClipboard();
1731   }
1732 }
1733
1734 void Controller::Impl::SetClipboardHideEnable(bool enable)
1735 {
1736   mClipboardHideEnabled = enable;
1737 }
1738
1739 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1740 {
1741   //Send string to clipboard
1742   return ( mClipboard && mClipboard.SetItem( source ) );
1743 }
1744
1745 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1746 {
1747   std::string selectedText;
1748   RetrieveSelection( selectedText, deleteAfterSending );
1749   CopyStringToClipboard( selectedText );
1750   ChangeState( EventData::EDITING );
1751 }
1752
1753 void Controller::Impl::RequestGetTextFromClipboard()
1754 {
1755   if ( mClipboard )
1756   {
1757     mClipboard.RequestItem();
1758   }
1759 }
1760
1761 void Controller::Impl::RepositionSelectionHandles()
1762 {
1763   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1764   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1765
1766   if( selectionStart == selectionEnd )
1767   {
1768     // Nothing to select if handles are in the same place.
1769     return;
1770   }
1771
1772   mEventData->mDecorator->ClearHighlights();
1773
1774   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1775   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1776   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1777   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1778   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1779   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1780   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1781
1782   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1783   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1784   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1785
1786   // Swap the indices if the start is greater than the end.
1787   const bool indicesSwapped = selectionStart > selectionEnd;
1788
1789   // Tell the decorator to flip the selection handles if needed.
1790   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1791
1792   if( indicesSwapped )
1793   {
1794     std::swap( selectionStart, selectionEnd );
1795   }
1796
1797   // Get the indices to the first and last selected glyphs.
1798   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1799   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1800   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1801   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1802
1803   // Get the lines where the glyphs are laid-out.
1804   const LineRun* lineRun = mVisualModel->mLines.Begin();
1805
1806   LineIndex lineIndex = 0u;
1807   Length numberOfLines = 0u;
1808   mVisualModel->GetNumberOfLines( glyphStart,
1809                                   1u + glyphEnd - glyphStart,
1810                                   lineIndex,
1811                                   numberOfLines );
1812   const LineIndex firstLineIndex = lineIndex;
1813
1814   // Create the structure to store some selection box info.
1815   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1816   selectionBoxLinesInfo.Resize( numberOfLines );
1817
1818   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1819   selectionBoxInfo->minX = MAX_FLOAT;
1820   selectionBoxInfo->maxX = MIN_FLOAT;
1821
1822   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1823   float minHighlightX = std::numeric_limits<float>::max();
1824   float maxHighlightX = std::numeric_limits<float>::min();
1825   Size highLightSize;
1826   Vector2 highLightPosition; // The highlight position in decorator's coords.
1827
1828   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1829
1830   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1831   selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1832                                                       firstLineIndex );
1833
1834   // Transform to decorator's (control) coords.
1835   selectionBoxInfo->lineOffset += mScrollPosition.y;
1836
1837   lineRun += firstLineIndex;
1838
1839   // The line height is the addition of the line ascender and the line descender.
1840   // However, the line descender has a negative value, hence the subtraction.
1841   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1842
1843   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1844
1845   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1846   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1847   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1848
1849   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1850   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1851   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1852
1853   // The number of quads of the selection box.
1854   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1855   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1856
1857   // Count the actual number of quads.
1858   unsigned int actualNumberOfQuads = 0u;
1859   Vector4 quad;
1860
1861   // Traverse the glyphs.
1862   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1863   {
1864     const GlyphInfo& glyph = *( glyphsBuffer + index );
1865     const Vector2& position = *( positionsBuffer + index );
1866
1867     if( splitStartGlyph )
1868     {
1869       // 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.
1870
1871       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1872       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1873       // Get the direction of the character.
1874       CharacterDirection isCurrentRightToLeft = false;
1875       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1876       {
1877         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1878       }
1879
1880       // The end point could be in the middle of the ligature.
1881       // Calculate the number of characters selected.
1882       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1883
1884       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1885       quad.y = selectionBoxInfo->lineOffset;
1886       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1887       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1888
1889       // Store the min and max 'x' for each line.
1890       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1891       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1892
1893       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1894       ++actualNumberOfQuads;
1895
1896       splitStartGlyph = false;
1897       continue;
1898     }
1899
1900     if( splitEndGlyph && ( index == glyphEnd ) )
1901     {
1902       // 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.
1903
1904       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1905       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1906       // Get the direction of the character.
1907       CharacterDirection isCurrentRightToLeft = false;
1908       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1909       {
1910         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1911       }
1912
1913       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1914
1915       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1916       quad.y = selectionBoxInfo->lineOffset;
1917       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1918       quad.w = quad.y + selectionBoxInfo->lineHeight;
1919
1920       // Store the min and max 'x' for each line.
1921       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1922       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1923
1924       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1925                                             quad );
1926       ++actualNumberOfQuads;
1927
1928       splitEndGlyph = false;
1929       continue;
1930     }
1931
1932     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1933     quad.y = selectionBoxInfo->lineOffset;
1934     quad.z = quad.x + glyph.advance;
1935     quad.w = quad.y + selectionBoxInfo->lineHeight;
1936
1937     // Store the min and max 'x' for each line.
1938     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1939     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1940
1941     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1942                                           quad );
1943     ++actualNumberOfQuads;
1944
1945     // Whether to retrieve the next line.
1946     if( index == lastGlyphOfLine )
1947     {
1948       // Retrieve the next line.
1949       ++lineRun;
1950
1951       // Get the last glyph of the new line.
1952       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1953
1954       ++lineIndex;
1955       if( lineIndex < firstLineIndex + numberOfLines )
1956       {
1957         // Keep the offset and height of the current selection box.
1958         const float currentLineOffset = selectionBoxInfo->lineOffset;
1959         const float currentLineHeight = selectionBoxInfo->lineHeight;
1960
1961         // Get the selection box info for the next line.
1962         ++selectionBoxInfo;
1963
1964         selectionBoxInfo->minX = MAX_FLOAT;
1965         selectionBoxInfo->maxX = MIN_FLOAT;
1966
1967         // Update the line's vertical offset.
1968         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1969
1970         // The line height is the addition of the line ascender and the line descender.
1971         // However, the line descender has a negative value, hence the subtraction.
1972         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1973       }
1974     }
1975   }
1976
1977   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1978   // The final width is calculated after 'boxifying' the selection.
1979   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1980          endIt = selectionBoxLinesInfo.End();
1981        it != endIt;
1982        ++it )
1983   {
1984     const SelectionBoxInfo& info = *it;
1985
1986     // Update the size of the highlighted text.
1987     highLightSize.height += info.lineHeight;
1988     minHighlightX = std::min( minHighlightX, info.minX );
1989     maxHighlightX = std::max( maxHighlightX, info.maxX );
1990   }
1991
1992   // Add extra geometry to 'boxify' the selection.
1993
1994   if( 1u < numberOfLines )
1995   {
1996     // Boxify the first line.
1997     lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1998     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1999
2000     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2001     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2002
2003     if( boxifyBegin )
2004     {
2005       quad.x = 0.f;
2006       quad.y = firstSelectionBoxLineInfo.lineOffset;
2007       quad.z = firstSelectionBoxLineInfo.minX;
2008       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2009
2010       // Boxify at the beginning of the line.
2011       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2012                                             quad );
2013       ++actualNumberOfQuads;
2014
2015       // Update the size of the highlighted text.
2016       minHighlightX = 0.f;
2017     }
2018
2019     if( boxifyEnd )
2020     {
2021       quad.x = firstSelectionBoxLineInfo.maxX;
2022       quad.y = firstSelectionBoxLineInfo.lineOffset;
2023       quad.z = mVisualModel->mControlSize.width;
2024       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2025
2026       // Boxify at the end of the line.
2027       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2028                                             quad );
2029       ++actualNumberOfQuads;
2030
2031       // Update the size of the highlighted text.
2032       maxHighlightX = mVisualModel->mControlSize.width;
2033     }
2034
2035     // Boxify the central lines.
2036     if( 2u < numberOfLines )
2037     {
2038       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2039              endIt = selectionBoxLinesInfo.End() - 1u;
2040            it != endIt;
2041            ++it )
2042       {
2043         const SelectionBoxInfo& info = *it;
2044
2045         quad.x = 0.f;
2046         quad.y = info.lineOffset;
2047         quad.z = info.minX;
2048         quad.w = info.lineOffset + info.lineHeight;
2049
2050         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2051                                               quad );
2052         ++actualNumberOfQuads;
2053
2054         quad.x = info.maxX;
2055         quad.y = info.lineOffset;
2056         quad.z = mVisualModel->mControlSize.width;
2057         quad.w = info.lineOffset + info.lineHeight;
2058
2059         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2060                                               quad );
2061         ++actualNumberOfQuads;
2062       }
2063
2064       // Update the size of the highlighted text.
2065       minHighlightX = 0.f;
2066       maxHighlightX = mVisualModel->mControlSize.width;
2067     }
2068
2069     // Boxify the last line.
2070     lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2071     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2072
2073     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2074     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2075
2076     if( boxifyBegin )
2077     {
2078       quad.x = 0.f;
2079       quad.y = lastSelectionBoxLineInfo.lineOffset;
2080       quad.z = lastSelectionBoxLineInfo.minX;
2081       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2082
2083       // Boxify at the beginning of the line.
2084       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2085                                             quad );
2086       ++actualNumberOfQuads;
2087
2088       // Update the size of the highlighted text.
2089       minHighlightX = 0.f;
2090     }
2091
2092     if( boxifyEnd )
2093     {
2094       quad.x = lastSelectionBoxLineInfo.maxX;
2095       quad.y = lastSelectionBoxLineInfo.lineOffset;
2096       quad.z = mVisualModel->mControlSize.width;
2097       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2098
2099       // Boxify at the end of the line.
2100       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2101                                             quad );
2102       ++actualNumberOfQuads;
2103
2104       // Update the size of the highlighted text.
2105       maxHighlightX = mVisualModel->mControlSize.width;
2106     }
2107   }
2108
2109   // Set the actual number of quads.
2110   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2111
2112   // Sets the highlight's size and position. In decorator's coords.
2113   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2114   highLightSize.width = maxHighlightX - minHighlightX;
2115
2116   highLightPosition.x = minHighlightX;
2117   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2118   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2119
2120   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2121
2122   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2123   {
2124     CursorInfo primaryCursorInfo;
2125     GetCursorPosition( mEventData->mLeftSelectionPosition,
2126                        primaryCursorInfo );
2127
2128     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2129
2130     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2131                                          primaryPosition.x,
2132                                          primaryCursorInfo.lineOffset + mScrollPosition.y,
2133                                          primaryCursorInfo.lineHeight );
2134
2135     CursorInfo secondaryCursorInfo;
2136     GetCursorPosition( mEventData->mRightSelectionPosition,
2137                        secondaryCursorInfo );
2138
2139     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2140
2141     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2142                                          secondaryPosition.x,
2143                                          secondaryCursorInfo.lineOffset + mScrollPosition.y,
2144                                          secondaryCursorInfo.lineHeight );
2145   }
2146
2147   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2148   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2149
2150   // Set the flag to update the decorator.
2151   mEventData->mDecoratorUpdated = true;
2152 }
2153
2154 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2155 {
2156   if( NULL == mEventData )
2157   {
2158     // Nothing to do if there is no text input.
2159     return;
2160   }
2161
2162   if( IsShowingPlaceholderText() )
2163   {
2164     // Nothing to do if there is the place-holder text.
2165     return;
2166   }
2167
2168   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2169   const Length numberOfLines  = mVisualModel->mLines.Count();
2170   if( ( 0 == numberOfGlyphs ) ||
2171       ( 0 == numberOfLines ) )
2172   {
2173     // Nothing to do if there is no text.
2174     return;
2175   }
2176
2177   // Find which word was selected
2178   CharacterIndex selectionStart( 0 );
2179   CharacterIndex selectionEnd( 0 );
2180   const bool indicesFound = FindSelectionIndices( mVisualModel,
2181                                                   mLogicalModel,
2182                                                   mMetrics,
2183                                                   visualX,
2184                                                   visualY,
2185                                                   selectionStart,
2186                                                   selectionEnd );
2187   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2188
2189   if( indicesFound )
2190   {
2191     ChangeState( EventData::SELECTING );
2192
2193     mEventData->mLeftSelectionPosition = selectionStart;
2194     mEventData->mRightSelectionPosition = selectionEnd;
2195
2196     mEventData->mUpdateLeftSelectionPosition = true;
2197     mEventData->mUpdateRightSelectionPosition = true;
2198     mEventData->mUpdateHighlightBox = true;
2199
2200     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2201   }
2202   else
2203   {
2204     // Nothing to select. i.e. a white space, out of bounds
2205     ChangeState( EventData::EDITING );
2206
2207     mEventData->mPrimaryCursorPosition = selectionEnd;
2208
2209     mEventData->mUpdateCursorPosition = true;
2210     mEventData->mUpdateGrabHandlePosition = true;
2211     mEventData->mScrollAfterUpdatePosition = true;
2212     mEventData->mUpdateInputStyle = true;
2213   }
2214 }
2215
2216 void Controller::Impl::SetPopupButtons()
2217 {
2218   /**
2219    *  Sets the Popup buttons to be shown depending on State.
2220    *
2221    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2222    *
2223    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2224    */
2225
2226   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2227
2228   if( EventData::SELECTING == mEventData->mState )
2229   {
2230     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2231
2232     if( !IsClipboardEmpty() )
2233     {
2234       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2235       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2236     }
2237
2238     if( !mEventData->mAllTextSelected )
2239     {
2240       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2241     }
2242   }
2243   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2244   {
2245     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2246     {
2247       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2248     }
2249
2250     if( !IsClipboardEmpty() )
2251     {
2252       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2253       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2254     }
2255   }
2256   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2257   {
2258     if ( !IsClipboardEmpty() )
2259     {
2260       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2261       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2262     }
2263   }
2264
2265   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2266 }
2267
2268 void Controller::Impl::ChangeState( EventData::State newState )
2269 {
2270   if( NULL == mEventData )
2271   {
2272     // Nothing to do if there is no text input.
2273     return;
2274   }
2275
2276   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2277
2278   if( mEventData->mState != newState )
2279   {
2280     mEventData->mPreviousState = mEventData->mState;
2281     mEventData->mState = newState;
2282
2283     switch( mEventData->mState )
2284     {
2285       case EventData::INACTIVE:
2286       {
2287         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2288         mEventData->mDecorator->StopCursorBlink();
2289         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2290         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2291         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2292         mEventData->mDecorator->SetHighlightActive( false );
2293         mEventData->mDecorator->SetPopupActive( false );
2294         mEventData->mDecoratorUpdated = true;
2295         break;
2296       }
2297       case EventData::INTERRUPTED:
2298       {
2299         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2300         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2301         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2302         mEventData->mDecorator->SetHighlightActive( false );
2303         mEventData->mDecorator->SetPopupActive( false );
2304         mEventData->mDecoratorUpdated = true;
2305         break;
2306       }
2307       case EventData::SELECTING:
2308       {
2309         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2310         mEventData->mDecorator->StopCursorBlink();
2311         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2312         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2313         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2314         mEventData->mDecorator->SetHighlightActive( true );
2315         if( mEventData->mGrabHandlePopupEnabled )
2316         {
2317           SetPopupButtons();
2318           mEventData->mDecorator->SetPopupActive( true );
2319         }
2320         mEventData->mDecoratorUpdated = true;
2321         break;
2322       }
2323       case EventData::EDITING:
2324       {
2325         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2326         if( mEventData->mCursorBlinkEnabled )
2327         {
2328           mEventData->mDecorator->StartCursorBlink();
2329         }
2330         // Grab handle is not shown until a tap is received whilst EDITING
2331         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2332         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2333         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2334         mEventData->mDecorator->SetHighlightActive( false );
2335         if( mEventData->mGrabHandlePopupEnabled )
2336         {
2337           mEventData->mDecorator->SetPopupActive( false );
2338         }
2339         mEventData->mDecoratorUpdated = true;
2340         break;
2341       }
2342       case EventData::EDITING_WITH_POPUP:
2343       {
2344         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2345
2346         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2347         if( mEventData->mCursorBlinkEnabled )
2348         {
2349           mEventData->mDecorator->StartCursorBlink();
2350         }
2351         if( mEventData->mSelectionEnabled )
2352         {
2353           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2354           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2355           mEventData->mDecorator->SetHighlightActive( false );
2356         }
2357         else
2358         {
2359           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2360         }
2361         if( mEventData->mGrabHandlePopupEnabled )
2362         {
2363           SetPopupButtons();
2364           mEventData->mDecorator->SetPopupActive( true );
2365         }
2366         mEventData->mDecoratorUpdated = true;
2367         break;
2368       }
2369       case EventData::EDITING_WITH_GRAB_HANDLE:
2370       {
2371         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2372
2373         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2374         if( mEventData->mCursorBlinkEnabled )
2375         {
2376           mEventData->mDecorator->StartCursorBlink();
2377         }
2378         // Grab handle is not shown until a tap is received whilst EDITING
2379         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2380         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2381         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2382         mEventData->mDecorator->SetHighlightActive( false );
2383         if( mEventData->mGrabHandlePopupEnabled )
2384         {
2385           mEventData->mDecorator->SetPopupActive( false );
2386         }
2387         mEventData->mDecoratorUpdated = true;
2388         break;
2389       }
2390       case EventData::SELECTION_HANDLE_PANNING:
2391       {
2392         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2393         mEventData->mDecorator->StopCursorBlink();
2394         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2395         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2396         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2397         mEventData->mDecorator->SetHighlightActive( true );
2398         if( mEventData->mGrabHandlePopupEnabled )
2399         {
2400           mEventData->mDecorator->SetPopupActive( false );
2401         }
2402         mEventData->mDecoratorUpdated = true;
2403         break;
2404       }
2405       case EventData::GRAB_HANDLE_PANNING:
2406       {
2407         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2408
2409         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2410         if( mEventData->mCursorBlinkEnabled )
2411         {
2412           mEventData->mDecorator->StartCursorBlink();
2413         }
2414         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2415         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2416         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2417         mEventData->mDecorator->SetHighlightActive( false );
2418         if( mEventData->mGrabHandlePopupEnabled )
2419         {
2420           mEventData->mDecorator->SetPopupActive( false );
2421         }
2422         mEventData->mDecoratorUpdated = true;
2423         break;
2424       }
2425       case EventData::EDITING_WITH_PASTE_POPUP:
2426       {
2427         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2428
2429         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2430         if( mEventData->mCursorBlinkEnabled )
2431         {
2432           mEventData->mDecorator->StartCursorBlink();
2433         }
2434
2435         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2436         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2437         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2438         mEventData->mDecorator->SetHighlightActive( false );
2439
2440         if( mEventData->mGrabHandlePopupEnabled )
2441         {
2442           SetPopupButtons();
2443           mEventData->mDecorator->SetPopupActive( true );
2444         }
2445         mEventData->mDecoratorUpdated = true;
2446         break;
2447       }
2448       case EventData::TEXT_PANNING:
2449       {
2450         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2451         mEventData->mDecorator->StopCursorBlink();
2452         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2453         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2454             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2455         {
2456           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2457           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2458           mEventData->mDecorator->SetHighlightActive( true );
2459         }
2460
2461         if( mEventData->mGrabHandlePopupEnabled )
2462         {
2463           mEventData->mDecorator->SetPopupActive( false );
2464         }
2465
2466         mEventData->mDecoratorUpdated = true;
2467         break;
2468       }
2469     }
2470   }
2471 }
2472
2473 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2474                                           CursorInfo& cursorInfo )
2475 {
2476   if( !IsShowingRealText() )
2477   {
2478     // Do not want to use the place-holder text to set the cursor position.
2479
2480     // Use the line's height of the font's family set to set the cursor's size.
2481     // If there is no font's family set, use the default font.
2482     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2483
2484     cursorInfo.lineOffset = 0.f;
2485     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2486     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2487
2488     switch( mLayoutEngine.GetHorizontalAlignment() )
2489     {
2490       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2491       {
2492         cursorInfo.primaryPosition.x = 0.f;
2493         break;
2494       }
2495       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2496       {
2497         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2498         break;
2499       }
2500       case LayoutEngine::HORIZONTAL_ALIGN_END:
2501       {
2502         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2503         break;
2504       }
2505     }
2506
2507     // Nothing else to do.
2508     return;
2509   }
2510
2511   Text::GetCursorPosition( mVisualModel,
2512                            mLogicalModel,
2513                            mMetrics,
2514                            logical,
2515                            cursorInfo );
2516
2517   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2518   {
2519     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2520
2521     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2522     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2523
2524     if( 0.f > cursorInfo.primaryPosition.x )
2525     {
2526       cursorInfo.primaryPosition.x = 0.f;
2527     }
2528
2529     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2530     if( cursorInfo.primaryPosition.x > edgeWidth )
2531     {
2532       cursorInfo.primaryPosition.x = edgeWidth;
2533     }
2534   }
2535 }
2536
2537 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2538 {
2539   if( NULL == mEventData )
2540   {
2541     // Nothing to do if there is no text input.
2542     return 0u;
2543   }
2544
2545   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2546
2547   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2548   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2549
2550   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2551   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2552
2553   if( numberOfCharacters > 1u )
2554   {
2555     const Script script = mLogicalModel->GetScript( index );
2556     if( HasLigatureMustBreak( script ) )
2557     {
2558       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»,  ...
2559       numberOfCharacters = 1u;
2560     }
2561   }
2562   else
2563   {
2564     while( 0u == numberOfCharacters )
2565     {
2566       ++glyphIndex;
2567       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2568     }
2569   }
2570
2571   if( index < mEventData->mPrimaryCursorPosition )
2572   {
2573     cursorIndex -= numberOfCharacters;
2574   }
2575   else
2576   {
2577     cursorIndex += numberOfCharacters;
2578   }
2579
2580   // Will update the cursor hook position.
2581   mEventData->mUpdateCursorHookPosition = true;
2582
2583   return cursorIndex;
2584 }
2585
2586 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2587 {
2588   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2589   if( NULL == mEventData )
2590   {
2591     // Nothing to do if there is no text input.
2592     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2593     return;
2594   }
2595
2596   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2597
2598   // Sets the cursor position.
2599   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2600                                        cursorPosition.x,
2601                                        cursorPosition.y,
2602                                        cursorInfo.primaryCursorHeight,
2603                                        cursorInfo.lineHeight );
2604   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2605
2606   if( mEventData->mUpdateGrabHandlePosition )
2607   {
2608     // Sets the grab handle position.
2609     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2610                                          cursorPosition.x,
2611                                          cursorInfo.lineOffset + mScrollPosition.y,
2612                                          cursorInfo.lineHeight );
2613   }
2614
2615   if( cursorInfo.isSecondaryCursor )
2616   {
2617     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2618                                          cursorInfo.secondaryPosition.x + mScrollPosition.x,
2619                                          cursorInfo.secondaryPosition.y + mScrollPosition.y,
2620                                          cursorInfo.secondaryCursorHeight,
2621                                          cursorInfo.lineHeight );
2622     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2623   }
2624
2625   // Set which cursors are active according the state.
2626   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2627   {
2628     if( cursorInfo.isSecondaryCursor )
2629     {
2630       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2631     }
2632     else
2633     {
2634       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2635     }
2636   }
2637   else
2638   {
2639     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2640   }
2641
2642   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2643 }
2644
2645 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2646                                               const CursorInfo& cursorInfo )
2647 {
2648   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2649       ( RIGHT_SELECTION_HANDLE != handleType ) )
2650   {
2651     return;
2652   }
2653
2654   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2655
2656   // Sets the handle's position.
2657   mEventData->mDecorator->SetPosition( handleType,
2658                                        cursorPosition.x,
2659                                        cursorInfo.lineOffset + mScrollPosition.y,
2660                                        cursorInfo.lineHeight );
2661
2662   // If selection handle at start of the text and other at end of the text then all text is selected.
2663   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2664   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2665   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2666 }
2667
2668 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2669 {
2670   // Clamp between -space & 0.
2671
2672   if( layoutSize.width > mVisualModel->mControlSize.width )
2673   {
2674     const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2675     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2676     mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2677
2678     mEventData->mDecoratorUpdated = true;
2679   }
2680   else
2681   {
2682     mScrollPosition.x = 0.f;
2683   }
2684 }
2685
2686 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2687 {
2688   if( LayoutEngine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2689   {
2690     // Nothing to do if the text is single line.
2691     return;
2692   }
2693
2694   // Clamp between -space & 0.
2695   if( layoutSize.height > mVisualModel->mControlSize.height )
2696   {
2697     const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2698     mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2699     mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2700
2701     mEventData->mDecoratorUpdated = true;
2702   }
2703   else
2704   {
2705     mScrollPosition.y = 0.f;
2706   }
2707 }
2708
2709 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2710 {
2711   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2712
2713   // position is in actor's coords.
2714   const float positionEndX = position.x + cursorWidth;
2715   const float positionEndY = position.y + lineHeight;
2716
2717   // Transform the position to decorator coords.
2718   const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2719   const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2720
2721   const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2722   const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2723
2724   if( decoratorPositionBeginX < 0.f )
2725   {
2726     mScrollPosition.x = -position.x;
2727   }
2728   else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2729   {
2730     mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2731   }
2732
2733   if( decoratorPositionBeginY < 0.f )
2734   {
2735     mScrollPosition.y = -position.y;
2736   }
2737   else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2738   {
2739     mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2740   }
2741 }
2742
2743 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2744 {
2745   // Get the current cursor position in decorator coords.
2746   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2747
2748   // Calculate the offset to match the cursor position before the character was deleted.
2749   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2750   mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2751
2752   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2753   ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2754
2755   // Makes the new cursor position visible if needed.
2756   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2757 }
2758
2759 void Controller::Impl::RequestRelayout()
2760 {
2761   if( NULL != mControlInterface )
2762   {
2763     mControlInterface->RequestTextRelayout();
2764   }
2765 }
2766
2767 } // namespace Text
2768
2769 } // namespace Toolkit
2770
2771 } // namespace Dali