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