TextModel - Update the color runs.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <memory.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/markup-processor.h>
33 #include <dali-toolkit/internal/text/text-controller-impl.h>
34
35 namespace
36 {
37
38 #if defined(DEBUG_ENABLED)
39   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
40 #endif
41
42 const float MAX_FLOAT = std::numeric_limits<float>::max();
43 const unsigned int POINTS_PER_INCH = 72;
44
45 const std::string EMPTY_STRING("");
46
47 float ConvertToEven( float value )
48 {
49   int intValue(static_cast<int>( value ));
50   return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
51 }
52
53 } // namespace
54
55 namespace Dali
56 {
57
58 namespace Toolkit
59 {
60
61 namespace Text
62 {
63
64 /**
65  * @brief Adds a new font description run for the selected text.
66  *
67  * The new font parameters are added after the call to this method.
68  *
69  * @param[in] eventData The event data pointer.
70  * @param[in] logicalModel The logical model where to add the new font description run.
71  */
72 FontDescriptionRun& UpdateSelectionFontStyleRun( EventData* eventData,
73                                                  LogicalModelPtr logicalModel )
74 {
75   const bool handlesCrossed = eventData->mLeftSelectionPosition > eventData->mRightSelectionPosition;
76
77   // Get start and end position of selection
78   const CharacterIndex startOfSelectedText = handlesCrossed ? eventData->mRightSelectionPosition : eventData->mLeftSelectionPosition;
79   const Length lengthOfSelectedText = ( handlesCrossed ? eventData->mLeftSelectionPosition : eventData->mRightSelectionPosition ) - startOfSelectedText;
80
81   // Add the font run.
82   const VectorBase::SizeType numberOfRuns = logicalModel->mFontDescriptionRuns.Count();
83   logicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
84
85   FontDescriptionRun& fontDescriptionRun = *( logicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
86
87   fontDescriptionRun.characterRun.characterIndex = startOfSelectedText;
88   fontDescriptionRun.characterRun.numberOfCharacters = lengthOfSelectedText;
89
90   // Recalculate the selection highlight as the metrics may have changed.
91   eventData->mUpdateLeftSelectionPosition = true;
92   eventData->mUpdateRightSelectionPosition = true;
93
94   return fontDescriptionRun;
95 }
96
97 ControllerPtr Controller::New( ControlInterface& controlInterface )
98 {
99   return ControllerPtr( new Controller( controlInterface ) );
100 }
101
102 void Controller::EnableTextInput( DecoratorPtr decorator )
103 {
104   if( NULL == mImpl->mEventData )
105   {
106     mImpl->mEventData = new EventData( decorator );
107   }
108 }
109
110 void Controller::SetGlyphType( TextAbstraction::GlyphType glyphType )
111 {
112   // Metrics for bitmap & vector based glyphs are different
113   mImpl->mMetrics->SetGlyphType( glyphType );
114
115   // Clear the font-specific data
116   ClearFontData();
117
118   mImpl->mRecalculateNaturalSize = true;
119
120   mImpl->RequestRelayout();
121 }
122
123 void Controller::SetMarkupProcessorEnabled( bool enable )
124 {
125   mImpl->mMarkupProcessorEnabled = enable;
126 }
127
128 bool Controller::IsMarkupProcessorEnabled() const
129 {
130   return mImpl->mMarkupProcessorEnabled;
131 }
132
133 void Controller::SetText( const std::string& text )
134 {
135   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
136
137   // Reset keyboard as text changed
138   mImpl->ResetImfManager();
139
140   // Remove the previously set text and style.
141   ResetText();
142
143   // Remove the style.
144   ClearStyleData();
145
146   CharacterIndex lastCursorIndex = 0u;
147
148   if( NULL != mImpl->mEventData )
149   {
150     // If popup shown then hide it by switching to Editing state
151     if( ( EventData::SELECTING == mImpl->mEventData->mState )          ||
152         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
153         ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) ||
154         ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
155     {
156       mImpl->ChangeState( EventData::EDITING );
157     }
158   }
159
160   if( !text.empty() )
161   {
162     mImpl->mVisualModel->SetTextColor( mImpl->mTextColor );
163
164     MarkupProcessData markupProcessData( mImpl->mLogicalModel->mColorRuns,
165                                          mImpl->mLogicalModel->mFontDescriptionRuns );
166
167     Length textSize = 0u;
168     const uint8_t* utf8 = NULL;
169     if( mImpl->mMarkupProcessorEnabled )
170     {
171       ProcessMarkupString( text, markupProcessData );
172       textSize = markupProcessData.markupProcessedText.size();
173
174       // This is a bit horrible but std::string returns a (signed) char*
175       utf8 = reinterpret_cast<const uint8_t*>( markupProcessData.markupProcessedText.c_str() );
176     }
177     else
178     {
179       textSize = text.size();
180
181       // This is a bit horrible but std::string returns a (signed) char*
182       utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
183     }
184
185     //  Convert text into UTF-32
186     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
187     utf32Characters.Resize( textSize );
188
189     // Transform a text array encoded in utf8 into an array encoded in utf32.
190     // It returns the actual number of characters.
191     Length characterCount = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
192     utf32Characters.Resize( characterCount );
193
194     DALI_ASSERT_DEBUG( textSize >= characterCount && "Invalid UTF32 conversion length" );
195     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, textSize, mImpl->mLogicalModel->mText.Count() );
196
197     // The characters to be added.
198     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mLogicalModel->mText.Count();
199
200     // To reset the cursor position
201     lastCursorIndex = characterCount;
202
203     // Update the rest of the model during size negotiation
204     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
205
206     // The natural size needs to be re-calculated.
207     mImpl->mRecalculateNaturalSize = true;
208
209     // Apply modifications to the model
210     mImpl->mOperationsPending = ALL_OPERATIONS;
211   }
212   else
213   {
214     ShowPlaceholderText();
215   }
216
217   // Resets the cursor position.
218   ResetCursorPosition( lastCursorIndex );
219
220   // Scrolls the text to make the cursor visible.
221   ResetScrollPosition();
222
223   mImpl->RequestRelayout();
224
225   if( NULL != mImpl->mEventData )
226   {
227     // Cancel previously queued events
228     mImpl->mEventData->mEventQueue.clear();
229   }
230
231   // Notify IMF as text changed
232   NotifyImfManager();
233
234   // Do this last since it provides callbacks into application code
235   mImpl->mControlInterface.TextChanged();
236 }
237
238 void Controller::GetText( std::string& text ) const
239 {
240   if( !mImpl->IsShowingPlaceholderText() )
241   {
242     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
243
244     if( 0u != utf32Characters.Count() )
245     {
246       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
247     }
248   }
249   else
250   {
251     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
252   }
253 }
254
255 unsigned int Controller::GetLogicalCursorPosition() const
256 {
257   if( NULL != mImpl->mEventData )
258   {
259     return mImpl->mEventData->mPrimaryCursorPosition;
260   }
261
262   return 0u;
263 }
264
265 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
266 {
267   if( NULL != mImpl->mEventData )
268   {
269     if( PLACEHOLDER_TYPE_INACTIVE == type )
270     {
271       mImpl->mEventData->mPlaceholderTextInactive = text;
272     }
273     else
274     {
275       mImpl->mEventData->mPlaceholderTextActive = text;
276     }
277
278     // Update placeholder if there is no text
279     if( mImpl->IsShowingPlaceholderText() ||
280         ( 0u == mImpl->mLogicalModel->mText.Count() ) )
281     {
282       ShowPlaceholderText();
283     }
284   }
285 }
286
287 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
288 {
289   if( NULL != mImpl->mEventData )
290   {
291     if( PLACEHOLDER_TYPE_INACTIVE == type )
292     {
293       text = mImpl->mEventData->mPlaceholderTextInactive;
294     }
295     else
296     {
297       text = mImpl->mEventData->mPlaceholderTextActive;
298     }
299   }
300 }
301
302 void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
303 {
304   mImpl->mMaximumNumberOfCharacters = maxCharacters;
305 }
306
307 int Controller::GetMaximumNumberOfCharacters()
308 {
309   return mImpl->mMaximumNumberOfCharacters;
310 }
311
312 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
313 {
314   if( NULL == mImpl->mFontDefaults )
315   {
316     mImpl->mFontDefaults = new FontDefaults();
317   }
318
319   mImpl->mFontDefaults->mFontDescription.family = defaultFontFamily;
320   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str());
321   mImpl->mFontDefaults->familyDefined = true;
322
323   // Clear the font-specific data
324   ClearFontData();
325
326   mImpl->mRecalculateNaturalSize = true;
327
328   mImpl->RequestRelayout();
329 }
330
331 const std::string& Controller::GetDefaultFontFamily() const
332 {
333   if( NULL != mImpl->mFontDefaults )
334   {
335     return mImpl->mFontDefaults->mFontDescription.family;
336   }
337
338   return EMPTY_STRING;
339 }
340
341 void Controller::SetDefaultFontStyle( const std::string& style )
342 {
343   if( NULL == mImpl->mFontDefaults )
344   {
345     mImpl->mFontDefaults = new FontDefaults();
346   }
347
348   mImpl->mFontDefaults->mFontStyle = style;
349 }
350
351 const std::string& Controller::GetDefaultFontStyle() const
352 {
353   if( NULL != mImpl->mFontDefaults )
354   {
355     return mImpl->mFontDefaults->mFontStyle;
356   }
357
358   return EMPTY_STRING;
359 }
360
361 void Controller::SetDefaultFontWeight( FontWeight weight )
362 {
363   if( NULL == mImpl->mFontDefaults )
364   {
365     mImpl->mFontDefaults = new FontDefaults();
366   }
367
368   mImpl->mFontDefaults->mFontDescription.weight = weight;
369   mImpl->mFontDefaults->weightDefined = true;
370
371   // Clear the font-specific data
372   ClearFontData();
373
374   mImpl->mRecalculateNaturalSize = true;
375
376   mImpl->RequestRelayout();
377 }
378
379 FontWeight Controller::GetDefaultFontWeight() const
380 {
381   if( NULL != mImpl->mFontDefaults )
382   {
383     return mImpl->mFontDefaults->mFontDescription.weight;
384   }
385
386   return TextAbstraction::FontWeight::NORMAL;
387 }
388
389 void Controller::SetDefaultFontWidth( FontWidth width )
390 {
391   if( NULL == mImpl->mFontDefaults )
392   {
393     mImpl->mFontDefaults = new FontDefaults();
394   }
395
396   mImpl->mFontDefaults->mFontDescription.width = width;
397   mImpl->mFontDefaults->widthDefined = true;
398
399   // Clear the font-specific data
400   ClearFontData();
401
402   mImpl->mRecalculateNaturalSize = true;
403
404   mImpl->RequestRelayout();
405 }
406
407 FontWidth Controller::GetDefaultFontWidth() const
408 {
409   if( NULL != mImpl->mFontDefaults )
410   {
411     return mImpl->mFontDefaults->mFontDescription.width;
412   }
413
414   return TextAbstraction::FontWidth::NORMAL;
415 }
416
417 void Controller::SetDefaultFontSlant( FontSlant slant )
418 {
419   if( NULL == mImpl->mFontDefaults )
420   {
421     mImpl->mFontDefaults = new FontDefaults();
422   }
423
424   mImpl->mFontDefaults->mFontDescription.slant = slant;
425   mImpl->mFontDefaults->slantDefined = true;
426
427   // Clear the font-specific data
428   ClearFontData();
429
430   mImpl->mRecalculateNaturalSize = true;
431
432   mImpl->RequestRelayout();
433 }
434
435 FontSlant Controller::GetDefaultFontSlant() const
436 {
437   if( NULL != mImpl->mFontDefaults )
438   {
439     return mImpl->mFontDefaults->mFontDescription.slant;
440   }
441
442   return TextAbstraction::FontSlant::NORMAL;
443 }
444
445 void Controller::SetDefaultPointSize( float pointSize )
446 {
447   if( NULL == mImpl->mFontDefaults )
448   {
449     mImpl->mFontDefaults = new FontDefaults();
450   }
451
452   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
453   mImpl->mFontDefaults->sizeDefined = true;
454
455   unsigned int horizontalDpi( 0u );
456   unsigned int verticalDpi( 0u );
457   mImpl->mFontClient.GetDpi( horizontalDpi, verticalDpi );
458
459   // Adjust the metrics if the fixed-size font should be down-scaled
460   int maxEmojiSize( pointSize/POINTS_PER_INCH * verticalDpi );
461   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultPointSize %p setting MaxEmojiSize %d\n", this, maxEmojiSize );
462   mImpl->mMetrics->SetMaxEmojiSize( maxEmojiSize );
463
464   // Clear the font-specific data
465   ClearFontData();
466
467   mImpl->mRecalculateNaturalSize = true;
468
469   mImpl->RequestRelayout();
470 }
471
472 float Controller::GetDefaultPointSize() const
473 {
474   if( NULL != mImpl->mFontDefaults )
475   {
476     return mImpl->mFontDefaults->mDefaultPointSize;
477   }
478
479   return 0.0f;
480 }
481
482 void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
483 {
484   DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange");
485
486   if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
487   {
488     DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
489     ClearFontData();
490     mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
491     mImpl->mRecalculateNaturalSize = true;
492     mImpl->RequestRelayout();
493   }
494 }
495
496 void Controller::SetTextColor( const Vector4& textColor )
497 {
498   mImpl->mTextColor = textColor;
499
500   if( !mImpl->IsShowingPlaceholderText() )
501   {
502     mImpl->mVisualModel->SetTextColor( textColor );
503
504     mImpl->RequestRelayout();
505   }
506 }
507
508 const Vector4& Controller::GetTextColor() const
509 {
510   return mImpl->mTextColor;
511 }
512
513 bool Controller::RemoveText( int cursorOffset,
514                              int numberOfCharacters,
515                              UpdateInputStyleType type )
516 {
517   bool removed = false;
518
519   if( NULL == mImpl->mEventData )
520   {
521     return removed;
522   }
523
524   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
525                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
526
527   if( !mImpl->IsShowingPlaceholderText() )
528   {
529     // Delete at current cursor position
530     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
531     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
532
533     CharacterIndex cursorIndex = oldCursorIndex;
534
535     // Validate the cursor position & number of characters
536     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
537     {
538       cursorIndex = oldCursorIndex + cursorOffset;
539     }
540
541     if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
542     {
543       numberOfCharacters = currentText.Count() - cursorIndex;
544     }
545
546     if( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
547     {
548       // Mark the paragraphs to be updated.
549       mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
550       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
551
552       // Update the input style and remove the text's style before removing the text.
553
554       if( UPDATE_INPUT_STYLE == type )
555       {
556         // Set first the default input style.
557         mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
558
559         // Update the input style.
560         mImpl->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
561       }
562
563       // Updates the text style runs by removing characters. Runs with no characters are removed.
564       mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
565
566       // Remove the characters.
567       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
568       Vector<Character>::Iterator last  = first + numberOfCharacters;
569
570       currentText.Erase( first, last );
571
572       // Cursor position retreat
573       oldCursorIndex = cursorIndex;
574
575       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
576       removed = true;
577     }
578   }
579
580   return removed;
581 }
582
583 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
584 {
585   if( NULL != mImpl->mEventData )
586   {
587     mImpl->mEventData->mPlaceholderTextColor = textColor;
588   }
589
590   if( mImpl->IsShowingPlaceholderText() )
591   {
592     mImpl->mVisualModel->SetTextColor( textColor );
593     mImpl->RequestRelayout();
594   }
595 }
596
597 const Vector4& Controller::GetPlaceholderTextColor() const
598 {
599   if( NULL != mImpl->mEventData )
600   {
601     return mImpl->mEventData->mPlaceholderTextColor;
602   }
603
604   return Color::BLACK;
605 }
606
607 void Controller::SetShadowOffset( const Vector2& shadowOffset )
608 {
609   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
610
611   mImpl->RequestRelayout();
612 }
613
614 const Vector2& Controller::GetShadowOffset() const
615 {
616   return mImpl->mVisualModel->GetShadowOffset();
617 }
618
619 void Controller::SetShadowColor( const Vector4& shadowColor )
620 {
621   mImpl->mVisualModel->SetShadowColor( shadowColor );
622
623   mImpl->RequestRelayout();
624 }
625
626 const Vector4& Controller::GetShadowColor() const
627 {
628   return mImpl->mVisualModel->GetShadowColor();
629 }
630
631 void Controller::SetUnderlineColor( const Vector4& color )
632 {
633   mImpl->mVisualModel->SetUnderlineColor( color );
634
635   mImpl->RequestRelayout();
636 }
637
638 const Vector4& Controller::GetUnderlineColor() const
639 {
640   return mImpl->mVisualModel->GetUnderlineColor();
641 }
642
643 void Controller::SetUnderlineEnabled( bool enabled )
644 {
645   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
646
647   mImpl->RequestRelayout();
648 }
649
650 bool Controller::IsUnderlineEnabled() const
651 {
652   return mImpl->mVisualModel->IsUnderlineEnabled();
653 }
654
655 void Controller::SetUnderlineHeight( float height )
656 {
657   mImpl->mVisualModel->SetUnderlineHeight( height );
658
659   mImpl->RequestRelayout();
660 }
661
662 float Controller::GetUnderlineHeight() const
663 {
664   return mImpl->mVisualModel->GetUnderlineHeight();
665 }
666
667 void Controller::SetInputColor( const Vector4& color )
668 {
669   if( NULL != mImpl->mEventData )
670   {
671     mImpl->mEventData->mInputStyle.textColor = color;
672     mImpl->mEventData->mInputStyle.isDefaultColor = false;
673
674     if( EventData::SELECTING == mImpl->mEventData->mState )
675     {
676       const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition;
677
678       // Get start and end position of selection
679       const CharacterIndex startOfSelectedText = handlesCrossed ? mImpl->mEventData->mRightSelectionPosition : mImpl->mEventData->mLeftSelectionPosition;
680       const Length lengthOfSelectedText = ( handlesCrossed ? mImpl->mEventData->mLeftSelectionPosition : mImpl->mEventData->mRightSelectionPosition ) - startOfSelectedText;
681
682       // Add the color run.
683       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
684       mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
685
686       ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
687       colorRun.color = color;
688       colorRun.characterRun.characterIndex = startOfSelectedText;
689       colorRun.characterRun.numberOfCharacters = lengthOfSelectedText;
690
691       // Request to relayout.
692       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | COLOR );
693       mImpl->RequestRelayout();
694
695       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
696       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
697       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
698     }
699   }
700 }
701
702 const Vector4& Controller::GetInputColor() const
703 {
704   if( NULL != mImpl->mEventData )
705   {
706     return mImpl->mEventData->mInputStyle.textColor;
707   }
708
709   // Return the default text's color if there is no EventData.
710   return mImpl->mTextColor;
711
712 }
713
714 void Controller::SetInputFontFamily( const std::string& fontFamily )
715 {
716   if( NULL != mImpl->mEventData )
717   {
718     mImpl->mEventData->mInputStyle.familyName = fontFamily;
719     mImpl->mEventData->mInputStyle.familyDefined = true;
720
721     if( EventData::SELECTING == mImpl->mEventData->mState )
722     {
723       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
724                                                                             mImpl->mLogicalModel );
725
726       fontDescriptionRun.familyLength = fontFamily.size();
727       fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
728       memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
729       fontDescriptionRun.familyDefined = true;
730
731       // The memory allocated for the font family name is freed when the font description is removed from the logical model.
732
733       // Request to relayout.
734       mImpl->mOperationsPending = ALL_OPERATIONS;
735       mImpl->mRecalculateNaturalSize = true;
736       mImpl->RequestRelayout();
737
738       // As the font changes, recalculate the handle positions is needed.
739       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
740       mImpl->mEventData->mUpdateRightSelectionPosition = true;
741       mImpl->mEventData->mScrollAfterUpdatePosition = true;
742     }
743   }
744 }
745
746 const std::string& Controller::GetInputFontFamily() const
747 {
748   if( NULL != mImpl->mEventData )
749   {
750     return mImpl->mEventData->mInputStyle.familyName;
751   }
752
753   // Return the default font's family if there is no EventData.
754   return GetDefaultFontFamily();
755 }
756
757 void Controller::SetInputFontStyle( const std::string& fontStyle )
758 {
759   if( NULL != mImpl->mEventData )
760   {
761     mImpl->mEventData->mInputStyle.fontStyle = fontStyle;
762   }
763 }
764
765 const std::string& Controller::GetInputFontStyle() const
766 {
767   if( NULL != mImpl->mEventData )
768   {
769     return mImpl->mEventData->mInputStyle.fontStyle;
770   }
771
772   // Return the default font's style if there is no EventData.
773   return GetDefaultFontStyle();
774 }
775
776 void Controller::SetInputFontWeight( FontWeight weight )
777 {
778   if( NULL != mImpl->mEventData )
779   {
780     mImpl->mEventData->mInputStyle.weight = weight;
781     mImpl->mEventData->mInputStyle.weightDefined = true;
782
783     if( EventData::SELECTING == mImpl->mEventData->mState )
784     {
785       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
786                                                                             mImpl->mLogicalModel );
787
788       fontDescriptionRun.weight = weight;
789       fontDescriptionRun.weightDefined = true;
790
791       // Request to relayout.
792       mImpl->mOperationsPending = ALL_OPERATIONS;
793       mImpl->mRecalculateNaturalSize = true;
794       mImpl->RequestRelayout();
795
796       // As the font might change, recalculate the handle positions is needed.
797       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
798       mImpl->mEventData->mUpdateRightSelectionPosition = true;
799       mImpl->mEventData->mScrollAfterUpdatePosition = true;
800     }
801   }
802 }
803
804 FontWeight Controller::GetInputFontWeight() const
805 {
806   if( NULL != mImpl->mEventData )
807   {
808     return mImpl->mEventData->mInputStyle.weight;
809   }
810
811   return GetDefaultFontWeight();
812 }
813
814 void Controller::SetInputFontWidth( FontWidth width )
815 {
816   if( NULL != mImpl->mEventData )
817   {
818     mImpl->mEventData->mInputStyle.width = width;
819     mImpl->mEventData->mInputStyle.widthDefined = true;
820
821     if( EventData::SELECTING == mImpl->mEventData->mState )
822     {
823       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
824                                                                             mImpl->mLogicalModel );
825
826       fontDescriptionRun.width = width;
827       fontDescriptionRun.widthDefined = true;
828
829       // Request to relayout.
830       mImpl->mOperationsPending = ALL_OPERATIONS;
831       mImpl->mRecalculateNaturalSize = true;
832       mImpl->RequestRelayout();
833
834       // As the font might change, recalculate the handle positions is needed.
835       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
836       mImpl->mEventData->mUpdateRightSelectionPosition = true;
837       mImpl->mEventData->mScrollAfterUpdatePosition = true;
838     }
839   }
840 }
841
842 FontWidth Controller::GetInputFontWidth() const
843 {
844   if( NULL != mImpl->mEventData )
845   {
846     return mImpl->mEventData->mInputStyle.width;
847   }
848
849   return GetDefaultFontWidth();
850 }
851
852 void Controller::SetInputFontSlant( FontSlant slant )
853 {
854   if( NULL != mImpl->mEventData )
855   {
856     mImpl->mEventData->mInputStyle.slant = slant;
857     mImpl->mEventData->mInputStyle.slantDefined = true;
858
859     if( EventData::SELECTING == mImpl->mEventData->mState )
860     {
861       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
862                                                                             mImpl->mLogicalModel );
863
864       fontDescriptionRun.slant = slant;
865       fontDescriptionRun.slantDefined = true;
866
867       // Request to relayout.
868       mImpl->mOperationsPending = ALL_OPERATIONS;
869       mImpl->mRecalculateNaturalSize = true;
870       mImpl->RequestRelayout();
871
872       // As the font might change, recalculate the handle positions is needed.
873       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
874       mImpl->mEventData->mUpdateRightSelectionPosition = true;
875       mImpl->mEventData->mScrollAfterUpdatePosition = true;
876     }
877   }
878 }
879
880 FontSlant Controller::GetInputFontSlant() const
881 {
882   if( NULL != mImpl->mEventData )
883   {
884     return mImpl->mEventData->mInputStyle.slant;
885   }
886
887   return GetDefaultFontSlant();
888 }
889
890 void Controller::SetInputFontPointSize( float size )
891 {
892   if( NULL != mImpl->mEventData )
893   {
894     mImpl->mEventData->mInputStyle.size = size;
895
896     if( EventData::SELECTING == mImpl->mEventData->mState )
897     {
898       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
899                                                                             mImpl->mLogicalModel );
900
901       fontDescriptionRun.size = static_cast<PointSize26Dot6>( size * 64.f );
902       fontDescriptionRun.sizeDefined = true;
903
904       // Request to relayout.
905       mImpl->mOperationsPending = ALL_OPERATIONS;
906       mImpl->mRecalculateNaturalSize = true;
907       mImpl->RequestRelayout();
908
909       // As the font might change, recalculate the handle positions is needed.
910       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
911       mImpl->mEventData->mUpdateRightSelectionPosition = true;
912       mImpl->mEventData->mScrollAfterUpdatePosition = true;
913     }
914   }
915 }
916
917 float Controller::GetInputFontPointSize() const
918 {
919   if( NULL != mImpl->mEventData )
920   {
921     return mImpl->mEventData->mInputStyle.size;
922   }
923
924   // Return the default font's point size if there is no EventData.
925   return GetDefaultPointSize();
926 }
927
928 void Controller::SetEnableCursorBlink( bool enable )
929 {
930   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
931
932   if( NULL != mImpl->mEventData )
933   {
934     mImpl->mEventData->mCursorBlinkEnabled = enable;
935
936     if( !enable &&
937         mImpl->mEventData->mDecorator )
938     {
939       mImpl->mEventData->mDecorator->StopCursorBlink();
940     }
941   }
942 }
943
944 bool Controller::GetEnableCursorBlink() const
945 {
946   if( NULL != mImpl->mEventData )
947   {
948     return mImpl->mEventData->mCursorBlinkEnabled;
949   }
950
951   return false;
952 }
953
954 const Vector2& Controller::GetScrollPosition() const
955 {
956   if( NULL != mImpl->mEventData )
957   {
958     return mImpl->mEventData->mScrollPosition;
959   }
960
961   return Vector2::ZERO;
962 }
963
964 const Vector2& Controller::GetAlignmentOffset() const
965 {
966   return mImpl->mAlignmentOffset;
967 }
968
969 Vector3 Controller::GetNaturalSize()
970 {
971   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
972   Vector3 naturalSize;
973
974   // Make sure the model is up-to-date before layouting
975   ProcessModifyEvents();
976
977   if( mImpl->mRecalculateNaturalSize )
978   {
979     // Operations that can be done only once until the text changes.
980     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
981                                                                            GET_SCRIPTS       |
982                                                                            VALIDATE_FONTS    |
983                                                                            GET_LINE_BREAKS   |
984                                                                            GET_WORD_BREAKS   |
985                                                                            BIDI_INFO         |
986                                                                            SHAPE_TEXT        |
987                                                                            GET_GLYPH_METRICS );
988     // Make sure the model is up-to-date before layouting
989     mImpl->UpdateModel( onlyOnceOperations );
990
991     // Layout the text for the new width.
992     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
993
994     // Set the update info to relayout the whole text.
995     mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
996     mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
997
998     // Store the actual control's width.
999     const float actualControlWidth = mImpl->mVisualModel->mControlSize.width;
1000
1001     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1002                 static_cast<OperationsMask>( onlyOnceOperations |
1003                                              LAYOUT ),
1004                 naturalSize.GetVectorXY() );
1005
1006     // Do not do again the only once operations.
1007     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1008
1009     // Do the size related operations again.
1010     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
1011                                                                         ALIGN  |
1012                                                                         REORDER );
1013     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1014
1015     // Stores the natural size to avoid recalculate it again
1016     // unless the text/style changes.
1017     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1018
1019     mImpl->mRecalculateNaturalSize = false;
1020
1021     // Clear the update info. This info will be set the next time the text is updated.
1022     mImpl->mTextUpdateInfo.Clear();
1023
1024     // Restore the actual control's width.
1025     mImpl->mVisualModel->mControlSize.width = actualControlWidth;
1026
1027     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1028   }
1029   else
1030   {
1031     naturalSize = mImpl->mVisualModel->GetNaturalSize();
1032
1033     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1034   }
1035
1036   naturalSize.x = ConvertToEven( naturalSize.x );
1037   naturalSize.y = ConvertToEven( naturalSize.y );
1038
1039   return naturalSize;
1040 }
1041
1042 float Controller::GetHeightForWidth( float width )
1043 {
1044   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
1045   // Make sure the model is up-to-date before layouting
1046   ProcessModifyEvents();
1047
1048   Size layoutSize;
1049   if( fabsf( width - mImpl->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 )
1050   {
1051     // Operations that can be done only once until the text changes.
1052     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
1053                                                                            GET_SCRIPTS       |
1054                                                                            VALIDATE_FONTS    |
1055                                                                            GET_LINE_BREAKS   |
1056                                                                            GET_WORD_BREAKS   |
1057                                                                            BIDI_INFO         |
1058                                                                            SHAPE_TEXT        |
1059                                                                            GET_GLYPH_METRICS );
1060     // Make sure the model is up-to-date before layouting
1061     mImpl->UpdateModel( onlyOnceOperations );
1062
1063
1064     // Layout the text for the new width.
1065     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
1066
1067     // Set the update info to relayout the whole text.
1068     mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1069     mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
1070
1071     // Store the actual control's width.
1072     const float actualControlWidth = mImpl->mVisualModel->mControlSize.width;
1073
1074     DoRelayout( Size( width, MAX_FLOAT ),
1075                 static_cast<OperationsMask>( onlyOnceOperations |
1076                                              LAYOUT ),
1077                 layoutSize );
1078
1079     // Do not do again the only once operations.
1080     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1081
1082     // Do the size related operations again.
1083     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
1084                                                                         ALIGN  |
1085                                                                         REORDER );
1086
1087     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1088
1089     // Clear the update info. This info will be set the next time the text is updated.
1090     mImpl->mTextUpdateInfo.Clear();
1091
1092     // Restore the actual control's width.
1093     mImpl->mVisualModel->mControlSize.width = actualControlWidth;
1094
1095     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
1096   }
1097   else
1098   {
1099     layoutSize = mImpl->mVisualModel->GetLayoutSize();
1100     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
1101   }
1102
1103   return layoutSize.height;
1104 }
1105
1106 bool Controller::Relayout( const Size& size )
1107 {
1108   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
1109
1110   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1111   {
1112     bool glyphsRemoved( false );
1113     if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
1114     {
1115       mImpl->mVisualModel->mGlyphPositions.Clear();
1116       glyphsRemoved = true;
1117     }
1118
1119     // Clear the update info. This info will be set the next time the text is updated.
1120     mImpl->mTextUpdateInfo.Clear();
1121
1122     // Not worth to relayout if width or height is equal to zero.
1123     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
1124
1125     return glyphsRemoved;
1126   }
1127
1128   // Whether a new size has been set.
1129   const bool newSize = ( size != mImpl->mVisualModel->mControlSize );
1130
1131   if( newSize )
1132   {
1133     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
1134
1135     // Layout operations that need to be done if the size changes.
1136     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1137                                                              LAYOUT                    |
1138                                                              ALIGN                     |
1139                                                              UPDATE_ACTUAL_SIZE        |
1140                                                              REORDER );
1141     // Set the update info to relayout the whole text.
1142     mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
1143     mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
1144   }
1145
1146   // Whether there are modify events.
1147   if( 0u != mImpl->mModifyEvents.Count() )
1148   {
1149     // Style operations that need to be done if the text is modified.
1150     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1151                                                              COLOR );
1152   }
1153
1154   // Make sure the model is up-to-date before layouting.
1155   ProcessModifyEvents();
1156   bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
1157
1158   // Layout the text.
1159   Size layoutSize;
1160   updated = DoRelayout( size,
1161                         mImpl->mOperationsPending,
1162                         layoutSize ) || updated;
1163
1164   // Do not re-do any operation until something changes.
1165   mImpl->mOperationsPending = NO_OPERATION;
1166
1167   // Whether the text control is editable
1168   const bool isEditable = NULL != mImpl->mEventData;
1169
1170   // Keep the current offset and alignment as it will be used to update the decorator's positions (if the size changes).
1171   Vector2 offset;
1172   if( newSize && isEditable )
1173   {
1174     offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
1175   }
1176
1177   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1178   CalculateTextAlignment( size );
1179
1180   if( isEditable )
1181   {
1182     if( newSize )
1183     {
1184       // If there is a new size, the scroll position needs to be clamped.
1185       mImpl->ClampHorizontalScroll( layoutSize );
1186
1187       // Update the decorator's positions is needed if there is a new size.
1188       mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
1189     }
1190
1191     // Move the cursor, grab handle etc.
1192     updated = mImpl->ProcessInputEvents() || updated;
1193   }
1194
1195   // Clear the update info. This info will be set the next time the text is updated.
1196   mImpl->mTextUpdateInfo.Clear();
1197   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
1198
1199   return updated;
1200 }
1201
1202 void Controller::ProcessModifyEvents()
1203 {
1204   Vector<ModifyEvent>& events = mImpl->mModifyEvents;
1205
1206   if( 0u == events.Count() )
1207   {
1208     // Nothing to do.
1209     return;
1210   }
1211
1212   for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
1213          endIt = events.End();
1214        it != endIt;
1215        ++it )
1216   {
1217     const ModifyEvent& event = *it;
1218
1219     if( ModifyEvent::TEXT_REPLACED == event.type )
1220     {
1221       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1222       DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
1223
1224       TextReplacedEvent();
1225     }
1226     else if( ModifyEvent::TEXT_INSERTED == event.type )
1227     {
1228       TextInsertedEvent();
1229     }
1230     else if( ModifyEvent::TEXT_DELETED == event.type )
1231     {
1232       // Placeholder-text cannot be deleted
1233       if( !mImpl->IsShowingPlaceholderText() )
1234       {
1235         TextDeletedEvent();
1236       }
1237     }
1238   }
1239
1240   if( NULL != mImpl->mEventData )
1241   {
1242     // When the text is being modified, delay cursor blinking
1243     mImpl->mEventData->mDecorator->DelayCursorBlink();
1244   }
1245
1246   // Discard temporary text
1247   events.Clear();
1248 }
1249
1250 void Controller::ResetText()
1251 {
1252   // Reset buffers.
1253   mImpl->mLogicalModel->mText.Clear();
1254
1255   // We have cleared everything including the placeholder-text
1256   mImpl->PlaceholderCleared();
1257
1258   mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
1259   mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
1260   mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
1261
1262   // Clear any previous text.
1263   mImpl->mTextUpdateInfo.mClearAll = true;
1264
1265   // The natural size needs to be re-calculated.
1266   mImpl->mRecalculateNaturalSize = true;
1267
1268   // Apply modifications to the model
1269   mImpl->mOperationsPending = ALL_OPERATIONS;
1270 }
1271
1272 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
1273 {
1274   // Reset the cursor position
1275   if( NULL != mImpl->mEventData )
1276   {
1277     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
1278
1279     // Update the cursor if it's in editing mode.
1280     if( EventData::IsEditingState( mImpl->mEventData->mState )  )
1281     {
1282       mImpl->mEventData->mUpdateCursorPosition = true;
1283     }
1284   }
1285 }
1286
1287 void Controller::ResetScrollPosition()
1288 {
1289   if( NULL != mImpl->mEventData )
1290   {
1291     // Reset the scroll position.
1292     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
1293     mImpl->mEventData->mScrollAfterUpdatePosition = true;
1294   }
1295 }
1296
1297 void Controller::TextReplacedEvent()
1298 {
1299   // The natural size needs to be re-calculated.
1300   mImpl->mRecalculateNaturalSize = true;
1301
1302   // Apply modifications to the model
1303   mImpl->mOperationsPending = ALL_OPERATIONS;
1304 }
1305
1306 void Controller::TextInsertedEvent()
1307 {
1308   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
1309
1310   if( NULL == mImpl->mEventData )
1311   {
1312     return;
1313   }
1314
1315   // The natural size needs to be re-calculated.
1316   mImpl->mRecalculateNaturalSize = true;
1317
1318   // Apply modifications to the model; TODO - Optimize this
1319   mImpl->mOperationsPending = ALL_OPERATIONS;
1320
1321   // Queue a cursor reposition event; this must wait until after DoRelayout()
1322   if( EventData::IsEditingState( mImpl->mEventData->mState ) )
1323   {
1324     mImpl->mEventData->mUpdateCursorPosition = true;
1325     mImpl->mEventData->mScrollAfterUpdatePosition = true;
1326   }
1327 }
1328
1329 void Controller::TextDeletedEvent()
1330 {
1331   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
1332
1333   if( NULL == mImpl->mEventData )
1334   {
1335     return;
1336   }
1337
1338   // The natural size needs to be re-calculated.
1339   mImpl->mRecalculateNaturalSize = true;
1340
1341   // Apply modifications to the model; TODO - Optimize this
1342   mImpl->mOperationsPending = ALL_OPERATIONS;
1343
1344   // Queue a cursor reposition event; this must wait until after DoRelayout()
1345   mImpl->mEventData->mUpdateCursorPosition = true;
1346   if( 0u != mImpl->mLogicalModel->mText.Count() )
1347   {
1348     mImpl->mEventData->mScrollAfterDelete = true;
1349   }
1350 }
1351
1352 bool Controller::DoRelayout( const Size& size,
1353                              OperationsMask operationsRequired,
1354                              Size& layoutSize )
1355 {
1356   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
1357   bool viewUpdated( false );
1358
1359   // Calculate the operations to be done.
1360   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1361
1362   const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
1363   const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
1364
1365   if( LAYOUT & operations )
1366   {
1367     // Some vectors with data needed to layout and reorder may be void
1368     // after the first time the text has been laid out.
1369     // Fill the vectors again.
1370
1371     // Calculate the number of glyphs to layout.
1372     const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
1373     const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
1374     const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
1375     const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
1376
1377     const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
1378     const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
1379     const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
1380     const Length totalNumberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
1381
1382     if( 0u == totalNumberOfGlyphs )
1383     {
1384       if( UPDATE_ACTUAL_SIZE & operations )
1385       {
1386         mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
1387       }
1388
1389       // Nothing else to do if there is no glyphs.
1390       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
1391       return true;
1392     }
1393
1394     const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1395     const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1396     const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
1397     const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1398     const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1399     const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1400     const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
1401
1402     // Set the layout parameters.
1403     LayoutParameters layoutParameters( size,
1404                                        textBuffer,
1405                                        lineBreakInfo.Begin(),
1406                                        wordBreakInfo.Begin(),
1407                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
1408                                        glyphs.Begin(),
1409                                        glyphsToCharactersMap.Begin(),
1410                                        charactersPerGlyph.Begin(),
1411                                        charactersToGlyphBuffer,
1412                                        glyphsPerCharacterBuffer,
1413                                        totalNumberOfGlyphs );
1414
1415     // Resize the vector of positions to have the same size than the vector of glyphs.
1416     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1417     glyphPositions.Resize( totalNumberOfGlyphs );
1418
1419     // Whether the last character is a new paragraph character.
1420     mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph =  TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
1421     layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
1422
1423     // The initial glyph and the number of glyphs to layout.
1424     layoutParameters.startGlyphIndex = startGlyphIndex;
1425     layoutParameters.numberOfGlyphs = numberOfGlyphs;
1426     layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
1427     layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
1428
1429     // Update the visual model.
1430     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1431                                                    glyphPositions,
1432                                                    mImpl->mVisualModel->mLines,
1433                                                    layoutSize );
1434
1435     if( viewUpdated )
1436     {
1437       // Reorder the lines
1438       if( REORDER & operations )
1439       {
1440         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1441         Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
1442
1443         // Check first if there are paragraphs with bidirectional info.
1444         if( 0u != bidirectionalInfo.Count() )
1445         {
1446           // Get the lines
1447           const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
1448
1449           // Reorder the lines.
1450           bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1451           ReorderLines( bidirectionalInfo,
1452                         startIndex,
1453                         requestedNumberOfCharacters,
1454                         mImpl->mVisualModel->mLines,
1455                         bidirectionalLineInfo );
1456
1457           // Set the bidirectional info per line into the layout parameters.
1458           layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
1459           layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
1460
1461           // TODO: update the conversion map instead creating it from scratch.
1462           //       Note this tables store indices to characters, so update the table means modify all the indices of the text after the last updated character.
1463           //       It's better to refactor this. Store this table per line and don't update the indices.
1464           //       For the cursor position probably is better to use the function instead creating a table.
1465           // Set the bidirectional info into the model.
1466           mImpl->mLogicalModel->SetVisualToLogicalMap( layoutParameters.lineBidirectionalInfoRunsBuffer,
1467                                                        layoutParameters.numberOfBidirectionalInfoRuns,
1468                                                        0u,
1469                                                        mImpl->mLogicalModel->mText.Count() );
1470
1471           // Re-layout the text. Reorder those lines with right to left characters.
1472           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1473                                                          startIndex,
1474                                                          requestedNumberOfCharacters,
1475                                                          glyphPositions );
1476
1477         }
1478       } // REORDER
1479
1480       // Sets the actual size.
1481       if( UPDATE_ACTUAL_SIZE & operations )
1482       {
1483         mImpl->mVisualModel->SetLayoutSize( layoutSize );
1484       }
1485     } // view updated
1486
1487     // Store the size used to layout the text.
1488     mImpl->mVisualModel->mControlSize = size;
1489   }
1490   else
1491   {
1492     layoutSize = mImpl->mVisualModel->GetLayoutSize();
1493   }
1494
1495   if( ALIGN & operations )
1496   {
1497     // The laid-out lines.
1498     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1499
1500     mImpl->mLayoutEngine.Align( size,
1501                                 startIndex,
1502                                 requestedNumberOfCharacters,
1503                                 lines );
1504
1505     viewUpdated = true;
1506   }
1507
1508   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
1509   return viewUpdated;
1510 }
1511
1512 void Controller::SetMultiLineEnabled( bool enable )
1513 {
1514   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
1515
1516   if( layout != mImpl->mLayoutEngine.GetLayout() )
1517   {
1518     // Set the layout type.
1519     mImpl->mLayoutEngine.SetLayout( layout );
1520
1521     // Set the flags to redo the layout operations
1522     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
1523                                                                           UPDATE_ACTUAL_SIZE |
1524                                                                           ALIGN              |
1525                                                                           REORDER );
1526
1527     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
1528
1529     mImpl->RequestRelayout();
1530   }
1531 }
1532
1533 bool Controller::IsMultiLineEnabled() const
1534 {
1535   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
1536 }
1537
1538 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
1539 {
1540   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1541   {
1542     // Set the alignment.
1543     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1544
1545     // Set the flag to redo the alignment operation.
1546     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1547
1548     mImpl->RequestRelayout();
1549   }
1550 }
1551
1552 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1553 {
1554   return mImpl->mLayoutEngine.GetHorizontalAlignment();
1555 }
1556
1557 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1558 {
1559   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1560   {
1561     // Set the alignment.
1562     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1563
1564     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1565
1566     mImpl->RequestRelayout();
1567   }
1568 }
1569
1570 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1571 {
1572   return mImpl->mLayoutEngine.GetVerticalAlignment();
1573 }
1574
1575 void Controller::CalculateTextAlignment( const Size& controlSize )
1576 {
1577   Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
1578
1579   if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
1580   {
1581     // Get the line height of the default font.
1582     layoutSize.height = mImpl->GetDefaultFontLineHeight();
1583   }
1584
1585   if( LayoutEngine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
1586   {
1587     // Get the direction of the first character.
1588     const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1589
1590     // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1591     LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1592     if( firstParagraphDirection )
1593     {
1594       switch( horizontalAlignment )
1595       {
1596         case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1597         {
1598           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1599           break;
1600         }
1601         case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1602         {
1603           // Nothing to do.
1604           break;
1605         }
1606         case LayoutEngine::HORIZONTAL_ALIGN_END:
1607         {
1608           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1609           break;
1610         }
1611       }
1612     }
1613
1614     switch( horizontalAlignment )
1615     {
1616       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1617       {
1618         mImpl->mAlignmentOffset.x = 0.f;
1619         break;
1620       }
1621       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1622       {
1623         mImpl->mAlignmentOffset.x = floorf( 0.5f * ( controlSize.width - layoutSize.width ) ); // try to avoid pixel alignment.
1624         break;
1625       }
1626       case LayoutEngine::HORIZONTAL_ALIGN_END:
1627       {
1628         mImpl->mAlignmentOffset.x = controlSize.width - layoutSize.width;
1629         break;
1630       }
1631     }
1632   }
1633
1634   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1635   switch( verticalAlignment )
1636   {
1637     case LayoutEngine::VERTICAL_ALIGN_TOP:
1638     {
1639       mImpl->mAlignmentOffset.y = 0.f;
1640       break;
1641     }
1642     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1643     {
1644       mImpl->mAlignmentOffset.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
1645       break;
1646     }
1647     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1648     {
1649       mImpl->mAlignmentOffset.y = controlSize.height - layoutSize.height;
1650       break;
1651     }
1652   }
1653 }
1654
1655 LayoutEngine& Controller::GetLayoutEngine()
1656 {
1657   return mImpl->mLayoutEngine;
1658 }
1659
1660 View& Controller::GetView()
1661 {
1662   return mImpl->mView;
1663 }
1664
1665 void Controller::KeyboardFocusGainEvent()
1666 {
1667   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1668
1669   if( NULL != mImpl->mEventData )
1670   {
1671     if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1672         ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1673     {
1674       mImpl->ChangeState( EventData::EDITING );
1675       mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1676     }
1677
1678     if( mImpl->IsShowingPlaceholderText() )
1679     {
1680       // Show alternative placeholder-text when editing
1681       ShowPlaceholderText();
1682     }
1683
1684     mImpl->RequestRelayout();
1685   }
1686 }
1687
1688 void Controller::KeyboardFocusLostEvent()
1689 {
1690   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1691
1692   if( NULL != mImpl->mEventData )
1693   {
1694     if( EventData::INTERRUPTED != mImpl->mEventData->mState )
1695     {
1696       mImpl->ChangeState( EventData::INACTIVE );
1697
1698       if( !mImpl->IsShowingRealText() )
1699       {
1700         // Revert to regular placeholder-text when not editing
1701         ShowPlaceholderText();
1702       }
1703     }
1704   }
1705   mImpl->RequestRelayout();
1706 }
1707
1708 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1709 {
1710   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1711
1712   bool textChanged( false );
1713
1714   if( ( NULL != mImpl->mEventData ) &&
1715       ( keyEvent.state == KeyEvent::Down ) )
1716   {
1717     int keyCode = keyEvent.keyCode;
1718     const std::string& keyString = keyEvent.keyPressed;
1719
1720     // Pre-process to separate modifying events from non-modifying input events.
1721     if( Dali::DALI_KEY_ESCAPE == keyCode )
1722     {
1723       // Escape key is a special case which causes focus loss
1724       KeyboardFocusLostEvent();
1725     }
1726     else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
1727              ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
1728              ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
1729              ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
1730     {
1731       Event event( Event::CURSOR_KEY_EVENT );
1732       event.p1.mInt = keyCode;
1733       mImpl->mEventData->mEventQueue.push_back( event );
1734     }
1735     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1736     {
1737       textChanged = BackspaceKeyEvent();
1738     }
1739     else if( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
1740     {
1741       mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
1742       // Avoids calling the InsertText() method which can delete selected text
1743     }
1744     else if( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
1745              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
1746     {
1747       mImpl->ChangeState( EventData::INACTIVE );
1748       // Menu/Home key behaviour does not allow edit mode to resume like Power key
1749       // Avoids calling the InsertText() method which can delete selected text
1750     }
1751     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
1752     {
1753       // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
1754       // and a character is typed after the type of a upper case latin character.
1755
1756       // Do nothing.
1757     }
1758     else
1759     {
1760       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1761
1762       // IMF manager is no longer handling key-events
1763       mImpl->ClearPreEditFlag();
1764
1765       InsertText( keyString, COMMIT );
1766       textChanged = true;
1767     }
1768
1769     if( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
1770         ( mImpl->mEventData->mState != EventData::INACTIVE ) )
1771     {
1772       mImpl->ChangeState( EventData::EDITING );
1773     }
1774
1775     mImpl->RequestRelayout();
1776   }
1777
1778   if( textChanged )
1779   {
1780     // Do this last since it provides callbacks into application code
1781     mImpl->mControlInterface.TextChanged();
1782   }
1783
1784   return true;
1785 }
1786
1787 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1788 {
1789   bool removedPrevious( false );
1790   bool maxLengthReached( false );
1791
1792   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1793
1794   if( NULL == mImpl->mEventData )
1795   {
1796     return;
1797   }
1798
1799   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1800                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1801                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1802
1803   // TODO: At the moment the underline runs are only for pre-edit.
1804   mImpl->mVisualModel->mUnderlineRuns.Clear();
1805
1806   Vector<Character> utf32Characters;
1807   Length characterCount( 0u );
1808
1809   // Remove the previous IMF pre-edit (predicitive text)
1810   if( mImpl->mEventData->mPreEditFlag &&
1811       ( 0u != mImpl->mEventData->mPreEditLength ) )
1812   {
1813     const CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1814
1815     removedPrevious = RemoveText( -static_cast<int>( offset ),
1816                                   mImpl->mEventData->mPreEditLength,
1817                                   DONT_UPDATE_INPUT_STYLE );
1818
1819     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1820     mImpl->mEventData->mPreEditLength = 0u;
1821   }
1822   else
1823   {
1824     // Remove the previous Selection
1825     removedPrevious = RemoveSelectedText();
1826   }
1827
1828   if( !text.empty() )
1829   {
1830     //  Convert text into UTF-32
1831     utf32Characters.Resize( text.size() );
1832
1833     // This is a bit horrible but std::string returns a (signed) char*
1834     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1835
1836     // Transform a text array encoded in utf8 into an array encoded in utf32.
1837     // It returns the actual number of characters.
1838     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1839     utf32Characters.Resize( characterCount );
1840
1841     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1842     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1843   }
1844
1845   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1846   {
1847     // The placeholder text is no longer needed
1848     if( mImpl->IsShowingPlaceholderText() )
1849     {
1850       ResetText();
1851     }
1852
1853     mImpl->ChangeState( EventData::EDITING );
1854
1855     // Handle the IMF (predicitive text) state changes
1856     if( COMMIT == type )
1857     {
1858       // IMF manager is no longer handling key-events
1859       mImpl->ClearPreEditFlag();
1860     }
1861     else // PRE_EDIT
1862     {
1863       if( !mImpl->mEventData->mPreEditFlag )
1864       {
1865         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1866
1867         // Record the start of the pre-edit text
1868         mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1869       }
1870
1871       mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1872       mImpl->mEventData->mPreEditFlag = true;
1873
1874       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1875     }
1876
1877     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1878
1879     // Restrict new text to fit within Maximum characters setting.
1880     Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1881     maxLengthReached = ( characterCount > maxSizeOfNewText );
1882
1883     // The cursor position.
1884     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1885
1886     // Update the text's style.
1887
1888     // Updates the text style runs by adding characters.
1889     mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
1890
1891     // Get the character index from the cursor index.
1892     const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
1893
1894     // Retrieve the text's style for the given index.
1895     InputStyle style;
1896     mImpl->RetrieveDefaultInputStyle( style );
1897     mImpl->mLogicalModel->RetrieveStyle( styleIndex, style );
1898
1899     // Whether to add a new text color run.
1900     const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
1901
1902     // Whether to add a new font run.
1903     const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
1904     const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
1905     const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
1906     const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
1907     const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
1908
1909     // Add style runs.
1910     if( addColorRun )
1911     {
1912       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
1913       mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
1914
1915       ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
1916       colorRun.color = mImpl->mEventData->mInputStyle.textColor;
1917       colorRun.characterRun.characterIndex = cursorIndex;
1918       colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
1919     }
1920
1921     if( addFontNameRun   ||
1922         addFontWeightRun ||
1923         addFontWidthRun  ||
1924         addFontSlantRun  ||
1925         addFontSizeRun )
1926     {
1927       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mFontDescriptionRuns.Count();
1928       mImpl->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
1929
1930       FontDescriptionRun& fontDescriptionRun = *( mImpl->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
1931
1932       if( addFontNameRun )
1933       {
1934         fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
1935         fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
1936         memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
1937         fontDescriptionRun.familyDefined = true;
1938
1939         // The memory allocated for the font family name is freed when the font description is removed from the logical model.
1940       }
1941
1942       if( addFontWeightRun )
1943       {
1944         fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
1945         fontDescriptionRun.weightDefined = true;
1946       }
1947
1948       if( addFontWidthRun )
1949       {
1950         fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
1951         fontDescriptionRun.widthDefined = true;
1952       }
1953
1954       if( addFontSlantRun )
1955       {
1956         fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
1957         fontDescriptionRun.slantDefined = true;
1958       }
1959
1960       if( addFontSizeRun )
1961       {
1962         fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
1963         fontDescriptionRun.sizeDefined = true;
1964       }
1965
1966       fontDescriptionRun.characterRun.characterIndex = cursorIndex;
1967       fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
1968     }
1969
1970     // Insert at current cursor position.
1971     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1972
1973     if( cursorIndex < numberOfCharactersInModel )
1974     {
1975       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1976     }
1977     else
1978     {
1979       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1980     }
1981
1982     // Mark the first paragraph to be updated.
1983     mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
1984     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
1985
1986     // Update the cursor index.
1987     cursorIndex += maxSizeOfNewText;
1988
1989     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1990   }
1991
1992   if( ( 0u == mImpl->mLogicalModel->mText.Count() ) &&
1993       mImpl->IsPlaceholderAvailable() )
1994   {
1995     // Show place-holder if empty after removing the pre-edit text
1996     ShowPlaceholderText();
1997     mImpl->mEventData->mUpdateCursorPosition = true;
1998     mImpl->ClearPreEditFlag();
1999   }
2000   else if( removedPrevious ||
2001            ( 0 != utf32Characters.Count() ) )
2002   {
2003     // Queue an inserted event
2004     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
2005   }
2006
2007   if( maxLengthReached )
2008   {
2009     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
2010
2011     mImpl->ResetImfManager();
2012
2013     // Do this last since it provides callbacks into application code
2014     mImpl->mControlInterface.MaxLengthReached();
2015   }
2016 }
2017
2018 bool Controller::RemoveSelectedText()
2019 {
2020   bool textRemoved( false );
2021
2022   if( EventData::SELECTING == mImpl->mEventData->mState )
2023   {
2024     std::string removedString;
2025     mImpl->RetrieveSelection( removedString, true );
2026
2027     if( !removedString.empty() )
2028     {
2029       textRemoved = true;
2030       mImpl->ChangeState( EventData::EDITING );
2031     }
2032   }
2033
2034   return textRemoved;
2035 }
2036
2037 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2038 {
2039   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
2040
2041   if( NULL != mImpl->mEventData )
2042   {
2043     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
2044
2045     if( 1u == tapCount )
2046     {
2047       // This is to avoid unnecessary relayouts when tapping an empty text-field
2048       bool relayoutNeeded( false );
2049
2050       if( ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) ||
2051           ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
2052       {
2053         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE);  // If Popup shown hide it here so can be shown again if required.
2054       }
2055
2056       if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != mImpl->mEventData->mState ) )
2057       {
2058         // Already in an active state so show a popup
2059         if( !mImpl->IsClipboardEmpty() )
2060         {
2061           // Shows Paste popup but could show full popup with Selection options. ( EDITING_WITH_POPUP )
2062           mImpl->ChangeState( EventData::EDITING_WITH_PASTE_POPUP );
2063         }
2064         else
2065         {
2066           mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2067         }
2068         relayoutNeeded = true;
2069       }
2070       else
2071       {
2072         if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
2073         {
2074           // Hide placeholder text
2075           ResetText();
2076         }
2077
2078         if( EventData::INACTIVE == mImpl->mEventData->mState )
2079         {
2080           mImpl->ChangeState( EventData::EDITING );
2081         }
2082         else if( !mImpl->IsClipboardEmpty() )
2083         {
2084           mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
2085         }
2086         relayoutNeeded = true;
2087       }
2088
2089       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
2090       if( relayoutNeeded )
2091       {
2092         Event event( Event::TAP_EVENT );
2093         event.p1.mUint = tapCount;
2094         event.p2.mFloat = x;
2095         event.p3.mFloat = y;
2096         mImpl->mEventData->mEventQueue.push_back( event );
2097
2098         mImpl->RequestRelayout();
2099       }
2100     }
2101     else if( 2u == tapCount )
2102     {
2103       if( mImpl->mEventData->mSelectionEnabled &&
2104           mImpl->IsShowingRealText() )
2105       {
2106         SelectEvent( x, y, false );
2107       }
2108     }
2109   }
2110
2111   // Reset keyboard as tap event has occurred.
2112   mImpl->ResetImfManager();
2113 }
2114
2115 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2116         // Show cursor and grabhandle on first tap, this matches the behaviour of tapping when already editing
2117 {
2118   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
2119
2120   if( NULL != mImpl->mEventData )
2121   {
2122     Event event( Event::PAN_EVENT );
2123     event.p1.mInt = state;
2124     event.p2.mFloat = displacement.x;
2125     event.p3.mFloat = displacement.y;
2126     mImpl->mEventData->mEventQueue.push_back( event );
2127
2128     mImpl->RequestRelayout();
2129   }
2130 }
2131
2132 void Controller::LongPressEvent( Gesture::State state, float x, float y  )
2133 {
2134   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
2135
2136   if( ( state == Gesture::Started ) &&
2137       ( NULL != mImpl->mEventData ) )
2138   {
2139     if( !mImpl->IsShowingRealText() )
2140     {
2141       Event event( Event::LONG_PRESS_EVENT );
2142       event.p1.mInt = state;
2143       mImpl->mEventData->mEventQueue.push_back( event );
2144       mImpl->RequestRelayout();
2145     }
2146     else
2147     {
2148       // The 1st long-press on inactive text-field is treated as tap
2149       if( EventData::INACTIVE == mImpl->mEventData->mState )
2150       {
2151         mImpl->ChangeState( EventData::EDITING );
2152
2153         Event event( Event::TAP_EVENT );
2154         event.p1.mUint = 1;
2155         event.p2.mFloat = x;
2156         event.p3.mFloat = y;
2157         mImpl->mEventData->mEventQueue.push_back( event );
2158
2159         mImpl->RequestRelayout();
2160       }
2161       else
2162       {
2163         // Reset the imf manger to commit the pre-edit before selecting the text.
2164         mImpl->ResetImfManager();
2165
2166         SelectEvent( x, y, false );
2167       }
2168     }
2169   }
2170 }
2171
2172 void Controller::SelectEvent( float x, float y, bool selectAll )
2173 {
2174   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
2175
2176   if( NULL != mImpl->mEventData )
2177   {
2178     mImpl->ChangeState( EventData::SELECTING );
2179
2180     if( selectAll )
2181     {
2182       Event event( Event::SELECT_ALL );
2183       mImpl->mEventData->mEventQueue.push_back( event );
2184     }
2185     else
2186     {
2187       Event event( Event::SELECT );
2188       event.p2.mFloat = x;
2189       event.p3.mFloat = y;
2190       mImpl->mEventData->mEventQueue.push_back( event );
2191     }
2192
2193     mImpl->RequestRelayout();
2194   }
2195 }
2196
2197 void Controller::GetTargetSize( Vector2& targetSize )
2198 {
2199   targetSize = mImpl->mVisualModel->mControlSize;
2200 }
2201
2202 void Controller::AddDecoration( Actor& actor, bool needsClipping )
2203 {
2204   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
2205 }
2206
2207 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
2208 {
2209   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
2210
2211   if( NULL != mImpl->mEventData )
2212   {
2213     switch( handleType )
2214     {
2215       case GRAB_HANDLE:
2216       {
2217         Event event( Event::GRAB_HANDLE_EVENT );
2218         event.p1.mUint  = state;
2219         event.p2.mFloat = x;
2220         event.p3.mFloat = y;
2221
2222         mImpl->mEventData->mEventQueue.push_back( event );
2223         break;
2224       }
2225       case LEFT_SELECTION_HANDLE:
2226       {
2227         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
2228         event.p1.mUint  = state;
2229         event.p2.mFloat = x;
2230         event.p3.mFloat = y;
2231
2232         mImpl->mEventData->mEventQueue.push_back( event );
2233         break;
2234       }
2235       case RIGHT_SELECTION_HANDLE:
2236       {
2237         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
2238         event.p1.mUint  = state;
2239         event.p2.mFloat = x;
2240         event.p3.mFloat = y;
2241
2242         mImpl->mEventData->mEventQueue.push_back( event );
2243         break;
2244       }
2245       case LEFT_SELECTION_HANDLE_MARKER:
2246       case RIGHT_SELECTION_HANDLE_MARKER:
2247       {
2248         // Markers do not move the handles.
2249         break;
2250       }
2251       case HANDLE_TYPE_COUNT:
2252       {
2253         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
2254       }
2255     }
2256
2257     mImpl->RequestRelayout();
2258   }
2259 }
2260
2261 void Controller::PasteText( const std::string& stringToPaste )
2262 {
2263   InsertText( stringToPaste, Text::Controller::COMMIT );
2264   mImpl->ChangeState( EventData::EDITING );
2265   mImpl->RequestRelayout();
2266
2267   // Do this last since it provides callbacks into application code
2268   mImpl->mControlInterface.TextChanged();
2269 }
2270
2271 void Controller::PasteClipboardItemEvent()
2272 {
2273   // Retrieve the clipboard contents first
2274   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
2275   std::string stringToPaste( notifier.GetContent() );
2276
2277   // Commit the current pre-edit text; the contents of the clipboard should be appended
2278   mImpl->ResetImfManager();
2279
2280   // Paste
2281   PasteText( stringToPaste );
2282 }
2283
2284 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
2285 {
2286   if( NULL == mImpl->mEventData )
2287   {
2288     return;
2289   }
2290
2291   switch( button )
2292   {
2293     case Toolkit::TextSelectionPopup::CUT:
2294     {
2295       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
2296       mImpl->mOperationsPending = ALL_OPERATIONS;
2297
2298       // This is to reset the virtual keyboard to Upper-case
2299       if( 0u == mImpl->mLogicalModel->mText.Count() )
2300       {
2301         NotifyImfManager();
2302       }
2303
2304       if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2305           !mImpl->IsPlaceholderAvailable() )
2306       {
2307         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2308       }
2309       else
2310       {
2311         ShowPlaceholderText();
2312         mImpl->mEventData->mUpdateCursorPosition = true;
2313       }
2314       mImpl->RequestRelayout();
2315       mImpl->mControlInterface.TextChanged();
2316       break;
2317     }
2318     case Toolkit::TextSelectionPopup::COPY:
2319     {
2320       mImpl->SendSelectionToClipboard( false ); // Text not modified
2321       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
2322       break;
2323     }
2324     case Toolkit::TextSelectionPopup::PASTE:
2325     {
2326       std::string stringToPaste("");
2327       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
2328       PasteText( stringToPaste );
2329       break;
2330     }
2331     case Toolkit::TextSelectionPopup::SELECT:
2332     {
2333       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2334
2335       if( mImpl->mEventData->mSelectionEnabled )
2336       {
2337         // Creates a SELECT event.
2338         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
2339       }
2340       break;
2341     }
2342     case Toolkit::TextSelectionPopup::SELECT_ALL:
2343     {
2344       // Creates a SELECT_ALL event
2345       SelectEvent( 0.f, 0.f, true );
2346       break;
2347     }
2348     case Toolkit::TextSelectionPopup::CLIPBOARD:
2349     {
2350       mImpl->ShowClipboard();
2351       break;
2352     }
2353     case Toolkit::TextSelectionPopup::NONE:
2354     {
2355       // Nothing to do.
2356       break;
2357     }
2358   }
2359 }
2360
2361 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2362 {
2363   bool update = false;
2364   bool requestRelayout = false;
2365
2366   std::string text;
2367   unsigned int cursorPosition = 0u;
2368
2369   switch( imfEvent.eventName )
2370   {
2371     case ImfManager::COMMIT:
2372     {
2373       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
2374       update = true;
2375       requestRelayout = true;
2376       break;
2377     }
2378     case ImfManager::PREEDIT:
2379     {
2380       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
2381       update = true;
2382       requestRelayout = true;
2383       break;
2384     }
2385     case ImfManager::DELETESURROUNDING:
2386     {
2387       update = RemoveText( imfEvent.cursorOffset,
2388                            imfEvent.numberOfChars,
2389                            DONT_UPDATE_INPUT_STYLE );
2390
2391       if( update )
2392       {
2393         if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2394             !mImpl->IsPlaceholderAvailable() )
2395         {
2396           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2397         }
2398         else
2399         {
2400           ShowPlaceholderText();
2401           mImpl->mEventData->mUpdateCursorPosition = true;
2402         }
2403       }
2404       requestRelayout = true;
2405       break;
2406     }
2407     case ImfManager::GETSURROUNDING:
2408     {
2409       GetText( text );
2410       cursorPosition = GetLogicalCursorPosition();
2411
2412       imfManager.SetSurroundingText( text );
2413       imfManager.SetCursorPosition( cursorPosition );
2414       break;
2415     }
2416     case ImfManager::VOID:
2417     {
2418       // do nothing
2419       break;
2420     }
2421   } // end switch
2422
2423   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
2424   {
2425     GetText( text );
2426     cursorPosition = GetLogicalCursorPosition();
2427   }
2428
2429   if( requestRelayout )
2430   {
2431     mImpl->mOperationsPending = ALL_OPERATIONS;
2432     mImpl->RequestRelayout();
2433
2434     // Do this last since it provides callbacks into application code
2435     mImpl->mControlInterface.TextChanged();
2436   }
2437
2438   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
2439
2440   return callbackData;
2441 }
2442
2443 Controller::~Controller()
2444 {
2445   delete mImpl;
2446 }
2447
2448 bool Controller::BackspaceKeyEvent()
2449 {
2450   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
2451
2452   bool removed = false;
2453
2454   if( NULL == mImpl->mEventData )
2455   {
2456     return removed;
2457   }
2458
2459   // IMF manager is no longer handling key-events
2460   mImpl->ClearPreEditFlag();
2461
2462   if( EventData::SELECTING == mImpl->mEventData->mState )
2463   {
2464     removed = RemoveSelectedText();
2465   }
2466   else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
2467   {
2468     // Remove the character before the current cursor position
2469     removed = RemoveText( -1,
2470                           1,
2471                           UPDATE_INPUT_STYLE );
2472   }
2473
2474   if( removed )
2475   {
2476     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE RemovedText\n", this );
2477     // Notifiy the IMF manager after text changed
2478     // Automatic  Upper-case and restarting prediction on an existing word require this.
2479     NotifyImfManager();
2480
2481     if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2482         !mImpl->IsPlaceholderAvailable() )
2483     {
2484       mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2485     }
2486     else
2487     {
2488       ShowPlaceholderText();
2489       mImpl->mEventData->mUpdateCursorPosition = true;
2490     }
2491   }
2492
2493   return removed;
2494 }
2495
2496 void Controller::NotifyImfManager()
2497 {
2498   if( NULL != mImpl->mEventData )
2499   {
2500     if( mImpl->mEventData->mImfManager )
2501     {
2502       // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2503       std::string text;
2504       GetText( text );
2505       mImpl->mEventData->mImfManager.SetSurroundingText( text );
2506
2507       mImpl->mEventData->mImfManager.SetCursorPosition( GetLogicalCursorPosition() );
2508       mImpl->mEventData->mImfManager.NotifyCursorPosition();
2509     }
2510   }
2511 }
2512
2513 void Controller::ShowPlaceholderText()
2514 {
2515   if( mImpl->IsPlaceholderAvailable() )
2516   {
2517     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
2518
2519     if( NULL == mImpl->mEventData )
2520     {
2521       return;
2522     }
2523
2524     mImpl->mEventData->mIsShowingPlaceholderText = true;
2525
2526     // Disable handles when showing place-holder text
2527     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2528     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2529     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2530
2531     const char* text( NULL );
2532     size_t size( 0 );
2533
2534     // TODO - Switch placeholder text styles when changing state
2535     if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
2536         ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
2537     {
2538       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
2539       size = mImpl->mEventData->mPlaceholderTextActive.size();
2540     }
2541     else
2542     {
2543       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
2544       size = mImpl->mEventData->mPlaceholderTextInactive.size();
2545     }
2546
2547     mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2548     mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2549
2550     // Reset model for showing placeholder.
2551     mImpl->mLogicalModel->mText.Clear();
2552     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
2553
2554     // Convert text into UTF-32
2555     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
2556     utf32Characters.Resize( size );
2557
2558     // This is a bit horrible but std::string returns a (signed) char*
2559     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
2560
2561     // Transform a text array encoded in utf8 into an array encoded in utf32.
2562     // It returns the actual number of characters.
2563     const Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
2564     utf32Characters.Resize( characterCount );
2565
2566     // The characters to be added.
2567     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = characterCount;
2568
2569     // Reset the cursor position
2570     mImpl->mEventData->mPrimaryCursorPosition = 0;
2571
2572     // The natural size needs to be re-calculated.
2573     mImpl->mRecalculateNaturalSize = true;
2574
2575     // Apply modifications to the model
2576     mImpl->mOperationsPending = ALL_OPERATIONS;
2577
2578     // Update the rest of the model during size negotiation
2579     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
2580   }
2581 }
2582
2583 void Controller::ClearFontData()
2584 {
2585   if( mImpl->mFontDefaults )
2586   {
2587     mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
2588   }
2589   mImpl->mVisualModel->ClearCaches();
2590
2591   mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2592   mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2593   mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mLogicalModel->mText.Count();
2594
2595   mImpl->mTextUpdateInfo.mClearAll = true;
2596   mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2597                                                            VALIDATE_FONTS            |
2598                                                            SHAPE_TEXT                |
2599                                                            GET_GLYPH_METRICS         |
2600                                                            LAYOUT                    |
2601                                                            UPDATE_ACTUAL_SIZE        |
2602                                                            REORDER                   |
2603                                                            ALIGN );
2604 }
2605
2606 void Controller::ClearStyleData()
2607 {
2608   mImpl->mLogicalModel->mColorRuns.Clear();
2609   mImpl->mLogicalModel->ClearFontDescriptionRuns();
2610 }
2611
2612 Controller::Controller( ControlInterface& controlInterface )
2613 : mImpl( NULL )
2614 {
2615   mImpl = new Controller::Impl( controlInterface );
2616 }
2617
2618 } // namespace Text
2619
2620 } // namespace Toolkit
2621
2622 } // namespace Dali