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