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