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