Notify multiline hint to IMF context
[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 id.
837       const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
838
839       // Validates the fonts. If there is a character with no assigned font it sets a default one.
840       // After this call, fonts are validated.
841       multilanguageSupport.ValidateFonts( utf32Characters,
842                                           scripts,
843                                           fontDescriptionRuns,
844                                           defaultFontId,
845                                           startIndex,
846                                           requestedNumberOfCharacters,
847                                           validFonts );
848     }
849     updated = true;
850   }
851
852   Vector<Character> mirroredUtf32Characters;
853   bool textMirrored = false;
854   const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
855   if( NO_OPERATION != ( BIDI_INFO & operations ) )
856   {
857     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
858     bidirectionalInfo.Reserve( numberOfParagraphs );
859
860     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
861     SetBidirectionalInfo( utf32Characters,
862                           scripts,
863                           lineBreakInfo,
864                           startIndex,
865                           requestedNumberOfCharacters,
866                           bidirectionalInfo );
867
868     if( 0u != bidirectionalInfo.Count() )
869     {
870       // Only set the character directions if there is right to left characters.
871       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
872       GetCharactersDirection( bidirectionalInfo,
873                               numberOfCharacters,
874                               startIndex,
875                               requestedNumberOfCharacters,
876                               directions );
877
878       // This paragraph has right to left text. Some characters may need to be mirrored.
879       // TODO: consider if the mirrored string can be stored as well.
880
881       textMirrored = GetMirroredText( utf32Characters,
882                                       directions,
883                                       bidirectionalInfo,
884                                       startIndex,
885                                       requestedNumberOfCharacters,
886                                       mirroredUtf32Characters );
887     }
888     else
889     {
890       // There is no right to left characters. Clear the directions vector.
891       mLogicalModel->mCharacterDirections.Clear();
892     }
893     updated = true;
894   }
895
896   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
897   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
898   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
899   Vector<GlyphIndex> newParagraphGlyphs;
900   newParagraphGlyphs.Reserve( numberOfParagraphs );
901
902   const Length currentNumberOfGlyphs = glyphs.Count();
903   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
904   {
905     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
906     // Shapes the text.
907     ShapeText( textToShape,
908                lineBreakInfo,
909                scripts,
910                validFonts,
911                startIndex,
912                mTextUpdateInfo.mStartGlyphIndex,
913                requestedNumberOfCharacters,
914                glyphs,
915                glyphsToCharactersMap,
916                charactersPerGlyph,
917                newParagraphGlyphs );
918
919     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
920     mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
921     mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
922     updated = true;
923   }
924
925   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
926
927   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
928   {
929     GlyphInfo* glyphsBuffer = glyphs.Begin();
930     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
931
932     // Update the width and advance of all new paragraph characters.
933     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
934     {
935       const GlyphIndex index = *it;
936       GlyphInfo& glyph = *( glyphsBuffer + index );
937
938       glyph.xBearing = 0.f;
939       glyph.width = 0.f;
940       glyph.advance = 0.f;
941     }
942     updated = true;
943   }
944
945   if( NO_OPERATION != ( COLOR & operations ) )
946   {
947     // Set the color runs in glyphs.
948     SetColorSegmentationInfo( mLogicalModel->mColorRuns,
949                               mVisualModel->mCharactersToGlyph,
950                               mVisualModel->mGlyphsPerCharacter,
951                               startIndex,
952                               mTextUpdateInfo.mStartGlyphIndex,
953                               requestedNumberOfCharacters,
954                               mVisualModel->mColors,
955                               mVisualModel->mColorIndices );
956
957     updated = true;
958   }
959
960   if( ( NULL != mEventData ) &&
961       mEventData->mPreEditFlag &&
962       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
963   {
964     // Add the underline for the pre-edit text.
965     const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
966     const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
967
968     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
969     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
970     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
971     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
972
973     GlyphRun underlineRun;
974     underlineRun.glyphIndex = glyphStart;
975     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
976
977     // TODO: At the moment the underline runs are only for pre-edit.
978     mVisualModel->mUnderlineRuns.PushBack( underlineRun );
979   }
980
981   // The estimated number of lines. Used to avoid reallocations when layouting.
982   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
983
984   // Set the previous number of characters for the next time the text is updated.
985   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
986
987   return updated;
988 }
989
990 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
991 {
992   // Sets the default text's color.
993   inputStyle.textColor = mTextColor;
994   inputStyle.isDefaultColor = true;
995
996   inputStyle.familyName.clear();
997   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
998   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
999   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1000   inputStyle.size = 0.f;
1001
1002   inputStyle.familyDefined = false;
1003   inputStyle.weightDefined = false;
1004   inputStyle.widthDefined = false;
1005   inputStyle.slantDefined = false;
1006   inputStyle.sizeDefined = false;
1007
1008   // Sets the default font's family name, weight, width, slant and size.
1009   if( mFontDefaults )
1010   {
1011     if( mFontDefaults->familyDefined )
1012     {
1013       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1014       inputStyle.familyDefined = true;
1015     }
1016
1017     if( mFontDefaults->weightDefined )
1018     {
1019       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1020       inputStyle.weightDefined = true;
1021     }
1022
1023     if( mFontDefaults->widthDefined )
1024     {
1025       inputStyle.width = mFontDefaults->mFontDescription.width;
1026       inputStyle.widthDefined = true;
1027     }
1028
1029     if( mFontDefaults->slantDefined )
1030     {
1031       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1032       inputStyle.slantDefined = true;
1033     }
1034
1035     if( mFontDefaults->sizeDefined )
1036     {
1037       inputStyle.size = mFontDefaults->mDefaultPointSize;
1038       inputStyle.sizeDefined = true;
1039     }
1040   }
1041 }
1042
1043 float Controller::Impl::GetDefaultFontLineHeight()
1044 {
1045   FontId defaultFontId = 0u;
1046   if( NULL == mFontDefaults )
1047   {
1048     TextAbstraction::FontDescription fontDescription;
1049     defaultFontId = mFontClient.GetFontId( fontDescription );
1050   }
1051   else
1052   {
1053     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1054   }
1055
1056   Text::FontMetrics fontMetrics;
1057   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1058
1059   return( fontMetrics.ascender - fontMetrics.descender );
1060 }
1061
1062 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1063 {
1064   if( NULL == mEventData )
1065   {
1066     // Nothing to do if there is no text input.
1067     return;
1068   }
1069
1070   int keyCode = event.p1.mInt;
1071
1072   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1073   {
1074     if( mEventData->mPrimaryCursorPosition > 0u )
1075     {
1076       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1077     }
1078   }
1079   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1080   {
1081     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1082     {
1083       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1084     }
1085   }
1086   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1087   {
1088     // Get first the line index of the current cursor position index.
1089     CharacterIndex characterIndex = 0u;
1090
1091     if( mEventData->mPrimaryCursorPosition > 0u )
1092     {
1093       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1094     }
1095
1096     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1097
1098     if( lineIndex > 0u )
1099     {
1100       // Retrieve the cursor position info.
1101       CursorInfo cursorInfo;
1102       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1103                          cursorInfo );
1104
1105       // Get the line above.
1106       const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1107
1108       // Get the next hit 'y' point.
1109       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1110
1111       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1112       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1113                                                                         mLogicalModel,
1114                                                                         mMetrics,
1115                                                                         mEventData->mCursorHookPositionX,
1116                                                                         hitPointY );
1117     }
1118   }
1119   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1120   {
1121     // Get first the line index of the current cursor position index.
1122     CharacterIndex characterIndex = 0u;
1123
1124     if( mEventData->mPrimaryCursorPosition > 0u )
1125     {
1126       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1127     }
1128
1129     const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1130
1131     if( lineIndex + 1u < mVisualModel->mLines.Count() )
1132     {
1133       // Retrieve the cursor position info.
1134       CursorInfo cursorInfo;
1135       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1136                          cursorInfo );
1137
1138       // Get the line below.
1139       const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1140
1141       // Get the next hit 'y' point.
1142       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1143
1144       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1145       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1146                                                                         mLogicalModel,
1147                                                                         mMetrics,
1148                                                                         mEventData->mCursorHookPositionX,
1149                                                                         hitPointY );
1150     }
1151   }
1152
1153   mEventData->mUpdateCursorPosition = true;
1154   mEventData->mUpdateInputStyle = true;
1155   mEventData->mScrollAfterUpdatePosition = true;
1156 }
1157
1158 void Controller::Impl::OnTapEvent( const Event& event )
1159 {
1160   if( NULL != mEventData )
1161   {
1162     const unsigned int tapCount = event.p1.mUint;
1163
1164     if( 1u == tapCount )
1165     {
1166       if( IsShowingRealText() )
1167       {
1168         // Convert from control's coords to text's coords.
1169         const float xPosition = event.p2.mFloat - mScrollPosition.x;
1170         const float yPosition = event.p3.mFloat - mScrollPosition.y;
1171
1172         // Keep the tap 'x' position. Used to move the cursor.
1173         mEventData->mCursorHookPositionX = xPosition;
1174
1175         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1176                                                                           mLogicalModel,
1177                                                                           mMetrics,
1178                                                                           xPosition,
1179                                                                           yPosition );
1180
1181         // When the cursor position is changing, delay cursor blinking
1182         mEventData->mDecorator->DelayCursorBlink();
1183       }
1184       else
1185       {
1186         mEventData->mPrimaryCursorPosition = 0u;
1187       }
1188
1189       mEventData->mUpdateCursorPosition = true;
1190       mEventData->mUpdateGrabHandlePosition = true;
1191       mEventData->mScrollAfterUpdatePosition = true;
1192       mEventData->mUpdateInputStyle = true;
1193
1194       // Notify the cursor position to the imf manager.
1195       if( mEventData->mImfManager )
1196       {
1197         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1198         mEventData->mImfManager.NotifyCursorPosition();
1199       }
1200     }
1201   }
1202 }
1203
1204 void Controller::Impl::OnPanEvent( const Event& event )
1205 {
1206   if( NULL == mEventData )
1207   {
1208     // Nothing to do if there is no text input.
1209     return;
1210   }
1211
1212   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1213   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1214
1215   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1216   {
1217     // Nothing to do if scrolling is not enabled.
1218     return;
1219   }
1220
1221   const int state = event.p1.mInt;
1222
1223   switch( state )
1224   {
1225     case Gesture::Started:
1226     {
1227       // Will remove the cursor, handles or text's popup, ...
1228       ChangeState( EventData::TEXT_PANNING );
1229       break;
1230     }
1231     case Gesture::Continuing:
1232     {
1233       const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1234       const Vector2 currentScroll = mScrollPosition;
1235
1236       if( isHorizontalScrollEnabled )
1237       {
1238         const float displacementX = event.p2.mFloat;
1239         mScrollPosition.x += displacementX;
1240
1241         ClampHorizontalScroll( layoutSize );
1242       }
1243
1244       if( isVerticalScrollEnabled )
1245       {
1246         const float displacementY = event.p3.mFloat;
1247         mScrollPosition.y += displacementY;
1248
1249         ClampVerticalScroll( layoutSize );
1250       }
1251
1252       mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1253       break;
1254     }
1255     case Gesture::Finished:
1256     case Gesture::Cancelled: // FALLTHROUGH
1257     {
1258       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1259       ChangeState( mEventData->mPreviousState );
1260       break;
1261     }
1262     default:
1263       break;
1264   }
1265 }
1266
1267 void Controller::Impl::OnLongPressEvent( const Event& event )
1268 {
1269   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1270
1271   if( EventData::EDITING == mEventData->mState )
1272   {
1273     ChangeState ( EventData::EDITING_WITH_POPUP );
1274     mEventData->mDecoratorUpdated = true;
1275   }
1276 }
1277
1278 void Controller::Impl::OnHandleEvent( const Event& event )
1279 {
1280   if( NULL == mEventData )
1281   {
1282     // Nothing to do if there is no text input.
1283     return;
1284   }
1285
1286   const unsigned int state = event.p1.mUint;
1287   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1288   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1289
1290   if( HANDLE_PRESSED == state )
1291   {
1292     // Convert from decorator's coords to text's coords.
1293     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1294     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1295
1296     // Need to calculate the handle's new position.
1297     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1298                                                                           mLogicalModel,
1299                                                                           mMetrics,
1300                                                                           xPosition,
1301                                                                           yPosition );
1302
1303     if( Event::GRAB_HANDLE_EVENT == event.type )
1304     {
1305       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1306
1307       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1308       {
1309         // Updates the cursor position if the handle's new position is different than the current one.
1310         mEventData->mUpdateCursorPosition = true;
1311         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1312         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1313         mEventData->mPrimaryCursorPosition = handleNewPosition;
1314       }
1315
1316       // 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.
1317       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1318     }
1319     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1320     {
1321       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1322
1323       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1324           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1325       {
1326         // Updates the highlight box if the handle's new position is different than the current one.
1327         mEventData->mUpdateHighlightBox = true;
1328         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1329         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1330         mEventData->mLeftSelectionPosition = handleNewPosition;
1331       }
1332
1333       // 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.
1334       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1335
1336       // Will define the order to scroll the text to match the handle position.
1337       mEventData->mIsLeftHandleSelected = true;
1338     }
1339     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1340     {
1341       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1342
1343       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1344           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1345       {
1346         // Updates the highlight box if the handle's new position is different than the current one.
1347         mEventData->mUpdateHighlightBox = true;
1348         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1349         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1350         mEventData->mRightSelectionPosition = handleNewPosition;
1351       }
1352
1353       // 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.
1354       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1355
1356       // Will define the order to scroll the text to match the handle position.
1357       mEventData->mIsLeftHandleSelected = false;
1358     }
1359   } // end ( HANDLE_PRESSED == state )
1360   else if( ( HANDLE_RELEASED == state ) ||
1361            handleStopScrolling )
1362   {
1363     CharacterIndex handlePosition = 0u;
1364     if( handleStopScrolling || isSmoothHandlePanEnabled )
1365     {
1366       // Convert from decorator's coords to text's coords.
1367       const float xPosition = event.p2.mFloat - mScrollPosition.x;
1368       const float yPosition = event.p3.mFloat - mScrollPosition.y;
1369
1370       handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1371                                                     mLogicalModel,
1372                                                     mMetrics,
1373                                                     xPosition,
1374                                                     yPosition );
1375     }
1376
1377     if( Event::GRAB_HANDLE_EVENT == event.type )
1378     {
1379       mEventData->mUpdateCursorPosition = true;
1380       mEventData->mUpdateGrabHandlePosition = true;
1381       mEventData->mUpdateInputStyle = true;
1382
1383       if( !IsClipboardEmpty() )
1384       {
1385         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1386       }
1387
1388       if( handleStopScrolling || isSmoothHandlePanEnabled )
1389       {
1390         mEventData->mScrollAfterUpdatePosition = true;
1391         mEventData->mPrimaryCursorPosition = handlePosition;
1392       }
1393     }
1394     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1395     {
1396       ChangeState( EventData::SELECTING );
1397
1398       mEventData->mUpdateHighlightBox = true;
1399       mEventData->mUpdateLeftSelectionPosition = true;
1400       mEventData->mUpdateRightSelectionPosition = true;
1401
1402       if( handleStopScrolling || isSmoothHandlePanEnabled )
1403       {
1404         mEventData->mScrollAfterUpdatePosition = true;
1405
1406         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1407             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1408         {
1409           mEventData->mLeftSelectionPosition = handlePosition;
1410         }
1411       }
1412     }
1413     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1414     {
1415       ChangeState( EventData::SELECTING );
1416
1417       mEventData->mUpdateHighlightBox = true;
1418       mEventData->mUpdateRightSelectionPosition = true;
1419       mEventData->mUpdateLeftSelectionPosition = true;
1420
1421       if( handleStopScrolling || isSmoothHandlePanEnabled )
1422       {
1423         mEventData->mScrollAfterUpdatePosition = true;
1424         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1425             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1426         {
1427           mEventData->mRightSelectionPosition = handlePosition;
1428         }
1429       }
1430     }
1431
1432     mEventData->mDecoratorUpdated = true;
1433   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1434   else if( HANDLE_SCROLLING == state )
1435   {
1436     const float xSpeed = event.p2.mFloat;
1437     const float ySpeed = event.p3.mFloat;
1438     const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1439     const Vector2 currentScrollPosition = mScrollPosition;
1440
1441     mScrollPosition.x += xSpeed;
1442     mScrollPosition.y += ySpeed;
1443
1444     ClampHorizontalScroll( layoutSize );
1445     ClampVerticalScroll( layoutSize );
1446
1447     bool endOfScroll = false;
1448     if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1449     {
1450       // Notify the decorator there is no more text to scroll.
1451       // The decorator won't send more scroll events.
1452       mEventData->mDecorator->NotifyEndOfScroll();
1453       // Still need to set the position of the handle.
1454       endOfScroll = true;
1455     }
1456
1457     // Set the position of the handle.
1458     const bool scrollRightDirection = xSpeed > 0.f;
1459     const bool scrollBottomDirection = ySpeed > 0.f;
1460     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1461     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1462
1463     if( Event::GRAB_HANDLE_EVENT == event.type )
1464     {
1465       ChangeState( EventData::GRAB_HANDLE_PANNING );
1466
1467       // Get the grab handle position in decorator coords.
1468       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1469
1470       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1471       {
1472         // Position the grag handle close to either the left or right edge.
1473         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1474       }
1475
1476       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1477       {
1478         position.x = mEventData->mCursorHookPositionX;
1479
1480         // Position the grag handle close to either the top or bottom edge.
1481         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1482       }
1483
1484       // Get the new handle position.
1485       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1486       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1487                                                                          mLogicalModel,
1488                                                                          mMetrics,
1489                                                                          position.x - mScrollPosition.x,
1490                                                                          position.y - mScrollPosition.y );
1491
1492       if( mEventData->mPrimaryCursorPosition != handlePosition )
1493       {
1494         mEventData->mUpdateCursorPosition = true;
1495         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1496         mEventData->mScrollAfterUpdatePosition = true;
1497         mEventData->mPrimaryCursorPosition = handlePosition;
1498       }
1499       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1500
1501       // Updates the decorator if the soft handle panning is enabled.
1502       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1503     }
1504     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1505     {
1506       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1507
1508       // Get the selection handle position in decorator coords.
1509       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1510
1511       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1512       {
1513         // Position the selection handle close to either the left or right edge.
1514         position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1515       }
1516
1517       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1518       {
1519         position.x = mEventData->mCursorHookPositionX;
1520
1521         // Position the grag handle close to either the top or bottom edge.
1522         position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1523       }
1524
1525       // Get the new handle position.
1526       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1527       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1528                                                                          mLogicalModel,
1529                                                                          mMetrics,
1530                                                                          position.x - mScrollPosition.x,
1531                                                                          position.y - mScrollPosition.y );
1532
1533       if( leftSelectionHandleEvent )
1534       {
1535         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1536
1537         if( differentHandles || endOfScroll )
1538         {
1539           mEventData->mUpdateHighlightBox = true;
1540           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1541           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1542           mEventData->mLeftSelectionPosition = handlePosition;
1543         }
1544       }
1545       else
1546       {
1547         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1548         if( differentHandles || endOfScroll )
1549         {
1550           mEventData->mUpdateHighlightBox = true;
1551           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1552           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1553           mEventData->mRightSelectionPosition = handlePosition;
1554         }
1555       }
1556
1557       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1558       {
1559         RepositionSelectionHandles();
1560
1561         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1562       }
1563     }
1564     mEventData->mDecoratorUpdated = true;
1565   } // end ( HANDLE_SCROLLING == state )
1566 }
1567
1568 void Controller::Impl::OnSelectEvent( const Event& event )
1569 {
1570   if( NULL == mEventData )
1571   {
1572     // Nothing to do if there is no text.
1573     return;
1574   }
1575
1576   if( mEventData->mSelectionEnabled )
1577   {
1578     // Convert from control's coords to text's coords.
1579     const float xPosition = event.p2.mFloat - mScrollPosition.x;
1580     const float yPosition = event.p3.mFloat - mScrollPosition.y;
1581
1582     // Calculates the logical position from the x,y coords.
1583     RepositionSelectionHandles( xPosition,
1584                                 yPosition );
1585   }
1586 }
1587
1588 void Controller::Impl::OnSelectAllEvent()
1589 {
1590   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1591
1592   if( NULL == mEventData )
1593   {
1594     // Nothing to do if there is no text.
1595     return;
1596   }
1597
1598   if( mEventData->mSelectionEnabled )
1599   {
1600     ChangeState( EventData::SELECTING );
1601
1602     mEventData->mLeftSelectionPosition = 0u;
1603     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1604
1605     mEventData->mScrollAfterUpdatePosition = true;
1606     mEventData->mUpdateLeftSelectionPosition = true;
1607     mEventData->mUpdateRightSelectionPosition = true;
1608     mEventData->mUpdateHighlightBox = true;
1609   }
1610 }
1611
1612 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1613 {
1614   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1615   {
1616     // Nothing to select if handles are in the same place.
1617     selectedText.clear();
1618     return;
1619   }
1620
1621   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1622
1623   //Get start and end position of selection
1624   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1625   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1626
1627   Vector<Character>& utf32Characters = mLogicalModel->mText;
1628   const Length numberOfCharacters = utf32Characters.Count();
1629
1630   // Validate the start and end selection points
1631   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1632   {
1633     //Get text as a UTF8 string
1634     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1635
1636     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1637     {
1638       // Set as input style the style of the first deleted character.
1639       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1640
1641       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1642
1643       // Mark the paragraphs to be updated.
1644       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1645       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1646
1647       // Delete text between handles
1648       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1649       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1650       utf32Characters.Erase( first, last );
1651
1652       // Will show the cursor at the first character of the selection.
1653       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1654     }
1655     else
1656     {
1657       // Will show the cursor at the last character of the selection.
1658       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1659     }
1660
1661     mEventData->mDecoratorUpdated = true;
1662   }
1663 }
1664
1665 void Controller::Impl::ShowClipboard()
1666 {
1667   if( mClipboard )
1668   {
1669     mClipboard.ShowClipboard();
1670   }
1671 }
1672
1673 void Controller::Impl::HideClipboard()
1674 {
1675   if( mClipboard && mClipboardHideEnabled )
1676   {
1677     mClipboard.HideClipboard();
1678   }
1679 }
1680
1681 void Controller::Impl::SetClipboardHideEnable(bool enable)
1682 {
1683   mClipboardHideEnabled = enable;
1684 }
1685
1686 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1687 {
1688   //Send string to clipboard
1689   return ( mClipboard && mClipboard.SetItem( source ) );
1690 }
1691
1692 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1693 {
1694   std::string selectedText;
1695   RetrieveSelection( selectedText, deleteAfterSending );
1696   CopyStringToClipboard( selectedText );
1697   ChangeState( EventData::EDITING );
1698 }
1699
1700 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1701 {
1702   if ( mClipboard )
1703   {
1704     retrievedString =  mClipboard.GetItem( itemIndex );
1705   }
1706 }
1707
1708 void Controller::Impl::RepositionSelectionHandles()
1709 {
1710   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1711   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1712
1713   if( selectionStart == selectionEnd )
1714   {
1715     // Nothing to select if handles are in the same place.
1716     return;
1717   }
1718
1719   mEventData->mDecorator->ClearHighlights();
1720
1721   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1722   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1723   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1724   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1725   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1726   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1727   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1728
1729   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1730   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1731   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1732
1733   // Swap the indices if the start is greater than the end.
1734   const bool indicesSwapped = selectionStart > selectionEnd;
1735
1736   // Tell the decorator to flip the selection handles if needed.
1737   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1738
1739   if( indicesSwapped )
1740   {
1741     std::swap( selectionStart, selectionEnd );
1742   }
1743
1744   // Get the indices to the first and last selected glyphs.
1745   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1746   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1747   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1748   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1749
1750   // Get the lines where the glyphs are laid-out.
1751   const LineRun* lineRun = mVisualModel->mLines.Begin();
1752
1753   LineIndex lineIndex = 0u;
1754   Length numberOfLines = 0u;
1755   mVisualModel->GetNumberOfLines( glyphStart,
1756                                   1u + glyphEnd - glyphStart,
1757                                   lineIndex,
1758                                   numberOfLines );
1759   const LineIndex firstLineIndex = lineIndex;
1760
1761   // Create the structure to store some selection box info.
1762   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1763   selectionBoxLinesInfo.Resize( numberOfLines );
1764
1765   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1766   selectionBoxInfo->minX = MAX_FLOAT;
1767   selectionBoxInfo->maxX = MIN_FLOAT;
1768
1769   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1770   float minHighlightX = std::numeric_limits<float>::max();
1771   float maxHighlightX = std::numeric_limits<float>::min();
1772   Size highLightSize;
1773   Vector2 highLightPosition; // The highlight position in decorator's coords.
1774
1775   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1776
1777   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1778   selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1779                                                       firstLineIndex );
1780
1781   // Transform to decorator's (control) coords.
1782   selectionBoxInfo->lineOffset += mScrollPosition.y;
1783
1784   lineRun += firstLineIndex;
1785
1786   // The line height is the addition of the line ascender and the line descender.
1787   // However, the line descender has a negative value, hence the subtraction.
1788   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1789
1790   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1791
1792   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1793   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1794   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1795
1796   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1797   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1798   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1799
1800   // The number of quads of the selection box.
1801   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1802   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1803
1804   // Count the actual number of quads.
1805   unsigned int actualNumberOfQuads = 0u;
1806   Vector4 quad;
1807
1808   // Traverse the glyphs.
1809   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1810   {
1811     const GlyphInfo& glyph = *( glyphsBuffer + index );
1812     const Vector2& position = *( positionsBuffer + index );
1813
1814     if( splitStartGlyph )
1815     {
1816       // 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.
1817
1818       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1819       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1820       // Get the direction of the character.
1821       CharacterDirection isCurrentRightToLeft = false;
1822       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1823       {
1824         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1825       }
1826
1827       // The end point could be in the middle of the ligature.
1828       // Calculate the number of characters selected.
1829       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1830
1831       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1832       quad.y = selectionBoxInfo->lineOffset;
1833       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1834       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1835
1836       // Store the min and max 'x' for each line.
1837       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1838       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1839
1840       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1841       ++actualNumberOfQuads;
1842
1843       splitStartGlyph = false;
1844       continue;
1845     }
1846
1847     if( splitEndGlyph && ( index == glyphEnd ) )
1848     {
1849       // 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.
1850
1851       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1852       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1853       // Get the direction of the character.
1854       CharacterDirection isCurrentRightToLeft = false;
1855       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1856       {
1857         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1858       }
1859
1860       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1861
1862       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1863       quad.y = selectionBoxInfo->lineOffset;
1864       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1865       quad.w = quad.y + selectionBoxInfo->lineHeight;
1866
1867       // Store the min and max 'x' for each line.
1868       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1869       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1870
1871       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1872                                             quad );
1873       ++actualNumberOfQuads;
1874
1875       splitEndGlyph = false;
1876       continue;
1877     }
1878
1879     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1880     quad.y = selectionBoxInfo->lineOffset;
1881     quad.z = quad.x + glyph.advance;
1882     quad.w = quad.y + selectionBoxInfo->lineHeight;
1883
1884     // Store the min and max 'x' for each line.
1885     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1886     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1887
1888     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1889                                           quad );
1890     ++actualNumberOfQuads;
1891
1892     // Whether to retrieve the next line.
1893     if( index == lastGlyphOfLine )
1894     {
1895       // Retrieve the next line.
1896       ++lineRun;
1897
1898       // Get the last glyph of the new line.
1899       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1900
1901       ++lineIndex;
1902       if( lineIndex < firstLineIndex + numberOfLines )
1903       {
1904         // Keep the offset and height of the current selection box.
1905         const float currentLineOffset = selectionBoxInfo->lineOffset;
1906         const float currentLineHeight = selectionBoxInfo->lineHeight;
1907
1908         // Get the selection box info for the next line.
1909         ++selectionBoxInfo;
1910
1911         selectionBoxInfo->minX = MAX_FLOAT;
1912         selectionBoxInfo->maxX = MIN_FLOAT;
1913
1914         // Update the line's vertical offset.
1915         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1916
1917         // The line height is the addition of the line ascender and the line descender.
1918         // However, the line descender has a negative value, hence the subtraction.
1919         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1920       }
1921     }
1922   }
1923
1924   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1925   // The final width is calculated after 'boxifying' the selection.
1926   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1927          endIt = selectionBoxLinesInfo.End();
1928        it != endIt;
1929        ++it )
1930   {
1931     const SelectionBoxInfo& info = *it;
1932
1933     // Update the size of the highlighted text.
1934     highLightSize.height += info.lineHeight;
1935     minHighlightX = std::min( minHighlightX, info.minX );
1936     maxHighlightX = std::max( maxHighlightX, info.maxX );
1937   }
1938
1939   // Add extra geometry to 'boxify' the selection.
1940
1941   if( 1u < numberOfLines )
1942   {
1943     // Boxify the first line.
1944     lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1945     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1946
1947     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1948     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1949
1950     if( boxifyBegin )
1951     {
1952       quad.x = 0.f;
1953       quad.y = firstSelectionBoxLineInfo.lineOffset;
1954       quad.z = firstSelectionBoxLineInfo.minX;
1955       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1956
1957       // Boxify at the beginning of the line.
1958       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1959                                             quad );
1960       ++actualNumberOfQuads;
1961
1962       // Update the size of the highlighted text.
1963       minHighlightX = 0.f;
1964     }
1965
1966     if( boxifyEnd )
1967     {
1968       quad.x = firstSelectionBoxLineInfo.maxX;
1969       quad.y = firstSelectionBoxLineInfo.lineOffset;
1970       quad.z = mVisualModel->mControlSize.width;
1971       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1972
1973       // Boxify at the end of the line.
1974       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1975                                             quad );
1976       ++actualNumberOfQuads;
1977
1978       // Update the size of the highlighted text.
1979       maxHighlightX = mVisualModel->mControlSize.width;
1980     }
1981
1982     // Boxify the central lines.
1983     if( 2u < numberOfLines )
1984     {
1985       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1986              endIt = selectionBoxLinesInfo.End() - 1u;
1987            it != endIt;
1988            ++it )
1989       {
1990         const SelectionBoxInfo& info = *it;
1991
1992         quad.x = 0.f;
1993         quad.y = info.lineOffset;
1994         quad.z = info.minX;
1995         quad.w = info.lineOffset + info.lineHeight;
1996
1997         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1998                                               quad );
1999         ++actualNumberOfQuads;
2000
2001         quad.x = info.maxX;
2002         quad.y = info.lineOffset;
2003         quad.z = mVisualModel->mControlSize.width;
2004         quad.w = info.lineOffset + info.lineHeight;
2005
2006         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2007                                               quad );
2008         ++actualNumberOfQuads;
2009       }
2010
2011       // Update the size of the highlighted text.
2012       minHighlightX = 0.f;
2013       maxHighlightX = mVisualModel->mControlSize.width;
2014     }
2015
2016     // Boxify the last line.
2017     lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2018     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2019
2020     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2021     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2022
2023     if( boxifyBegin )
2024     {
2025       quad.x = 0.f;
2026       quad.y = lastSelectionBoxLineInfo.lineOffset;
2027       quad.z = lastSelectionBoxLineInfo.minX;
2028       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2029
2030       // Boxify at the beginning of the line.
2031       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2032                                             quad );
2033       ++actualNumberOfQuads;
2034
2035       // Update the size of the highlighted text.
2036       minHighlightX = 0.f;
2037     }
2038
2039     if( boxifyEnd )
2040     {
2041       quad.x = lastSelectionBoxLineInfo.maxX;
2042       quad.y = lastSelectionBoxLineInfo.lineOffset;
2043       quad.z = mVisualModel->mControlSize.width;
2044       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2045
2046       // Boxify at the end of the line.
2047       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2048                                             quad );
2049       ++actualNumberOfQuads;
2050
2051       // Update the size of the highlighted text.
2052       maxHighlightX = mVisualModel->mControlSize.width;
2053     }
2054   }
2055
2056   // Set the actual number of quads.
2057   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2058
2059   // Sets the highlight's size and position. In decorator's coords.
2060   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2061   highLightSize.width = maxHighlightX - minHighlightX;
2062
2063   highLightPosition.x = minHighlightX;
2064   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2065   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2066
2067   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2068
2069   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2070   {
2071     CursorInfo primaryCursorInfo;
2072     GetCursorPosition( mEventData->mLeftSelectionPosition,
2073                        primaryCursorInfo );
2074
2075     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2076
2077     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2078                                          primaryPosition.x,
2079                                          primaryCursorInfo.lineOffset + mScrollPosition.y,
2080                                          primaryCursorInfo.lineHeight );
2081
2082     CursorInfo secondaryCursorInfo;
2083     GetCursorPosition( mEventData->mRightSelectionPosition,
2084                        secondaryCursorInfo );
2085
2086     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2087
2088     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2089                                          secondaryPosition.x,
2090                                          secondaryCursorInfo.lineOffset + mScrollPosition.y,
2091                                          secondaryCursorInfo.lineHeight );
2092   }
2093
2094   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2095   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2096
2097   // Set the flag to update the decorator.
2098   mEventData->mDecoratorUpdated = true;
2099 }
2100
2101 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2102 {
2103   if( NULL == mEventData )
2104   {
2105     // Nothing to do if there is no text input.
2106     return;
2107   }
2108
2109   if( IsShowingPlaceholderText() )
2110   {
2111     // Nothing to do if there is the place-holder text.
2112     return;
2113   }
2114
2115   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2116   const Length numberOfLines  = mVisualModel->mLines.Count();
2117   if( ( 0 == numberOfGlyphs ) ||
2118       ( 0 == numberOfLines ) )
2119   {
2120     // Nothing to do if there is no text.
2121     return;
2122   }
2123
2124   // Find which word was selected
2125   CharacterIndex selectionStart( 0 );
2126   CharacterIndex selectionEnd( 0 );
2127   const bool indicesFound = FindSelectionIndices( mVisualModel,
2128                                                   mLogicalModel,
2129                                                   mMetrics,
2130                                                   visualX,
2131                                                   visualY,
2132                                                   selectionStart,
2133                                                   selectionEnd );
2134   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2135
2136   if( indicesFound )
2137   {
2138     ChangeState( EventData::SELECTING );
2139
2140     mEventData->mLeftSelectionPosition = selectionStart;
2141     mEventData->mRightSelectionPosition = selectionEnd;
2142
2143     mEventData->mUpdateLeftSelectionPosition = true;
2144     mEventData->mUpdateRightSelectionPosition = true;
2145     mEventData->mUpdateHighlightBox = true;
2146
2147     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2148   }
2149   else
2150   {
2151     // Nothing to select. i.e. a white space, out of bounds
2152     ChangeState( EventData::EDITING );
2153
2154     mEventData->mPrimaryCursorPosition = selectionEnd;
2155
2156     mEventData->mUpdateCursorPosition = true;
2157     mEventData->mUpdateGrabHandlePosition = true;
2158     mEventData->mScrollAfterUpdatePosition = true;
2159     mEventData->mUpdateInputStyle = true;
2160   }
2161 }
2162
2163 void Controller::Impl::SetPopupButtons()
2164 {
2165   /**
2166    *  Sets the Popup buttons to be shown depending on State.
2167    *
2168    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2169    *
2170    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2171    */
2172
2173   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2174
2175   if( EventData::SELECTING == mEventData->mState )
2176   {
2177     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2178
2179     if( !IsClipboardEmpty() )
2180     {
2181       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2182       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2183     }
2184
2185     if( !mEventData->mAllTextSelected )
2186     {
2187       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2188     }
2189   }
2190   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2191   {
2192     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2193     {
2194       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2195     }
2196
2197     if( !IsClipboardEmpty() )
2198     {
2199       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2200       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2201     }
2202   }
2203   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2204   {
2205     if ( !IsClipboardEmpty() )
2206     {
2207       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2208       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2209     }
2210   }
2211
2212   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2213 }
2214
2215 void Controller::Impl::ChangeState( EventData::State newState )
2216 {
2217   if( NULL == mEventData )
2218   {
2219     // Nothing to do if there is no text input.
2220     return;
2221   }
2222
2223   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2224
2225   if( mEventData->mState != newState )
2226   {
2227     mEventData->mPreviousState = mEventData->mState;
2228     mEventData->mState = newState;
2229
2230     switch( mEventData->mState )
2231     {
2232       case EventData::INACTIVE:
2233       {
2234         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2235         mEventData->mDecorator->StopCursorBlink();
2236         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2237         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2238         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2239         mEventData->mDecorator->SetHighlightActive( false );
2240         mEventData->mDecorator->SetPopupActive( false );
2241         mEventData->mDecoratorUpdated = true;
2242         HideClipboard();
2243         break;
2244       }
2245       case EventData::INTERRUPTED:
2246       {
2247         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2248         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2249         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2250         mEventData->mDecorator->SetHighlightActive( false );
2251         mEventData->mDecorator->SetPopupActive( false );
2252         mEventData->mDecoratorUpdated = true;
2253         HideClipboard();
2254         break;
2255       }
2256       case EventData::SELECTING:
2257       {
2258         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2259         mEventData->mDecorator->StopCursorBlink();
2260         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2261         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2262         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2263         mEventData->mDecorator->SetHighlightActive( true );
2264         if( mEventData->mGrabHandlePopupEnabled )
2265         {
2266           SetPopupButtons();
2267           mEventData->mDecorator->SetPopupActive( true );
2268         }
2269         mEventData->mDecoratorUpdated = true;
2270         break;
2271       }
2272       case EventData::EDITING:
2273       {
2274         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2275         if( mEventData->mCursorBlinkEnabled )
2276         {
2277           mEventData->mDecorator->StartCursorBlink();
2278         }
2279         // Grab handle is not shown until a tap is received whilst EDITING
2280         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2281         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2282         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2283         mEventData->mDecorator->SetHighlightActive( false );
2284         if( mEventData->mGrabHandlePopupEnabled )
2285         {
2286           mEventData->mDecorator->SetPopupActive( false );
2287         }
2288         mEventData->mDecoratorUpdated = true;
2289         HideClipboard();
2290         break;
2291       }
2292       case EventData::EDITING_WITH_POPUP:
2293       {
2294         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2295
2296         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2297         if( mEventData->mCursorBlinkEnabled )
2298         {
2299           mEventData->mDecorator->StartCursorBlink();
2300         }
2301         if( mEventData->mSelectionEnabled )
2302         {
2303           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2304           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2305           mEventData->mDecorator->SetHighlightActive( false );
2306         }
2307         else
2308         {
2309           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2310         }
2311         if( mEventData->mGrabHandlePopupEnabled )
2312         {
2313           SetPopupButtons();
2314           mEventData->mDecorator->SetPopupActive( true );
2315         }
2316         HideClipboard();
2317         mEventData->mDecoratorUpdated = true;
2318         break;
2319       }
2320       case EventData::EDITING_WITH_GRAB_HANDLE:
2321       {
2322         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2323
2324         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2325         if( mEventData->mCursorBlinkEnabled )
2326         {
2327           mEventData->mDecorator->StartCursorBlink();
2328         }
2329         // Grab handle is not shown until a tap is received whilst EDITING
2330         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2331         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2332         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2333         mEventData->mDecorator->SetHighlightActive( false );
2334         if( mEventData->mGrabHandlePopupEnabled )
2335         {
2336           mEventData->mDecorator->SetPopupActive( false );
2337         }
2338         mEventData->mDecoratorUpdated = true;
2339         HideClipboard();
2340         break;
2341       }
2342       case EventData::SELECTION_HANDLE_PANNING:
2343       {
2344         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2345         mEventData->mDecorator->StopCursorBlink();
2346         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2347         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2348         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2349         mEventData->mDecorator->SetHighlightActive( true );
2350         if( mEventData->mGrabHandlePopupEnabled )
2351         {
2352           mEventData->mDecorator->SetPopupActive( false );
2353         }
2354         mEventData->mDecoratorUpdated = true;
2355         break;
2356       }
2357       case EventData::GRAB_HANDLE_PANNING:
2358       {
2359         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2360
2361         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2362         if( mEventData->mCursorBlinkEnabled )
2363         {
2364           mEventData->mDecorator->StartCursorBlink();
2365         }
2366         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2367         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2368         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2369         mEventData->mDecorator->SetHighlightActive( false );
2370         if( mEventData->mGrabHandlePopupEnabled )
2371         {
2372           mEventData->mDecorator->SetPopupActive( false );
2373         }
2374         mEventData->mDecoratorUpdated = true;
2375         break;
2376       }
2377       case EventData::EDITING_WITH_PASTE_POPUP:
2378       {
2379         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2380
2381         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2382         if( mEventData->mCursorBlinkEnabled )
2383         {
2384           mEventData->mDecorator->StartCursorBlink();
2385         }
2386
2387         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2388         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2389         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2390         mEventData->mDecorator->SetHighlightActive( false );
2391
2392         if( mEventData->mGrabHandlePopupEnabled )
2393         {
2394           SetPopupButtons();
2395           mEventData->mDecorator->SetPopupActive( true );
2396         }
2397         HideClipboard();
2398         mEventData->mDecoratorUpdated = true;
2399         break;
2400       }
2401       case EventData::TEXT_PANNING:
2402       {
2403         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2404         mEventData->mDecorator->StopCursorBlink();
2405         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2406         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2407             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2408         {
2409           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2410           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2411           mEventData->mDecorator->SetHighlightActive( true );
2412         }
2413
2414         if( mEventData->mGrabHandlePopupEnabled )
2415         {
2416           mEventData->mDecorator->SetPopupActive( false );
2417         }
2418
2419         mEventData->mDecoratorUpdated = true;
2420         break;
2421       }
2422     }
2423   }
2424 }
2425
2426 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2427                                           CursorInfo& cursorInfo )
2428 {
2429   if( !IsShowingRealText() )
2430   {
2431     // Do not want to use the place-holder text to set the cursor position.
2432
2433     // Use the line's height of the font's family set to set the cursor's size.
2434     // If there is no font's family set, use the default font.
2435     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2436
2437     cursorInfo.lineOffset = 0.f;
2438     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2439     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2440
2441     switch( mLayoutEngine.GetHorizontalAlignment() )
2442     {
2443       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2444       {
2445         cursorInfo.primaryPosition.x = 0.f;
2446         break;
2447       }
2448       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2449       {
2450         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2451         break;
2452       }
2453       case LayoutEngine::HORIZONTAL_ALIGN_END:
2454       {
2455         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2456         break;
2457       }
2458     }
2459
2460     // Nothing else to do.
2461     return;
2462   }
2463
2464   Text::GetCursorPosition( mVisualModel,
2465                            mLogicalModel,
2466                            mMetrics,
2467                            logical,
2468                            cursorInfo );
2469
2470   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2471   {
2472     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2473
2474     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2475     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2476
2477     if( 0.f > cursorInfo.primaryPosition.x )
2478     {
2479       cursorInfo.primaryPosition.x = 0.f;
2480     }
2481
2482     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2483     if( cursorInfo.primaryPosition.x > edgeWidth )
2484     {
2485       cursorInfo.primaryPosition.x = edgeWidth;
2486     }
2487   }
2488 }
2489
2490 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2491 {
2492   if( NULL == mEventData )
2493   {
2494     // Nothing to do if there is no text input.
2495     return 0u;
2496   }
2497
2498   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2499
2500   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2501   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2502
2503   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2504   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2505
2506   if( numberOfCharacters > 1u )
2507   {
2508     const Script script = mLogicalModel->GetScript( index );
2509     if( HasLigatureMustBreak( script ) )
2510     {
2511       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
2512       numberOfCharacters = 1u;
2513     }
2514   }
2515   else
2516   {
2517     while( 0u == numberOfCharacters )
2518     {
2519       ++glyphIndex;
2520       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2521     }
2522   }
2523
2524   if( index < mEventData->mPrimaryCursorPosition )
2525   {
2526     cursorIndex -= numberOfCharacters;
2527   }
2528   else
2529   {
2530     cursorIndex += numberOfCharacters;
2531   }
2532
2533   // Will update the cursor hook position.
2534   mEventData->mUpdateCursorHookPosition = true;
2535
2536   return cursorIndex;
2537 }
2538
2539 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2540 {
2541   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2542   if( NULL == mEventData )
2543   {
2544     // Nothing to do if there is no text input.
2545     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2546     return;
2547   }
2548
2549   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2550
2551   // Sets the cursor position.
2552   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2553                                        cursorPosition.x,
2554                                        cursorPosition.y,
2555                                        cursorInfo.primaryCursorHeight,
2556                                        cursorInfo.lineHeight );
2557   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2558
2559   if( mEventData->mUpdateGrabHandlePosition )
2560   {
2561     // Sets the grab handle position.
2562     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2563                                          cursorPosition.x,
2564                                          cursorInfo.lineOffset + mScrollPosition.y,
2565                                          cursorInfo.lineHeight );
2566   }
2567
2568   if( cursorInfo.isSecondaryCursor )
2569   {
2570     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2571                                          cursorInfo.secondaryPosition.x + mScrollPosition.x,
2572                                          cursorInfo.secondaryPosition.y + mScrollPosition.y,
2573                                          cursorInfo.secondaryCursorHeight,
2574                                          cursorInfo.lineHeight );
2575     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2576   }
2577
2578   // Set which cursors are active according the state.
2579   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2580   {
2581     if( cursorInfo.isSecondaryCursor )
2582     {
2583       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2584     }
2585     else
2586     {
2587       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2588     }
2589   }
2590   else
2591   {
2592     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2593   }
2594
2595   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2596 }
2597
2598 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2599                                               const CursorInfo& cursorInfo )
2600 {
2601   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2602       ( RIGHT_SELECTION_HANDLE != handleType ) )
2603   {
2604     return;
2605   }
2606
2607   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2608
2609   // Sets the handle's position.
2610   mEventData->mDecorator->SetPosition( handleType,
2611                                        cursorPosition.x,
2612                                        cursorInfo.lineOffset + mScrollPosition.y,
2613                                        cursorInfo.lineHeight );
2614
2615   // If selection handle at start of the text and other at end of the text then all text is selected.
2616   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2617   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2618   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2619 }
2620
2621 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2622 {
2623   // Clamp between -space & 0.
2624
2625   if( layoutSize.width > mVisualModel->mControlSize.width )
2626   {
2627     const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2628     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2629     mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2630
2631     mEventData->mDecoratorUpdated = true;
2632   }
2633   else
2634   {
2635     mScrollPosition.x = 0.f;
2636   }
2637 }
2638
2639 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2640 {
2641   // Clamp between -space & 0.
2642   if( layoutSize.height > mVisualModel->mControlSize.height )
2643   {
2644     const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2645     mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2646     mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2647
2648     mEventData->mDecoratorUpdated = true;
2649   }
2650   else
2651   {
2652     mScrollPosition.y = 0.f;
2653   }
2654 }
2655
2656 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2657 {
2658   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2659
2660   // position is in actor's coords.
2661   const float positionEndX = position.x + cursorWidth;
2662   const float positionEndY = position.y + lineHeight;
2663
2664   // Transform the position to decorator coords.
2665   const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2666   const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2667
2668   const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2669   const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2670
2671   if( decoratorPositionBeginX < 0.f )
2672   {
2673     mScrollPosition.x = -position.x;
2674   }
2675   else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2676   {
2677     mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2678   }
2679
2680   if( decoratorPositionBeginY < 0.f )
2681   {
2682     mScrollPosition.y = -position.y;
2683   }
2684   else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2685   {
2686     mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2687   }
2688 }
2689
2690 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2691 {
2692   // Get the current cursor position in decorator coords.
2693   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2694
2695   // Calculate the offset to match the cursor position before the character was deleted.
2696   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2697   mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2698
2699   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2700   ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2701
2702   // Makes the new cursor position visible if needed.
2703   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2704 }
2705
2706 void Controller::Impl::RequestRelayout()
2707 {
2708   mControlInterface.RequestTextRelayout();
2709 }
2710
2711 } // namespace Text
2712
2713 } // namespace Toolkit
2714
2715 } // namespace Dali