6cf26fc5ad930cd6951352070160b87cb3d862a9
[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 }
1549
1550 void Controller::Impl::OnSelectAllEvent()
1551 {
1552   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1553
1554   if( NULL == mEventData )
1555   {
1556     // Nothing to do if there is no text.
1557     return;
1558   }
1559
1560   if( mEventData->mSelectionEnabled )
1561   {
1562     ChangeState( EventData::SELECTING );
1563
1564     mEventData->mLeftSelectionPosition = 0u;
1565     mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1566
1567     mEventData->mScrollAfterUpdatePosition = true;
1568     mEventData->mUpdateLeftSelectionPosition = true;
1569     mEventData->mUpdateRightSelectionPosition = true;
1570     mEventData->mUpdateHighlightBox = true;
1571   }
1572 }
1573
1574 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1575 {
1576   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1577   {
1578     // Nothing to select if handles are in the same place.
1579     selectedText.clear();
1580     return;
1581   }
1582
1583   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1584
1585   //Get start and end position of selection
1586   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1587   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1588
1589   Vector<Character>& utf32Characters = mLogicalModel->mText;
1590   const Length numberOfCharacters = utf32Characters.Count();
1591
1592   // Validate the start and end selection points
1593   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1594   {
1595     //Get text as a UTF8 string
1596     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1597
1598     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1599     {
1600       // Set as input style the style of the first deleted character.
1601       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1602
1603       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1604
1605       // Mark the paragraphs to be updated.
1606       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1607       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1608
1609       // Delete text between handles
1610       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1611       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1612       utf32Characters.Erase( first, last );
1613
1614       // Will show the cursor at the first character of the selection.
1615       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1616     }
1617     else
1618     {
1619       // Will show the cursor at the last character of the selection.
1620       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1621     }
1622
1623     mEventData->mDecoratorUpdated = true;
1624   }
1625 }
1626
1627 void Controller::Impl::ShowClipboard()
1628 {
1629   if( mClipboard )
1630   {
1631     mClipboard.ShowClipboard();
1632   }
1633 }
1634
1635 void Controller::Impl::HideClipboard()
1636 {
1637   if( mClipboard && mClipboardHideEnabled )
1638   {
1639     mClipboard.HideClipboard();
1640   }
1641 }
1642
1643 void Controller::Impl::SetClipboardHideEnable(bool enable)
1644 {
1645   mClipboardHideEnabled = enable;
1646 }
1647
1648 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1649 {
1650   //Send string to clipboard
1651   return ( mClipboard && mClipboard.SetItem( source ) );
1652 }
1653
1654 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1655 {
1656   std::string selectedText;
1657   RetrieveSelection( selectedText, deleteAfterSending );
1658   CopyStringToClipboard( selectedText );
1659   ChangeState( EventData::EDITING );
1660 }
1661
1662 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1663 {
1664   if ( mClipboard )
1665   {
1666     retrievedString =  mClipboard.GetItem( itemIndex );
1667   }
1668 }
1669
1670 void Controller::Impl::RepositionSelectionHandles()
1671 {
1672   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1673   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1674
1675   if( selectionStart == selectionEnd )
1676   {
1677     // Nothing to select if handles are in the same place.
1678     return;
1679   }
1680
1681   mEventData->mDecorator->ClearHighlights();
1682
1683   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1684   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1685   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1686   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1687   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1688   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1689   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1690
1691   const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1692   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1693   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1694
1695   // Swap the indices if the start is greater than the end.
1696   const bool indicesSwapped = selectionStart > selectionEnd;
1697
1698   // Tell the decorator to flip the selection handles if needed.
1699   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1700
1701   if( indicesSwapped )
1702   {
1703     std::swap( selectionStart, selectionEnd );
1704   }
1705
1706   // Get the indices to the first and last selected glyphs.
1707   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1708   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1709   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1710   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1711
1712   // Get the lines where the glyphs are laid-out.
1713   const LineRun* lineRun = mVisualModel->mLines.Begin();
1714
1715   LineIndex lineIndex = 0u;
1716   Length numberOfLines = 0u;
1717   mVisualModel->GetNumberOfLines( glyphStart,
1718                                   1u + glyphEnd - glyphStart,
1719                                   lineIndex,
1720                                   numberOfLines );
1721   const LineIndex firstLineIndex = lineIndex;
1722
1723   // Create the structure to store some selection box info.
1724   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1725   selectionBoxLinesInfo.Resize( numberOfLines );
1726
1727   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1728   selectionBoxInfo->minX = MAX_FLOAT;
1729   selectionBoxInfo->maxX = MIN_FLOAT;
1730
1731   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1732   float minHighlightX = std::numeric_limits<float>::max();
1733   float maxHighlightX = std::numeric_limits<float>::min();
1734   Size highLightSize;
1735   Vector2 highLightPosition; // The highlight position in decorator's coords.
1736
1737   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1738
1739   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1740   selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1741                                                       firstLineIndex );
1742
1743   // Transform to decorator's (control) coords.
1744   selectionBoxInfo->lineOffset += mScrollPosition.y;
1745
1746   lineRun += firstLineIndex;
1747
1748   // The line height is the addition of the line ascender and the line descender.
1749   // However, the line descender has a negative value, hence the subtraction.
1750   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1751
1752   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1753
1754   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1755   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1756   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1757
1758   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1759   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1760   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1761
1762   // Traverse the glyphs.
1763   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1764   {
1765     const GlyphInfo& glyph = *( glyphsBuffer + index );
1766     const Vector2& position = *( positionsBuffer + index );
1767
1768     if( splitStartGlyph )
1769     {
1770       // 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.
1771
1772       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1773       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1774       // Get the direction of the character.
1775       CharacterDirection isCurrentRightToLeft = false;
1776       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1777       {
1778         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1779       }
1780
1781       // The end point could be in the middle of the ligature.
1782       // Calculate the number of characters selected.
1783       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1784
1785       const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1786       const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1787       const float yPosition = selectionBoxInfo->lineOffset;
1788
1789       // Store the min and max 'x' for each line.
1790       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1791       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1792
1793       mEventData->mDecorator->AddHighlight( xPosition,
1794                                             yPosition,
1795                                             xPositionAdvance,
1796                                             yPosition + selectionBoxInfo->lineHeight );
1797
1798       splitStartGlyph = false;
1799       continue;
1800     }
1801
1802     if( splitEndGlyph && ( index == glyphEnd ) )
1803     {
1804       // 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.
1805
1806       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1807       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1808       // Get the direction of the character.
1809       CharacterDirection isCurrentRightToLeft = false;
1810       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1811       {
1812         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1813       }
1814
1815       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1816
1817       const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1818       const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1819       const float yPosition = selectionBoxInfo->lineOffset;
1820
1821       // Store the min and max 'x' for each line.
1822       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1823       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1824
1825       mEventData->mDecorator->AddHighlight( xPosition,
1826                                             yPosition,
1827                                             xPositionAdvance,
1828                                             yPosition + selectionBoxInfo->lineHeight );
1829
1830       splitEndGlyph = false;
1831       continue;
1832     }
1833
1834     const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1835     const float xPositionAdvance = xPosition + glyph.advance;
1836     const float yPosition = selectionBoxInfo->lineOffset;
1837
1838     // Store the min and max 'x' for each line.
1839     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1840     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1841
1842     mEventData->mDecorator->AddHighlight( xPosition,
1843                                           yPosition,
1844                                           xPositionAdvance,
1845                                           yPosition + selectionBoxInfo->lineHeight );
1846
1847     // Whether to retrieve the next line.
1848     if( index == lastGlyphOfLine )
1849     {
1850       // Retrieve the next line.
1851       ++lineRun;
1852
1853       // Get the last glyph of the new line.
1854       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1855
1856       ++lineIndex;
1857       if( lineIndex < firstLineIndex + numberOfLines )
1858       {
1859         // Keep the offset and height of the current selection box.
1860         const float currentLineOffset = selectionBoxInfo->lineOffset;
1861         const float currentLineHeight = selectionBoxInfo->lineHeight;
1862
1863         // Get the selection box info for the next line.
1864         ++selectionBoxInfo;
1865
1866         selectionBoxInfo->minX = MAX_FLOAT;
1867         selectionBoxInfo->maxX = MIN_FLOAT;
1868
1869         // Update the line's vertical offset.
1870         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1871
1872         // The line height is the addition of the line ascender and the line descender.
1873         // However, the line descender has a negative value, hence the subtraction.
1874         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1875       }
1876     }
1877   }
1878
1879   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1880   // The final width is calculated after 'boxifying' the selection.
1881   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1882          endIt = selectionBoxLinesInfo.End();
1883        it != endIt;
1884        ++it )
1885   {
1886     const SelectionBoxInfo& info = *it;
1887
1888     // Update the size of the highlighted text.
1889     highLightSize.height += selectionBoxInfo->lineHeight;
1890     minHighlightX = std::min( minHighlightX, info.minX );
1891     maxHighlightX = std::max( maxHighlightX, info.maxX );
1892   }
1893
1894   // Add extra geometry to 'boxify' the selection.
1895
1896   if( 1u < numberOfLines )
1897   {
1898     // Boxify the first line.
1899     lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1900     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1901
1902     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1903     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1904
1905     if( boxifyBegin )
1906     {
1907       // Boxify at the beginning of the line.
1908       mEventData->mDecorator->AddHighlight( 0.f,
1909                                             firstSelectionBoxLineInfo.lineOffset,
1910                                             firstSelectionBoxLineInfo.minX,
1911                                             firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1912
1913       // Update the size of the highlighted text.
1914       minHighlightX = 0.f;
1915     }
1916
1917     if( boxifyEnd )
1918     {
1919       // Boxify at the end of the line.
1920       mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1921                                             firstSelectionBoxLineInfo.lineOffset,
1922                                             mVisualModel->mControlSize.width,
1923                                             firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1924
1925       // Update the size of the highlighted text.
1926       maxHighlightX = mVisualModel->mControlSize.width;
1927     }
1928
1929     // Boxify the central lines.
1930     if( 2u < numberOfLines )
1931     {
1932       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1933              endIt = selectionBoxLinesInfo.End() - 1u;
1934            it != endIt;
1935            ++it )
1936       {
1937         const SelectionBoxInfo& info = *it;
1938
1939         mEventData->mDecorator->AddHighlight( 0.f,
1940                                               info.lineOffset,
1941                                               info.minX,
1942                                               info.lineOffset + info.lineHeight );
1943
1944         mEventData->mDecorator->AddHighlight( info.maxX,
1945                                               info.lineOffset,
1946                                               mVisualModel->mControlSize.width,
1947                                               info.lineOffset + info.lineHeight );
1948       }
1949
1950       // Update the size of the highlighted text.
1951       minHighlightX = 0.f;
1952       maxHighlightX = mVisualModel->mControlSize.width;
1953     }
1954
1955     // Boxify the last line.
1956     lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1957     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1958
1959     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1960     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1961
1962     if( boxifyBegin )
1963     {
1964       // Boxify at the beginning of the line.
1965       mEventData->mDecorator->AddHighlight( 0.f,
1966                                             lastSelectionBoxLineInfo.lineOffset,
1967                                             lastSelectionBoxLineInfo.minX,
1968                                             lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1969
1970       // Update the size of the highlighted text.
1971       minHighlightX = 0.f;
1972     }
1973
1974     if( boxifyEnd )
1975     {
1976       // Boxify at the end of the line.
1977       mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1978                                             lastSelectionBoxLineInfo.lineOffset,
1979                                             mVisualModel->mControlSize.width,
1980                                             lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1981
1982       // Update the size of the highlighted text.
1983       maxHighlightX = mVisualModel->mControlSize.width;
1984     }
1985   }
1986
1987   // Sets the highlight's size and position. In decorator's coords.
1988   // The highlight's height has been calculated above (before 'boxifying' the highlight).
1989   highLightSize.width = maxHighlightX - minHighlightX;
1990
1991   highLightPosition.x = minHighlightX;
1992   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1993   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
1994
1995   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
1996
1997   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1998   {
1999     CursorInfo primaryCursorInfo;
2000     GetCursorPosition( mEventData->mLeftSelectionPosition,
2001                        primaryCursorInfo );
2002
2003     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2004
2005     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2006                                          primaryPosition.x,
2007                                          primaryCursorInfo.lineOffset + mScrollPosition.y,
2008                                          primaryCursorInfo.lineHeight );
2009
2010     CursorInfo secondaryCursorInfo;
2011     GetCursorPosition( mEventData->mRightSelectionPosition,
2012                        secondaryCursorInfo );
2013
2014     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2015
2016     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2017                                          secondaryPosition.x,
2018                                          secondaryCursorInfo.lineOffset + mScrollPosition.y,
2019                                          secondaryCursorInfo.lineHeight );
2020   }
2021
2022   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2023   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2024
2025   // Set the flag to update the decorator.
2026   mEventData->mDecoratorUpdated = true;
2027 }
2028
2029 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2030 {
2031   if( NULL == mEventData )
2032   {
2033     // Nothing to do if there is no text input.
2034     return;
2035   }
2036
2037   if( IsShowingPlaceholderText() )
2038   {
2039     // Nothing to do if there is the place-holder text.
2040     return;
2041   }
2042
2043   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2044   const Length numberOfLines  = mVisualModel->mLines.Count();
2045   if( ( 0 == numberOfGlyphs ) ||
2046       ( 0 == numberOfLines ) )
2047   {
2048     // Nothing to do if there is no text.
2049     return;
2050   }
2051
2052   // Find which word was selected
2053   CharacterIndex selectionStart( 0 );
2054   CharacterIndex selectionEnd( 0 );
2055   const bool indicesFound = FindSelectionIndices( mVisualModel,
2056                                                   mLogicalModel,
2057                                                   mMetrics,
2058                                                   visualX,
2059                                                   visualY,
2060                                                   selectionStart,
2061                                                   selectionEnd );
2062   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2063
2064   if( indicesFound )
2065   {
2066     ChangeState( EventData::SELECTING );
2067
2068     mEventData->mLeftSelectionPosition = selectionStart;
2069     mEventData->mRightSelectionPosition = selectionEnd;
2070
2071     mEventData->mUpdateLeftSelectionPosition = true;
2072     mEventData->mUpdateRightSelectionPosition = true;
2073     mEventData->mUpdateHighlightBox = true;
2074
2075     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2076   }
2077   else
2078   {
2079     // Nothing to select. i.e. a white space, out of bounds
2080     ChangeState( EventData::EDITING );
2081
2082     mEventData->mPrimaryCursorPosition = selectionEnd;
2083
2084     mEventData->mUpdateCursorPosition = true;
2085     mEventData->mUpdateGrabHandlePosition = true;
2086     mEventData->mScrollAfterUpdatePosition = true;
2087     mEventData->mUpdateInputStyle = true;
2088   }
2089 }
2090
2091 void Controller::Impl::SetPopupButtons()
2092 {
2093   /**
2094    *  Sets the Popup buttons to be shown depending on State.
2095    *
2096    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2097    *
2098    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2099    */
2100
2101   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2102
2103   if( EventData::SELECTING == mEventData->mState )
2104   {
2105     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2106
2107     if( !IsClipboardEmpty() )
2108     {
2109       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2110       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2111     }
2112
2113     if( !mEventData->mAllTextSelected )
2114     {
2115       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2116     }
2117   }
2118   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2119   {
2120     if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2121     {
2122       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2123     }
2124
2125     if( !IsClipboardEmpty() )
2126     {
2127       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2128       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2129     }
2130   }
2131   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2132   {
2133     if ( !IsClipboardEmpty() )
2134     {
2135       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2136       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2137     }
2138   }
2139
2140   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2141 }
2142
2143 void Controller::Impl::ChangeState( EventData::State newState )
2144 {
2145   if( NULL == mEventData )
2146   {
2147     // Nothing to do if there is no text input.
2148     return;
2149   }
2150
2151   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2152
2153   if( mEventData->mState != newState )
2154   {
2155     mEventData->mState = newState;
2156
2157     switch( mEventData->mState )
2158     {
2159       case EventData::INACTIVE:
2160       {
2161         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2162         mEventData->mDecorator->StopCursorBlink();
2163         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2164         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2165         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2166         mEventData->mDecorator->SetPopupActive( false );
2167         mEventData->mDecoratorUpdated = true;
2168         HideClipboard();
2169         break;
2170       }
2171       case EventData::INTERRUPTED:
2172       {
2173         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2174         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2175         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2176         mEventData->mDecorator->SetPopupActive( false );
2177         mEventData->mDecoratorUpdated = true;
2178         HideClipboard();
2179         break;
2180       }
2181       case EventData::SELECTING:
2182       {
2183         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2184         mEventData->mDecorator->StopCursorBlink();
2185         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2186         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2187         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2188         if( mEventData->mGrabHandlePopupEnabled )
2189         {
2190           SetPopupButtons();
2191           mEventData->mDecorator->SetPopupActive( true );
2192         }
2193         mEventData->mDecoratorUpdated = true;
2194         break;
2195       }
2196       case EventData::EDITING:
2197       {
2198         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2199         if( mEventData->mCursorBlinkEnabled )
2200         {
2201           mEventData->mDecorator->StartCursorBlink();
2202         }
2203         // Grab handle is not shown until a tap is received whilst EDITING
2204         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2205         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2206         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2207         if( mEventData->mGrabHandlePopupEnabled )
2208         {
2209           mEventData->mDecorator->SetPopupActive( false );
2210         }
2211         mEventData->mDecoratorUpdated = true;
2212         HideClipboard();
2213         break;
2214       }
2215       case EventData::EDITING_WITH_POPUP:
2216       {
2217         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2218
2219         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2220         if( mEventData->mCursorBlinkEnabled )
2221         {
2222           mEventData->mDecorator->StartCursorBlink();
2223         }
2224         if( mEventData->mSelectionEnabled )
2225         {
2226           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2227           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2228         }
2229         else
2230         {
2231           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2232         }
2233         if( mEventData->mGrabHandlePopupEnabled )
2234         {
2235           SetPopupButtons();
2236           mEventData->mDecorator->SetPopupActive( true );
2237         }
2238         HideClipboard();
2239         mEventData->mDecoratorUpdated = true;
2240         break;
2241       }
2242       case EventData::EDITING_WITH_GRAB_HANDLE:
2243       {
2244         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2245
2246         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2247         if( mEventData->mCursorBlinkEnabled )
2248         {
2249           mEventData->mDecorator->StartCursorBlink();
2250         }
2251         // Grab handle is not shown until a tap is received whilst EDITING
2252         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2253         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2254         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2255         if( mEventData->mGrabHandlePopupEnabled )
2256         {
2257           mEventData->mDecorator->SetPopupActive( false );
2258         }
2259         mEventData->mDecoratorUpdated = true;
2260         HideClipboard();
2261         break;
2262       }
2263       case EventData::SELECTION_HANDLE_PANNING:
2264       {
2265         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2266         mEventData->mDecorator->StopCursorBlink();
2267         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2268         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2269         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2270         if( mEventData->mGrabHandlePopupEnabled )
2271         {
2272           mEventData->mDecorator->SetPopupActive( false );
2273         }
2274         mEventData->mDecoratorUpdated = true;
2275         break;
2276       }
2277       case EventData::GRAB_HANDLE_PANNING:
2278       {
2279         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2280
2281         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2282         if( mEventData->mCursorBlinkEnabled )
2283         {
2284           mEventData->mDecorator->StartCursorBlink();
2285         }
2286         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2287         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2288         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2289         if( mEventData->mGrabHandlePopupEnabled )
2290         {
2291           mEventData->mDecorator->SetPopupActive( false );
2292         }
2293         mEventData->mDecoratorUpdated = true;
2294         break;
2295       }
2296       case EventData::EDITING_WITH_PASTE_POPUP:
2297       {
2298         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2299
2300         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2301         if( mEventData->mCursorBlinkEnabled )
2302         {
2303           mEventData->mDecorator->StartCursorBlink();
2304         }
2305
2306         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2307         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2308         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2309
2310         if( mEventData->mGrabHandlePopupEnabled )
2311         {
2312           SetPopupButtons();
2313           mEventData->mDecorator->SetPopupActive( true );
2314         }
2315         HideClipboard();
2316         mEventData->mDecoratorUpdated = true;
2317         break;
2318       }
2319     }
2320   }
2321 }
2322
2323 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2324                                           CursorInfo& cursorInfo )
2325 {
2326   if( !IsShowingRealText() )
2327   {
2328     // Do not want to use the place-holder text to set the cursor position.
2329
2330     // Use the line's height of the font's family set to set the cursor's size.
2331     // If there is no font's family set, use the default font.
2332     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2333
2334     cursorInfo.lineOffset = 0.f;
2335     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2336     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2337
2338     switch( mLayoutEngine.GetHorizontalAlignment() )
2339     {
2340       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2341       {
2342         cursorInfo.primaryPosition.x = 0.f;
2343         break;
2344       }
2345       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2346       {
2347         cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2348         break;
2349       }
2350       case LayoutEngine::HORIZONTAL_ALIGN_END:
2351       {
2352         cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2353         break;
2354       }
2355     }
2356
2357     // Nothing else to do.
2358     return;
2359   }
2360
2361   Text::GetCursorPosition( mVisualModel,
2362                            mLogicalModel,
2363                            mMetrics,
2364                            logical,
2365                            cursorInfo );
2366
2367   if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2368   {
2369     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2370
2371     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2372     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2373
2374     if( 0.f > cursorInfo.primaryPosition.x )
2375     {
2376       cursorInfo.primaryPosition.x = 0.f;
2377     }
2378
2379     const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2380     if( cursorInfo.primaryPosition.x > edgeWidth )
2381     {
2382       cursorInfo.primaryPosition.x = edgeWidth;
2383     }
2384   }
2385 }
2386
2387 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2388 {
2389   if( NULL == mEventData )
2390   {
2391     // Nothing to do if there is no text input.
2392     return 0u;
2393   }
2394
2395   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2396
2397   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2398   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2399
2400   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2401   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2402
2403   if( numberOfCharacters > 1u )
2404   {
2405     const Script script = mLogicalModel->GetScript( index );
2406     if( HasLigatureMustBreak( script ) )
2407     {
2408       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
2409       numberOfCharacters = 1u;
2410     }
2411   }
2412   else
2413   {
2414     while( 0u == numberOfCharacters )
2415     {
2416       ++glyphIndex;
2417       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2418     }
2419   }
2420
2421   if( index < mEventData->mPrimaryCursorPosition )
2422   {
2423     cursorIndex -= numberOfCharacters;
2424   }
2425   else
2426   {
2427     cursorIndex += numberOfCharacters;
2428   }
2429
2430   // Will update the cursor hook position.
2431   mEventData->mUpdateCursorHookPosition = true;
2432
2433   return cursorIndex;
2434 }
2435
2436 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2437 {
2438   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2439   if( NULL == mEventData )
2440   {
2441     // Nothing to do if there is no text input.
2442     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2443     return;
2444   }
2445
2446   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2447
2448   // Sets the cursor position.
2449   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2450                                        cursorPosition.x,
2451                                        cursorPosition.y,
2452                                        cursorInfo.primaryCursorHeight,
2453                                        cursorInfo.lineHeight );
2454   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2455
2456   if( mEventData->mUpdateGrabHandlePosition )
2457   {
2458     // Sets the grab handle position.
2459     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2460                                          cursorPosition.x,
2461                                          cursorInfo.lineOffset + mScrollPosition.y,
2462                                          cursorInfo.lineHeight );
2463   }
2464
2465   if( cursorInfo.isSecondaryCursor )
2466   {
2467     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2468                                          cursorInfo.secondaryPosition.x + mScrollPosition.x,
2469                                          cursorInfo.secondaryPosition.y + mScrollPosition.y,
2470                                          cursorInfo.secondaryCursorHeight,
2471                                          cursorInfo.lineHeight );
2472     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2473   }
2474
2475   // Set which cursors are active according the state.
2476   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2477   {
2478     if( cursorInfo.isSecondaryCursor )
2479     {
2480       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2481     }
2482     else
2483     {
2484       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2485     }
2486   }
2487   else
2488   {
2489     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2490   }
2491
2492   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2493 }
2494
2495 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2496                                               const CursorInfo& cursorInfo )
2497 {
2498   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2499       ( RIGHT_SELECTION_HANDLE != handleType ) )
2500   {
2501     return;
2502   }
2503
2504   const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2505
2506   // Sets the handle's position.
2507   mEventData->mDecorator->SetPosition( handleType,
2508                                        cursorPosition.x,
2509                                        cursorInfo.lineOffset + mScrollPosition.y,
2510                                        cursorInfo.lineHeight );
2511
2512   // If selection handle at start of the text and other at end of the text then all text is selected.
2513   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2514   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2515   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2516 }
2517
2518 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2519 {
2520   // Clamp between -space & 0.
2521
2522   if( layoutSize.width > mVisualModel->mControlSize.width )
2523   {
2524     const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2525     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2526     mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2527
2528     mEventData->mDecoratorUpdated = true;
2529   }
2530   else
2531   {
2532     mScrollPosition.x = 0.f;
2533   }
2534 }
2535
2536 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2537 {
2538   // Clamp between -space & 0.
2539   if( layoutSize.height > mVisualModel->mControlSize.height )
2540   {
2541     const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2542     mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2543     mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2544
2545     mEventData->mDecoratorUpdated = true;
2546   }
2547   else
2548   {
2549     mScrollPosition.y = 0.f;
2550   }
2551 }
2552
2553 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2554 {
2555   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2556
2557   // position is in actor's coords.
2558   const float positionEndX = position.x + cursorWidth;
2559   const float positionEndY = position.y + lineHeight;
2560
2561   // Transform the position to decorator coords.
2562   const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2563   const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2564
2565   const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2566   const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2567
2568   if( decoratorPositionBeginX < 0.f )
2569   {
2570     mScrollPosition.x = -position.x;
2571   }
2572   else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2573   {
2574     mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2575   }
2576
2577   if( decoratorPositionBeginY < 0.f )
2578   {
2579     mScrollPosition.y = -position.y;
2580   }
2581   else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2582   {
2583     mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2584   }
2585 }
2586
2587 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2588 {
2589   // Get the current cursor position in decorator coords.
2590   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2591
2592   // Calculate the offset to match the cursor position before the character was deleted.
2593   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2594   mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2595
2596   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2597   ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2598
2599   // Makes the new cursor position visible if needed.
2600   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2601 }
2602
2603 void Controller::Impl::RequestRelayout()
2604 {
2605   mControlInterface.RequestTextRelayout();
2606 }
2607
2608 } // namespace Text
2609
2610 } // namespace Toolkit
2611
2612 } // namespace Dali