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