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