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