64003704f151fd6d5bde29ad2eb81e158c91e5fa
[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 <iostream>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/script-run.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-controller-impl.h>
36 #include <dali-toolkit/internal/text/text-io.h>
37 #include <dali-toolkit/internal/text/text-view.h>
38
39 namespace
40 {
41
42 #if defined(DEBUG_ENABLED)
43   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
44 #endif
45
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47
48 const std::string EMPTY_STRING("");
49
50 } // namespace
51
52 namespace Dali
53 {
54
55 namespace Toolkit
56 {
57
58 namespace Text
59 {
60
61 ControllerPtr Controller::New( ControlInterface& controlInterface )
62 {
63   return ControllerPtr( new Controller( controlInterface ) );
64 }
65
66 void Controller::EnableTextInput( DecoratorPtr decorator )
67 {
68   if( !mImpl->mEventData )
69   {
70     mImpl->mEventData = new EventData( decorator );
71   }
72 }
73
74 void Controller::SetText( const std::string& text )
75 {
76   // Cancel previously queued inserts etc.
77   mImpl->mModifyEvents.clear();
78
79   // Remove the previously set text
80   ResetText();
81
82   if( ! text.empty() )
83   {
84     //  Convert text into UTF-32
85     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
86     utf32Characters.Resize( text.size() );
87
88     // This is a bit horrible but std::string returns a (signed) char*
89     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
90
91     // Transform a text array encoded in utf8 into an array encoded in utf32.
92     // It returns the actual number of characters.
93     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
94     utf32Characters.Resize( characterCount );
95
96     DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
97     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
98
99     // Reset the cursor position
100     if( mImpl->mEventData )
101     {
102       mImpl->mEventData->mPrimaryCursorPosition = characterCount;
103     }
104
105     // Update the rest of the model during size negotiation
106     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
107   }
108   else
109   {
110     ShowPlaceholderText();
111   }
112
113   if( mImpl->mEventData )
114   {
115     // Cancel previously queued events
116     mImpl->mEventData->mEventQueue.clear();
117   }
118
119   // Reset keyboard as text changed
120   mImpl->ResetImfManager();
121 }
122
123 void Controller::GetText( std::string& text ) const
124 {
125   if( ! mImpl->IsShowingPlaceholderText() )
126   {
127     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
128
129     if( 0u != utf32Characters.Count() )
130     {
131       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
132     }
133   }
134   else
135   {
136     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
137   }
138 }
139
140 unsigned int Controller::GetLogicalCursorPosition() const
141 {
142   if( mImpl->mEventData )
143   {
144     return mImpl->mEventData->mPrimaryCursorPosition;
145   }
146
147   return 0u;
148 }
149
150 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
151 {
152   if( mImpl->mEventData )
153   {
154     if( PLACEHOLDER_TYPE_INACTIVE == type )
155     {
156       mImpl->mEventData->mPlaceholderTextInactive = text;
157     }
158     else
159     {
160       mImpl->mEventData->mPlaceholderTextActive = text;
161     }
162
163     ShowPlaceholderText();
164   }
165 }
166
167 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
168 {
169   if( mImpl->mEventData )
170   {
171     if( PLACEHOLDER_TYPE_INACTIVE == type )
172     {
173       text = mImpl->mEventData->mPlaceholderTextInactive;
174     }
175     else
176     {
177       text = mImpl->mEventData->mPlaceholderTextActive;
178     }
179   }
180 }
181
182 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
183 {
184   if ( maxCharacters >= 0 )
185   {
186     mImpl->mMaximumNumberOfCharacters = maxCharacters;
187   }
188 }
189
190 int Controller::GetMaximumNumberOfCharacters()
191 {
192   return mImpl->mMaximumNumberOfCharacters;
193 }
194
195 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
196 {
197   if( !mImpl->mFontDefaults )
198   {
199     mImpl->mFontDefaults = new FontDefaults();
200   }
201
202   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
203
204   // Clear the font-specific data
205   ClearFontData();
206
207   mImpl->mOperationsPending = ALL_OPERATIONS;
208   mImpl->mRecalculateNaturalSize = true;
209
210   mImpl->RequestRelayout();
211 }
212
213 const std::string& Controller::GetDefaultFontFamily() const
214 {
215   if( mImpl->mFontDefaults )
216   {
217     return mImpl->mFontDefaults->mDefaultFontFamily;
218   }
219
220   return EMPTY_STRING;
221 }
222
223 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
224 {
225   if( !mImpl->mFontDefaults )
226   {
227     mImpl->mFontDefaults = new FontDefaults();
228   }
229
230   mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
231
232   // Clear the font-specific data
233   ClearFontData();
234
235   mImpl->mOperationsPending = ALL_OPERATIONS;
236   mImpl->mRecalculateNaturalSize = true;
237
238   mImpl->RequestRelayout();
239 }
240
241 const std::string& Controller::GetDefaultFontStyle() const
242 {
243   if( mImpl->mFontDefaults )
244   {
245     return mImpl->mFontDefaults->mDefaultFontStyle;
246   }
247
248   return EMPTY_STRING;
249 }
250
251 void Controller::SetDefaultPointSize( float pointSize )
252 {
253   if( !mImpl->mFontDefaults )
254   {
255     mImpl->mFontDefaults = new FontDefaults();
256   }
257
258   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
259
260   // Clear the font-specific data
261   ClearFontData();
262
263   mImpl->mOperationsPending = ALL_OPERATIONS;
264   mImpl->mRecalculateNaturalSize = true;
265
266   mImpl->RequestRelayout();
267 }
268
269 float Controller::GetDefaultPointSize() const
270 {
271   if( mImpl->mFontDefaults )
272   {
273     return mImpl->mFontDefaults->mDefaultPointSize;
274   }
275
276   return 0.0f;
277 }
278
279 void Controller::SetTextColor( const Vector4& textColor )
280 {
281   mImpl->mTextColor = textColor;
282
283   if( ! mImpl->IsShowingPlaceholderText() )
284   {
285     mImpl->mVisualModel->SetTextColor( textColor );
286   }
287 }
288
289 const Vector4& Controller::GetTextColor() const
290 {
291   return mImpl->mTextColor;
292 }
293
294 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
295 {
296   bool removed( false );
297
298   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
299                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
300
301   if( ! mImpl->IsShowingPlaceholderText() )
302   {
303     // Delete at current cursor position
304     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
305     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
306
307     CharacterIndex cursorIndex = oldCursorIndex;
308
309     // Validate the cursor position & number of characters
310     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
311     {
312       cursorIndex = oldCursorIndex + cursorOffset;
313     }
314
315     if( (cursorIndex + numberOfChars) > currentText.Count() )
316     {
317       numberOfChars = currentText.Count() - cursorIndex;
318     }
319
320     if( cursorIndex >= 0 &&
321         (cursorIndex + numberOfChars) <= currentText.Count() )
322     {
323       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
324       Vector<Character>::Iterator last  = first + numberOfChars;
325
326       currentText.Erase( first, last );
327
328       // Cursor position retreat
329       oldCursorIndex = cursorIndex;
330
331       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
332       removed = true;
333     }
334   }
335
336   return removed;
337 }
338
339 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
340 {
341   if( mImpl->mEventData )
342   {
343     mImpl->mEventData->mPlaceholderTextColor = textColor;
344   }
345
346   if( mImpl->IsShowingPlaceholderText() )
347   {
348     mImpl->mVisualModel->SetTextColor( textColor );
349   }
350 }
351
352 const Vector4& Controller::GetPlaceholderTextColor() const
353 {
354   if( mImpl->mEventData )
355   {
356     return mImpl->mEventData->mPlaceholderTextColor;
357   }
358
359   return Color::BLACK;
360 }
361
362 void Controller::SetShadowOffset( const Vector2& shadowOffset )
363 {
364   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
365 }
366
367 const Vector2& Controller::GetShadowOffset() const
368 {
369   return mImpl->mVisualModel->GetShadowOffset();
370 }
371
372 void Controller::SetShadowColor( const Vector4& shadowColor )
373 {
374   mImpl->mVisualModel->SetShadowColor( shadowColor );
375 }
376
377 const Vector4& Controller::GetShadowColor() const
378 {
379   return mImpl->mVisualModel->GetShadowColor();
380 }
381
382 void Controller::SetUnderlineColor( const Vector4& color )
383 {
384   mImpl->mVisualModel->SetUnderlineColor( color );
385 }
386
387 const Vector4& Controller::GetUnderlineColor() const
388 {
389   return mImpl->mVisualModel->GetUnderlineColor();
390 }
391
392 void Controller::SetUnderlineEnabled( bool enabled )
393 {
394   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
395 }
396
397 bool Controller::IsUnderlineEnabled() const
398 {
399   return mImpl->mVisualModel->IsUnderlineEnabled();
400 }
401
402 void Controller::SetUnderlineHeight( float height )
403 {
404   mImpl->mVisualModel->SetUnderlineHeight( height );
405 }
406
407 float Controller::GetUnderlineHeight() const
408 {
409   return mImpl->mVisualModel->GetUnderlineHeight();
410 }
411
412 void Controller::SetEnableCursorBlink( bool enable )
413 {
414   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
415
416   if( mImpl->mEventData )
417   {
418     mImpl->mEventData->mCursorBlinkEnabled = enable;
419
420     if( !enable &&
421         mImpl->mEventData->mDecorator )
422     {
423       mImpl->mEventData->mDecorator->StopCursorBlink();
424     }
425   }
426 }
427
428 bool Controller::GetEnableCursorBlink() const
429 {
430   if( mImpl->mEventData )
431   {
432     return mImpl->mEventData->mCursorBlinkEnabled;
433   }
434
435   return false;
436 }
437
438 const Vector2& Controller::GetScrollPosition() const
439 {
440   if( mImpl->mEventData )
441   {
442     return mImpl->mEventData->mScrollPosition;
443   }
444
445   return Vector2::ZERO;
446 }
447
448 const Vector2& Controller::GetAlignmentOffset() const
449 {
450   return mImpl->mAlignmentOffset;
451 }
452
453 Vector3 Controller::GetNaturalSize()
454 {
455   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
456   Vector3 naturalSize;
457
458   // Make sure the model is up-to-date before layouting
459   ProcessModifyEvents();
460
461   if( mImpl->mRecalculateNaturalSize )
462   {
463     // Operations that can be done only once until the text changes.
464     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
465                                                                            GET_SCRIPTS       |
466                                                                            VALIDATE_FONTS    |
467                                                                            GET_LINE_BREAKS   |
468                                                                            GET_WORD_BREAKS   |
469                                                                            BIDI_INFO         |
470                                                                            SHAPE_TEXT        |
471                                                                            GET_GLYPH_METRICS );
472     // Make sure the model is up-to-date before layouting
473     mImpl->UpdateModel( onlyOnceOperations );
474
475     // Operations that need to be done if the size changes.
476     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
477                                                                         ALIGN  |
478                                                                         REORDER );
479
480     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
481                 static_cast<OperationsMask>( onlyOnceOperations |
482                                              sizeOperations ),
483                 naturalSize.GetVectorXY() );
484
485     // Do not do again the only once operations.
486     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
487
488     // Do the size related operations again.
489     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
490
491     // Stores the natural size to avoid recalculate it again
492     // unless the text/style changes.
493     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
494
495     mImpl->mRecalculateNaturalSize = false;
496
497     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
498   }
499   else
500   {
501     naturalSize = mImpl->mVisualModel->GetNaturalSize();
502
503     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
504   }
505
506   return naturalSize;
507 }
508
509 float Controller::GetHeightForWidth( float width )
510 {
511   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
512   // Make sure the model is up-to-date before layouting
513   ProcessModifyEvents();
514
515   Size layoutSize;
516   if( width != mImpl->mControlSize.width )
517   {
518     // Operations that can be done only once until the text changes.
519     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
520                                                                            GET_SCRIPTS       |
521                                                                            VALIDATE_FONTS    |
522                                                                            GET_LINE_BREAKS   |
523                                                                            GET_WORD_BREAKS   |
524                                                                            BIDI_INFO         |
525                                                                            SHAPE_TEXT        |
526                                                                            GET_GLYPH_METRICS );
527     // Make sure the model is up-to-date before layouting
528     mImpl->UpdateModel( onlyOnceOperations );
529
530     // Operations that need to be done if the size changes.
531     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
532                                                                         ALIGN  |
533                                                                         REORDER );
534
535     DoRelayout( Size( width, MAX_FLOAT ),
536                 static_cast<OperationsMask>( onlyOnceOperations |
537                                              sizeOperations ),
538                 layoutSize );
539
540     // Do not do again the only once operations.
541     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
542
543     // Do the size related operations again.
544     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
545     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
546   }
547   else
548   {
549     layoutSize = mImpl->mVisualModel->GetActualSize();
550     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
551   }
552
553   return layoutSize.height;
554 }
555
556 bool Controller::Relayout( const Size& size )
557 {
558   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
559
560   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
561   {
562     bool glyphsRemoved( false );
563     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
564     {
565       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
566       glyphsRemoved = true;
567     }
568     // Not worth to relayout if width or height is equal to zero.
569     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
570     return glyphsRemoved;
571   }
572
573   if( size != mImpl->mControlSize )
574   {
575     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
576
577     // Operations that need to be done if the size changes.
578     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
579                                                              LAYOUT                    |
580                                                              ALIGN                     |
581                                                              UPDATE_ACTUAL_SIZE        |
582                                                              REORDER );
583
584     mImpl->mControlSize = size;
585   }
586
587   // Make sure the model is up-to-date before layouting
588   ProcessModifyEvents();
589   mImpl->UpdateModel( mImpl->mOperationsPending );
590
591   Size layoutSize;
592   bool updated = DoRelayout( mImpl->mControlSize,
593                              mImpl->mOperationsPending,
594                              layoutSize );
595
596   // Do not re-do any operation until something changes.
597   mImpl->mOperationsPending = NO_OPERATION;
598
599   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
600   CalculateTextAlignment( size );
601
602   if( mImpl->mEventData )
603   {
604     // Move the cursor, grab handle etc.
605     updated = mImpl->ProcessInputEvents() || updated;
606   }
607
608   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
609   return updated;
610 }
611
612 void Controller::ProcessModifyEvents()
613 {
614   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
615
616   for( unsigned int i=0; i<events.size(); ++i )
617   {
618     if( ModifyEvent::TEXT_REPLACED == events[0].type )
619     {
620       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
621       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
622
623       TextReplacedEvent();
624     }
625     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
626     {
627       TextInsertedEvent();
628     }
629     else if( ModifyEvent::TEXT_DELETED == events[0].type )
630     {
631       // Placeholder-text cannot be deleted
632       if( !mImpl->IsShowingPlaceholderText() )
633       {
634         TextDeletedEvent();
635       }
636     }
637   }
638
639   // Discard temporary text
640   events.clear();
641 }
642
643 void Controller::ResetText()
644 {
645   // Reset buffers.
646   mImpl->mLogicalModel->mText.Clear();
647   ClearModelData();
648
649   // Reset the cursor position
650   if( mImpl->mEventData )
651   {
652     mImpl->mEventData->mPrimaryCursorPosition = 0;
653   }
654
655   // We have cleared everything including the placeholder-text
656   mImpl->PlaceholderCleared();
657
658   // The natural size needs to be re-calculated.
659   mImpl->mRecalculateNaturalSize = true;
660
661   // Apply modifications to the model
662   mImpl->mOperationsPending = ALL_OPERATIONS;
663 }
664
665 void Controller::TextReplacedEvent()
666 {
667   // Reset buffers.
668   ClearModelData();
669
670   // The natural size needs to be re-calculated.
671   mImpl->mRecalculateNaturalSize = true;
672
673   // Apply modifications to the model
674   mImpl->mOperationsPending = ALL_OPERATIONS;
675   mImpl->UpdateModel( ALL_OPERATIONS );
676   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
677                                                            ALIGN              |
678                                                            UPDATE_ACTUAL_SIZE |
679                                                            REORDER );
680 }
681
682 void Controller::TextInsertedEvent()
683 {
684   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
685
686   // TODO - Optimize this
687   ClearModelData();
688
689   // The natural size needs to be re-calculated.
690   mImpl->mRecalculateNaturalSize = true;
691
692   // Apply modifications to the model; TODO - Optimize this
693   mImpl->mOperationsPending = ALL_OPERATIONS;
694   mImpl->UpdateModel( ALL_OPERATIONS );
695   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
696                                                            ALIGN              |
697                                                            UPDATE_ACTUAL_SIZE |
698                                                            REORDER );
699
700   // Queue a cursor reposition event; this must wait until after DoRelayout()
701   mImpl->mEventData->mUpdateCursorPosition = true;
702   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
703 }
704
705 void Controller::TextDeletedEvent()
706 {
707   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
708
709   // TODO - Optimize this
710   ClearModelData();
711
712   // The natural size needs to be re-calculated.
713   mImpl->mRecalculateNaturalSize = true;
714
715   // Apply modifications to the model; TODO - Optimize this
716   mImpl->mOperationsPending = ALL_OPERATIONS;
717   mImpl->UpdateModel( ALL_OPERATIONS );
718   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
719                                                            ALIGN              |
720                                                            UPDATE_ACTUAL_SIZE |
721                                                            REORDER );
722
723   // Queue a cursor reposition event; this must wait until after DoRelayout()
724   mImpl->mEventData->mUpdateCursorPosition = true;
725   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
726 }
727
728 bool Controller::DoRelayout( const Size& size,
729                              OperationsMask operationsRequired,
730                              Size& layoutSize )
731 {
732   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
733   bool viewUpdated( false );
734
735   // Calculate the operations to be done.
736   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
737
738   if( LAYOUT & operations )
739   {
740     // Some vectors with data needed to layout and reorder may be void
741     // after the first time the text has been laid out.
742     // Fill the vectors again.
743
744     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
745
746     if( 0u == numberOfGlyphs )
747     {
748       // Nothing else to do if there is no glyphs.
749       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
750       return true;
751     }
752
753     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
754     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
755     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
756     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
757     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
758     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
759
760     // Set the layout parameters.
761     LayoutParameters layoutParameters( size,
762                                        mImpl->mLogicalModel->mText.Begin(),
763                                        lineBreakInfo.Begin(),
764                                        wordBreakInfo.Begin(),
765                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
766                                        numberOfGlyphs,
767                                        glyphs.Begin(),
768                                        glyphsToCharactersMap.Begin(),
769                                        charactersPerGlyph.Begin() );
770
771     // The laid-out lines.
772     // It's not possible to know in how many lines the text is going to be laid-out,
773     // but it can be resized at least with the number of 'paragraphs' to avoid
774     // some re-allocations.
775     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
776
777     // Delete any previous laid out lines before setting the new ones.
778     lines.Clear();
779
780     // The capacity of the bidirectional paragraph info is the number of paragraphs.
781     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
782
783     // Resize the vector of positions to have the same size than the vector of glyphs.
784     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
785     glyphPositions.Resize( numberOfGlyphs );
786
787     // Update the visual model.
788     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
789                                                    glyphPositions,
790                                                    lines,
791                                                    layoutSize );
792
793     if( viewUpdated )
794     {
795       // Reorder the lines
796       if( REORDER & operations )
797       {
798         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
799
800         // Check first if there are paragraphs with bidirectional info.
801         if( 0u != bidirectionalInfo.Count() )
802         {
803           // Get the lines
804           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
805
806           // Reorder the lines.
807           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
808           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
809           ReorderLines( bidirectionalInfo,
810                         lines,
811                         lineBidirectionalInfoRuns );
812
813           // Set the bidirectional info into the model.
814           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
815           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
816                                                        numberOfBidirectionalInfoRuns );
817
818           // Set the bidirectional info per line into the layout parameters.
819           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
820           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
821
822           // Get the character to glyph conversion table and set into the layout.
823           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
824
825           // Get the glyphs per character table and set into the layout.
826           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
827
828           // Re-layout the text. Reorder those lines with right to left characters.
829           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
830                                                          glyphPositions );
831
832           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
833           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
834                  endIt = lineBidirectionalInfoRuns.End();
835                it != endIt;
836                ++it )
837           {
838             BidirectionalLineInfoRun& bidiLineInfo = *it;
839
840             free( bidiLineInfo.visualToLogicalMap );
841           }
842         }
843       } // REORDER
844
845       if( ALIGN & operations )
846       {
847         mImpl->mLayoutEngine.Align( layoutParameters,
848                                     layoutSize,
849                                     lines,
850                                     glyphPositions );
851       }
852
853       // Sets the actual size.
854       if( UPDATE_ACTUAL_SIZE & operations )
855       {
856         mImpl->mVisualModel->SetActualSize( layoutSize );
857       }
858     } // view updated
859   }
860   else
861   {
862     layoutSize = mImpl->mVisualModel->GetActualSize();
863   }
864
865   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
866   return viewUpdated;
867 }
868
869 void Controller::CalculateTextAlignment( const Size& size )
870 {
871   // Get the direction of the first character.
872   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
873
874   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
875
876   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
877   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
878   if( firstParagraphDirection &&
879       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
880   {
881     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
882     {
883       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
884     }
885     else
886     {
887       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
888     }
889   }
890
891   switch( horizontalAlignment )
892   {
893     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
894     {
895       mImpl->mAlignmentOffset.x = 0.f;
896       break;
897     }
898     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
899     {
900       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
901       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
902       break;
903     }
904     case LayoutEngine::HORIZONTAL_ALIGN_END:
905     {
906       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
907       break;
908     }
909   }
910
911   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
912   switch( verticalAlignment )
913   {
914     case LayoutEngine::VERTICAL_ALIGN_TOP:
915     {
916       mImpl->mAlignmentOffset.y = 0.f;
917       break;
918     }
919     case LayoutEngine::VERTICAL_ALIGN_CENTER:
920     {
921       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
922       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
923       break;
924     }
925     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
926     {
927       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
928       break;
929     }
930   }
931 }
932
933 LayoutEngine& Controller::GetLayoutEngine()
934 {
935   return mImpl->mLayoutEngine;
936 }
937
938 View& Controller::GetView()
939 {
940   return mImpl->mView;
941 }
942
943 void Controller::KeyboardFocusGainEvent()
944 {
945   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
946
947   if( mImpl->mEventData )
948   {
949     mImpl->ChangeState( EventData::EDITING );
950
951     if( mImpl->IsShowingPlaceholderText() )
952     {
953       // Show alternative placeholder-text when editing
954       ShowPlaceholderText();
955     }
956
957     mImpl->RequestRelayout();
958   }
959 }
960
961 void Controller::KeyboardFocusLostEvent()
962 {
963   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
964
965   if( mImpl->mEventData )
966   {
967     mImpl->ChangeState( EventData::INACTIVE );
968
969     if( mImpl->IsShowingPlaceholderText() )
970     {
971       // Revert to regular placeholder-text when not editing
972       ShowPlaceholderText();
973     }
974
975     mImpl->RequestRelayout();
976   }
977 }
978
979 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
980 {
981   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
982
983   if( mImpl->mEventData &&
984       keyEvent.state == KeyEvent::Down )
985   {
986     int keyCode = keyEvent.keyCode;
987     const std::string& keyString = keyEvent.keyPressed;
988
989     // Pre-process to separate modifying events from non-modifying input events.
990     if( Dali::DALI_KEY_ESCAPE == keyCode )
991     {
992       // Escape key is a special case which causes focus loss
993       KeyboardFocusLostEvent();
994     }
995     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
996              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
997              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
998              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
999     {
1000       Event event( Event::CURSOR_KEY_EVENT );
1001       event.p1.mInt = keyCode;
1002       mImpl->mEventData->mEventQueue.push_back( event );
1003     }
1004     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1005     {
1006       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1007
1008       // IMF manager is no longer handling key-events
1009       mImpl->ClearPreEditFlag();
1010
1011       // Remove the character before the current cursor position
1012       bool removed = RemoveText( -1, 1 );
1013
1014       if( removed )
1015       {
1016         if( 0u == mImpl->mLogicalModel->mText.Count() )
1017         {
1018           ShowPlaceholderText();
1019           mImpl->mEventData->mUpdateCursorPosition = true;
1020         }
1021         else
1022         {
1023           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1024         }
1025       }
1026     }
1027     else
1028     {
1029       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1030
1031       // IMF manager is no longer handling key-events
1032       mImpl->ClearPreEditFlag();
1033
1034       InsertText( keyString, COMMIT );
1035     }
1036
1037     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1038
1039     mImpl->RequestRelayout();
1040   }
1041
1042   return false;
1043 }
1044
1045 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1046 {
1047   bool removedPreEdit( false );
1048   bool maxLengthReached( false );
1049
1050   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1051   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1052                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1053                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1054
1055   Vector<Character> utf32Characters;
1056   Length characterCount( 0u );
1057
1058   if( ! text.empty() )
1059   {
1060     // The placeholder text is no longer needed
1061     if( mImpl->IsShowingPlaceholderText() )
1062     {
1063       ResetText();
1064     }
1065
1066     //  Convert text into UTF-32
1067     utf32Characters.Resize( text.size() );
1068
1069     // This is a bit horrible but std::string returns a (signed) char*
1070     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1071
1072     // Transform a text array encoded in utf8 into an array encoded in utf32.
1073     // It returns the actual number of characters.
1074     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1075     utf32Characters.Resize( characterCount );
1076
1077     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1078     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1079   }
1080
1081   if( 0u != utf32Characters.Count() )
1082   {
1083     // Handle the IMF (predicitive text) state changes
1084     if( mImpl->mEventData )
1085     {
1086       if( mImpl->mEventData->mPreEditFlag &&
1087           0 != mImpl->mEventData->mPreEditLength )
1088       {
1089         // Remove previous pre-edit text
1090         CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1091         removedPreEdit = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1092
1093         mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1094         mImpl->mEventData->mPreEditLength = 0;
1095       }
1096
1097       if( COMMIT == type )
1098       {
1099         // IMF manager is no longer handling key-events
1100         mImpl->ClearPreEditFlag();
1101       }
1102       else // PRE_EDIT
1103       {
1104         if( ! mImpl->mEventData->mPreEditFlag )
1105         {
1106           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1107
1108           // Record the start of the pre-edit text
1109           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1110         }
1111
1112         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1113         mImpl->mEventData->mPreEditFlag = true;
1114
1115         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1116       }
1117     }
1118
1119     const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1120
1121     // Restrict new text to fit within Maximum characters setting
1122     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1123     maxLengthReached = ( characterCount > maxSizeOfNewText );
1124
1125     // Insert at current cursor position
1126     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1127
1128     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1129
1130     if( cursorIndex < numberOfCharactersInModel )
1131     {
1132       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1133     }
1134     else
1135     {
1136       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1137     }
1138
1139     cursorIndex += maxSizeOfNewText;
1140
1141     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1142   }
1143
1144   if( removedPreEdit ||
1145       0 != utf32Characters.Count() )
1146   {
1147     // Queue an inserted event
1148     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1149   }
1150
1151   if( maxLengthReached )
1152   {
1153     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1154
1155     mImpl->mControlInterface.MaxLengthReached();
1156
1157     mImpl->ResetImfManager();
1158   }
1159 }
1160
1161 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1162 {
1163   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1164
1165   if( NULL != mImpl->mEventData )
1166   {
1167     if( 1u == tapCount )
1168     {
1169       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
1170
1171       if( ! mImpl->IsShowingPlaceholderText() &&
1172           EventData::EDITING == mImpl->mEventData->mState )
1173       {
1174         // Grab handle is not shown until a tap is received whilst EDITING
1175         if( tapDuringEditMode )
1176         {
1177           mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1178         }
1179         mImpl->mEventData->mDecorator->SetPopupActive( false );
1180       }
1181
1182       mImpl->ChangeState( EventData::EDITING );
1183     }
1184     else if( mImpl->mEventData->mSelectionEnabled &&
1185              ( 2u == tapCount ) )
1186     {
1187       mImpl->ChangeState( EventData::SELECTING );
1188     }
1189   }
1190
1191   // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1192   if( mImpl->mEventData )
1193   {
1194     Event event( Event::TAP_EVENT );
1195     event.p1.mUint = tapCount;
1196     event.p2.mFloat = x;
1197     event.p3.mFloat = y;
1198     mImpl->mEventData->mEventQueue.push_back( event );
1199
1200     mImpl->RequestRelayout();
1201   }
1202
1203   // Reset keyboard as tap event has occurred.
1204   mImpl->ResetImfManager();
1205 }
1206
1207 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1208 {
1209   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1210
1211   if( mImpl->mEventData )
1212   {
1213     Event event( Event::PAN_EVENT );
1214     event.p1.mInt = state;
1215     event.p2.mFloat = displacement.x;
1216     event.p3.mFloat = displacement.y;
1217     mImpl->mEventData->mEventQueue.push_back( event );
1218
1219     mImpl->RequestRelayout();
1220   }
1221 }
1222
1223 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1224 {
1225   DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1226
1227   if( mImpl->mEventData )
1228   {
1229     switch( handleType )
1230     {
1231       case GRAB_HANDLE:
1232       {
1233         Event event( Event::GRAB_HANDLE_EVENT );
1234         event.p1.mUint  = state;
1235         event.p2.mFloat = x;
1236         event.p3.mFloat = y;
1237
1238         mImpl->mEventData->mEventQueue.push_back( event );
1239         break;
1240       }
1241       case LEFT_SELECTION_HANDLE:
1242       {
1243         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1244         event.p1.mUint  = state;
1245         event.p2.mFloat = x;
1246         event.p3.mFloat = y;
1247
1248         mImpl->mEventData->mEventQueue.push_back( event );
1249         break;
1250       }
1251       case RIGHT_SELECTION_HANDLE:
1252       {
1253         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1254         event.p1.mUint  = state;
1255         event.p2.mFloat = x;
1256         event.p3.mFloat = y;
1257
1258         mImpl->mEventData->mEventQueue.push_back( event );
1259         break;
1260       }
1261       case HANDLE_TYPE_COUNT:
1262       {
1263         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1264       }
1265     }
1266
1267     mImpl->RequestRelayout();
1268   }
1269 }
1270
1271 Controller::~Controller()
1272 {
1273   delete mImpl;
1274 }
1275
1276 void Controller::ShowPlaceholderText()
1277 {
1278   if( mImpl->IsPlaceholderAvailable() )
1279   {
1280     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1281
1282     mImpl->mEventData->mIsShowingPlaceholderText = true;
1283
1284     // Cancel previously queued inserts etc.
1285     mImpl->mModifyEvents.clear();
1286
1287     // Disable handles when showing place-holder text
1288     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1289     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1290     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1291
1292     const char* text( NULL );
1293     size_t size( 0 );
1294
1295     // TODO - Switch placeholder text styles when changing state
1296     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1297         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1298     {
1299       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1300       size = mImpl->mEventData->mPlaceholderTextActive.size();
1301     }
1302     else
1303     {
1304       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1305       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1306     }
1307
1308     // Reset model for showing placeholder.
1309     mImpl->mLogicalModel->mText.Clear();
1310     ClearModelData();
1311     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1312
1313     // Convert text into UTF-32
1314     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1315     utf32Characters.Resize( size );
1316
1317     // This is a bit horrible but std::string returns a (signed) char*
1318     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1319
1320     // Transform a text array encoded in utf8 into an array encoded in utf32.
1321     // It returns the actual number of characters.
1322     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1323     utf32Characters.Resize( characterCount );
1324
1325     // Reset the cursor position
1326     mImpl->mEventData->mPrimaryCursorPosition = 0;
1327
1328     // The natural size needs to be re-calculated.
1329     mImpl->mRecalculateNaturalSize = true;
1330
1331     // Apply modifications to the model
1332     mImpl->mOperationsPending = ALL_OPERATIONS;
1333     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1334   }
1335 }
1336
1337 void Controller::ClearModelData()
1338 {
1339   // n.b. This does not Clear the mText from mLogicalModel
1340   mImpl->mLogicalModel->mScriptRuns.Clear();
1341   mImpl->mLogicalModel->mFontRuns.Clear();
1342   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1343   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1344   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1345   mImpl->mLogicalModel->mCharacterDirections.Clear();
1346   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1347   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1348   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1349   mImpl->mVisualModel->mGlyphs.Clear();
1350   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1351   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1352   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1353   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1354   mImpl->mVisualModel->mGlyphPositions.Clear();
1355   mImpl->mVisualModel->mLines.Clear();
1356   mImpl->mVisualModel->ClearCaches();
1357 }
1358
1359 void Controller::ClearFontData()
1360 {
1361   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1362   mImpl->mLogicalModel->mFontRuns.Clear();
1363   mImpl->mVisualModel->mGlyphs.Clear();
1364   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1365   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1366   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1367   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1368   mImpl->mVisualModel->mGlyphPositions.Clear();
1369   mImpl->mVisualModel->mLines.Clear();
1370   mImpl->mVisualModel->ClearCaches();
1371 }
1372
1373 Controller::Controller( ControlInterface& controlInterface )
1374 : mImpl( NULL )
1375 {
1376   mImpl = new Controller::Impl( controlInterface );
1377 }
1378
1379 } // namespace Text
1380
1381 } // namespace Toolkit
1382
1383 } // namespace Dali