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