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