Merge "Set label padding in case of ResizePolicy::USE_NATURAL_SIZE - we don't...
[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->PreEditReset();
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   Vector3 naturalSize;
456
457   // Make sure the model is up-to-date before layouting
458   ProcessModifyEvents();
459
460   if( mImpl->mRecalculateNaturalSize )
461   {
462     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
463
464     // Operations that can be done only once until the text changes.
465     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
466                                                                            GET_SCRIPTS       |
467                                                                            VALIDATE_FONTS    |
468                                                                            GET_LINE_BREAKS   |
469                                                                            GET_WORD_BREAKS   |
470                                                                            BIDI_INFO         |
471                                                                            SHAPE_TEXT        |
472                                                                            GET_GLYPH_METRICS );
473     // Make sure the model is up-to-date before layouting
474     mImpl->UpdateModel( onlyOnceOperations );
475
476     // Operations that need to be done if the size changes.
477     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
478                                                                         ALIGN  |
479                                                                         REORDER );
480
481     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
482                 static_cast<OperationsMask>( onlyOnceOperations |
483                                              sizeOperations ),
484                 naturalSize.GetVectorXY() );
485
486     // Do not do again the only once operations.
487     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
488
489     // Do the size related operations again.
490     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
491
492     // Stores the natural size to avoid recalculate it again
493     // unless the text/style changes.
494     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
495
496     mImpl->mRecalculateNaturalSize = false;
497
498     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
499   }
500   else
501   {
502     naturalSize = mImpl->mVisualModel->GetNaturalSize();
503
504     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
505   }
506
507   return naturalSize;
508 }
509
510 float Controller::GetHeightForWidth( float width )
511 {
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   }
546   else
547   {
548     layoutSize = mImpl->mVisualModel->GetActualSize();
549   }
550
551   return layoutSize.height;
552 }
553
554 bool Controller::Relayout( const Size& size )
555 {
556   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
557
558   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
559   {
560     bool glyphsRemoved( false );
561     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
562     {
563       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
564       glyphsRemoved = true;
565     }
566     // Not worth to relayout if width or height is equal to zero.
567     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
568     return glyphsRemoved;
569   }
570
571   if( size != mImpl->mControlSize )
572   {
573     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
574
575     // Operations that need to be done if the size changes.
576     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
577                                                              LAYOUT                    |
578                                                              ALIGN                     |
579                                                              UPDATE_ACTUAL_SIZE        |
580                                                              REORDER );
581
582     mImpl->mControlSize = size;
583   }
584
585   // Make sure the model is up-to-date before layouting
586   ProcessModifyEvents();
587   mImpl->UpdateModel( mImpl->mOperationsPending );
588
589   Size layoutSize;
590   bool updated = DoRelayout( mImpl->mControlSize,
591                              mImpl->mOperationsPending,
592                              layoutSize );
593
594   // Do not re-do any operation until something changes.
595   mImpl->mOperationsPending = NO_OPERATION;
596
597   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
598   CalculateTextAlignment( size );
599
600   if( mImpl->mEventData )
601   {
602     // Move the cursor, grab handle etc.
603     updated = mImpl->ProcessInputEvents() || updated;
604   }
605
606   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
607   return updated;
608 }
609
610 void Controller::ProcessModifyEvents()
611 {
612   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
613
614   for( unsigned int i=0; i<events.size(); ++i )
615   {
616     if( ModifyEvent::TEXT_REPLACED == events[0].type )
617     {
618       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
619       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
620
621       TextReplacedEvent();
622     }
623     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
624     {
625       TextInsertedEvent();
626     }
627     else if( ModifyEvent::TEXT_DELETED == events[0].type )
628     {
629       // Placeholder-text cannot be deleted
630       if( !mImpl->IsShowingPlaceholderText() )
631       {
632         TextDeletedEvent();
633       }
634     }
635   }
636
637   // Discard temporary text
638   events.clear();
639 }
640
641 void Controller::ResetText()
642 {
643   // Reset buffers.
644   mImpl->mLogicalModel->mText.Clear();
645   ClearModelData();
646
647   // Reset the cursor position
648   if( mImpl->mEventData )
649   {
650     mImpl->mEventData->mPrimaryCursorPosition = 0;
651   }
652
653   // We have cleared everything including the placeholder-text
654   mImpl->PlaceholderCleared();
655
656   // The natural size needs to be re-calculated.
657   mImpl->mRecalculateNaturalSize = true;
658
659   // Apply modifications to the model
660   mImpl->mOperationsPending = ALL_OPERATIONS;
661 }
662
663 void Controller::TextReplacedEvent()
664 {
665   // Reset buffers.
666   ClearModelData();
667
668   // The natural size needs to be re-calculated.
669   mImpl->mRecalculateNaturalSize = true;
670
671   // Apply modifications to the model
672   mImpl->mOperationsPending = ALL_OPERATIONS;
673   mImpl->UpdateModel( ALL_OPERATIONS );
674   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
675                                                            ALIGN              |
676                                                            UPDATE_ACTUAL_SIZE |
677                                                            REORDER );
678 }
679
680 void Controller::TextInsertedEvent()
681 {
682   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
683
684   // TODO - Optimize this
685   ClearModelData();
686
687   // The natural size needs to be re-calculated.
688   mImpl->mRecalculateNaturalSize = true;
689
690   // Apply modifications to the model; TODO - Optimize this
691   mImpl->mOperationsPending = ALL_OPERATIONS;
692   mImpl->UpdateModel( ALL_OPERATIONS );
693   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
694                                                            ALIGN              |
695                                                            UPDATE_ACTUAL_SIZE |
696                                                            REORDER );
697
698   // Queue a cursor reposition event; this must wait until after DoRelayout()
699   mImpl->mEventData->mUpdateCursorPosition = true;
700   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
701 }
702
703 void Controller::TextDeletedEvent()
704 {
705   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
706
707   // TODO - Optimize this
708   ClearModelData();
709
710   // The natural size needs to be re-calculated.
711   mImpl->mRecalculateNaturalSize = true;
712
713   // Apply modifications to the model; TODO - Optimize this
714   mImpl->mOperationsPending = ALL_OPERATIONS;
715   mImpl->UpdateModel( ALL_OPERATIONS );
716   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
717                                                            ALIGN              |
718                                                            UPDATE_ACTUAL_SIZE |
719                                                            REORDER );
720
721   // Queue a cursor reposition event; this must wait until after DoRelayout()
722   mImpl->mEventData->mUpdateCursorPosition = true;
723   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
724 }
725
726 bool Controller::DoRelayout( const Size& size,
727                              OperationsMask operationsRequired,
728                              Size& layoutSize )
729 {
730   bool viewUpdated( false );
731
732   // Calculate the operations to be done.
733   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
734
735   if( LAYOUT & operations )
736   {
737     // Some vectors with data needed to layout and reorder may be void
738     // after the first time the text has been laid out.
739     // Fill the vectors again.
740
741     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
742
743     if( 0u == numberOfGlyphs )
744     {
745       // Nothing else to do if there is no glyphs.
746       return true;
747     }
748
749     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
750     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
751     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
752     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
753     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
754     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
755
756     // Set the layout parameters.
757     LayoutParameters layoutParameters( size,
758                                        mImpl->mLogicalModel->mText.Begin(),
759                                        lineBreakInfo.Begin(),
760                                        wordBreakInfo.Begin(),
761                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
762                                        numberOfGlyphs,
763                                        glyphs.Begin(),
764                                        glyphsToCharactersMap.Begin(),
765                                        charactersPerGlyph.Begin() );
766
767     // The laid-out lines.
768     // It's not possible to know in how many lines the text is going to be laid-out,
769     // but it can be resized at least with the number of 'paragraphs' to avoid
770     // some re-allocations.
771     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
772
773     // Delete any previous laid out lines before setting the new ones.
774     lines.Clear();
775
776     // The capacity of the bidirectional paragraph info is the number of paragraphs.
777     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
778
779     // Resize the vector of positions to have the same size than the vector of glyphs.
780     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
781     glyphPositions.Resize( numberOfGlyphs );
782
783     // Update the visual model.
784     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
785                                                    glyphPositions,
786                                                    lines,
787                                                    layoutSize );
788
789     if( viewUpdated )
790     {
791       // Reorder the lines
792       if( REORDER & operations )
793       {
794         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
795
796         // Check first if there are paragraphs with bidirectional info.
797         if( 0u != bidirectionalInfo.Count() )
798         {
799           // Get the lines
800           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
801
802           // Reorder the lines.
803           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
804           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
805           ReorderLines( bidirectionalInfo,
806                         lines,
807                         lineBidirectionalInfoRuns );
808
809           // Set the bidirectional info into the model.
810           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
811           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
812                                                        numberOfBidirectionalInfoRuns );
813
814           // Set the bidirectional info per line into the layout parameters.
815           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
816           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
817
818           // Get the character to glyph conversion table and set into the layout.
819           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
820
821           // Get the glyphs per character table and set into the layout.
822           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
823
824           // Re-layout the text. Reorder those lines with right to left characters.
825           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
826                                                          glyphPositions );
827
828           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
829           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
830                  endIt = lineBidirectionalInfoRuns.End();
831                it != endIt;
832                ++it )
833           {
834             BidirectionalLineInfoRun& bidiLineInfo = *it;
835
836             free( bidiLineInfo.visualToLogicalMap );
837           }
838         }
839       } // REORDER
840
841       if( ALIGN & operations )
842       {
843         mImpl->mLayoutEngine.Align( layoutParameters,
844                                     layoutSize,
845                                     lines,
846                                     glyphPositions );
847       }
848
849       // Sets the actual size.
850       if( UPDATE_ACTUAL_SIZE & operations )
851       {
852         mImpl->mVisualModel->SetActualSize( layoutSize );
853       }
854     } // view updated
855   }
856   else
857   {
858     layoutSize = mImpl->mVisualModel->GetActualSize();
859   }
860
861   return viewUpdated;
862 }
863
864 void Controller::CalculateTextAlignment( const Size& size )
865 {
866   // Get the direction of the first character.
867   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
868
869   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
870
871   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
872   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
873   if( firstParagraphDirection &&
874       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
875   {
876     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
877     {
878       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
879     }
880     else
881     {
882       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
883     }
884   }
885
886   switch( horizontalAlignment )
887   {
888     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
889     {
890       mImpl->mAlignmentOffset.x = 0.f;
891       break;
892     }
893     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
894     {
895       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
896       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
897       break;
898     }
899     case LayoutEngine::HORIZONTAL_ALIGN_END:
900     {
901       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
902       break;
903     }
904   }
905
906   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
907   switch( verticalAlignment )
908   {
909     case LayoutEngine::VERTICAL_ALIGN_TOP:
910     {
911       mImpl->mAlignmentOffset.y = 0.f;
912       break;
913     }
914     case LayoutEngine::VERTICAL_ALIGN_CENTER:
915     {
916       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
917       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
918       break;
919     }
920     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
921     {
922       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
923       break;
924     }
925   }
926 }
927
928 LayoutEngine& Controller::GetLayoutEngine()
929 {
930   return mImpl->mLayoutEngine;
931 }
932
933 View& Controller::GetView()
934 {
935   return mImpl->mView;
936 }
937
938 void Controller::KeyboardFocusGainEvent()
939 {
940   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
941
942   if( mImpl->mEventData )
943   {
944     Event event( Event::KEYBOARD_FOCUS_GAIN_EVENT );
945     mImpl->mEventData->mEventQueue.push_back( event );
946
947     mImpl->RequestRelayout();
948   }
949 }
950
951 void Controller::KeyboardFocusLostEvent()
952 {
953   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
954
955   if( mImpl->mEventData )
956   {
957     Event event( Event::KEYBOARD_FOCUS_LOST_EVENT );
958     mImpl->mEventData->mEventQueue.push_back( event );
959
960     mImpl->RequestRelayout();
961   }
962 }
963
964 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
965 {
966   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
967
968   if( mImpl->mEventData &&
969       keyEvent.state == KeyEvent::Down )
970   {
971     int keyCode = keyEvent.keyCode;
972     const std::string& keyString = keyEvent.keyPressed;
973
974     // Pre-process to separate modifying events from non-modifying input events.
975     if( Dali::DALI_KEY_ESCAPE == keyCode )
976     {
977       // Escape key is a special case which causes focus loss
978       KeyboardFocusLostEvent();
979     }
980     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
981              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
982              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
983              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
984     {
985       Event event( Event::CURSOR_KEY_EVENT );
986       event.p1.mInt = keyCode;
987       mImpl->mEventData->mEventQueue.push_back( event );
988     }
989     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
990     {
991       // Remove the character before the current cursor position
992       bool removed = RemoveText( -1, 1 );
993
994       if( removed )
995       {
996         if( 0u == mImpl->mLogicalModel->mText.Count() )
997         {
998           ShowPlaceholderText();
999         }
1000         else
1001         {
1002           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1003         }
1004       }
1005     }
1006     else
1007     {
1008       InsertText( keyString, COMMIT );
1009     }
1010
1011     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1012
1013     mImpl->RequestRelayout();
1014   }
1015
1016   return false;
1017 }
1018
1019 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1020 {
1021   bool removedPreEdit( false );
1022   bool maxLengthReached( false );
1023
1024   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1025   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPreEditFlag %d cursor %d\n",
1026                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"), mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPrimaryCursorPosition );
1027
1028   if( ! text.empty() )
1029   {
1030     if( mImpl->IsShowingPlaceholderText() )
1031     {
1032       ResetText();
1033     }
1034   }
1035
1036   if( mImpl->mEventData )
1037   {
1038     if( COMMIT == type )
1039     {
1040       mImpl->mEventData->mPreEditFlag = false;
1041     }
1042     else // PRE_EDIT
1043     {
1044       if( mImpl->mEventData->mPreEditFlag &&
1045           0 != mImpl->mEventData->mPreEditLength )
1046       {
1047         // Remove previous pre-edit text
1048         mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1049         removedPreEdit = RemoveText( -1, mImpl->mEventData->mPreEditLength );
1050       }
1051       else
1052       {
1053         // Record the start of the pre-edit text
1054         mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1055         mImpl->mEventData->mPreEditLength = text.size();
1056
1057         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1058       }
1059
1060       mImpl->mEventData->mPreEditFlag = true;
1061     }
1062   }
1063
1064   if( ! text.empty() )
1065   {
1066     //  Convert text into UTF-32
1067     Vector<Character> utf32Characters;
1068     utf32Characters.Resize( text.size() );
1069
1070     // This is a bit horrible but std::string returns a (signed) char*
1071     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1072
1073     // Transform a text array encoded in utf8 into an array encoded in utf32.
1074     // It returns the actual number of characters.
1075     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1076     utf32Characters.Resize( characterCount );
1077
1078     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1079     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1080
1081     const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1082
1083     // Restrict new text to fit within Maximum characters setting
1084     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1085     maxLengthReached = ( characterCount > maxSizeOfNewText );
1086
1087     // Insert at current cursor position
1088     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1089
1090     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1091
1092     if( cursorIndex < numberOfCharactersInModel )
1093     {
1094       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1095     }
1096     else
1097     {
1098       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1099     }
1100
1101     cursorIndex += maxSizeOfNewText;
1102
1103     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1104   }
1105
1106   if( removedPreEdit || !text.empty() )
1107   {
1108     // Queue an inserted event
1109     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1110   }
1111
1112   if( maxLengthReached )
1113   {
1114     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1115
1116     mImpl->mControlInterface.MaxLengthReached();
1117
1118     mImpl->PreEditReset();
1119   }
1120 }
1121
1122 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1123 {
1124   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1125
1126   if( NULL != mImpl->mEventData )
1127   {
1128     if( 1u == tapCount )
1129     {
1130       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
1131
1132       mImpl->ChangeState( EventData::EDITING );
1133
1134       if( mImpl->IsShowingPlaceholderText() )
1135       {
1136         // Alternative placeholder-text is used when editing
1137         ShowPlaceholderText();
1138       }
1139       else if( EventData::EDITING == mImpl->mEventData->mState )
1140       {
1141         // Grab handle is not shown until a tap is received whilst EDITING
1142         if( tapDuringEditMode )
1143         {
1144           mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1145         }
1146         mImpl->mEventData->mDecorator->SetPopupActive( false );
1147       }
1148     }
1149     else if( mImpl->mEventData->mSelectionEnabled &&
1150              ( 2u == tapCount ) )
1151     {
1152       mImpl->ChangeState( EventData::SELECTING );
1153     }
1154   }
1155
1156   // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1157   if( mImpl->mEventData )
1158   {
1159     Event event( Event::TAP_EVENT );
1160     event.p1.mUint = tapCount;
1161     event.p2.mFloat = x;
1162     event.p3.mFloat = y;
1163     mImpl->mEventData->mEventQueue.push_back( event );
1164
1165     mImpl->RequestRelayout();
1166   }
1167
1168   // Reset keyboard as tap event has occurred.
1169   mImpl->PreEditReset();
1170 }
1171
1172 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1173 {
1174   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1175
1176   if( mImpl->mEventData )
1177   {
1178     Event event( Event::PAN_EVENT );
1179     event.p1.mInt = state;
1180     event.p2.mFloat = displacement.x;
1181     event.p3.mFloat = displacement.y;
1182     mImpl->mEventData->mEventQueue.push_back( event );
1183
1184     mImpl->RequestRelayout();
1185   }
1186 }
1187
1188 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1189 {
1190   DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1191
1192   if( mImpl->mEventData )
1193   {
1194     switch( handleType )
1195     {
1196       case GRAB_HANDLE:
1197       {
1198         Event event( Event::GRAB_HANDLE_EVENT );
1199         event.p1.mUint  = state;
1200         event.p2.mFloat = x;
1201         event.p3.mFloat = y;
1202
1203         mImpl->mEventData->mEventQueue.push_back( event );
1204         break;
1205       }
1206       case LEFT_SELECTION_HANDLE:
1207       {
1208         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1209         event.p1.mUint  = state;
1210         event.p2.mFloat = x;
1211         event.p3.mFloat = y;
1212
1213         mImpl->mEventData->mEventQueue.push_back( event );
1214         break;
1215       }
1216       case RIGHT_SELECTION_HANDLE:
1217       {
1218         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1219         event.p1.mUint  = state;
1220         event.p2.mFloat = x;
1221         event.p3.mFloat = y;
1222
1223         mImpl->mEventData->mEventQueue.push_back( event );
1224         break;
1225       }
1226       case HANDLE_TYPE_COUNT:
1227       {
1228         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1229       }
1230     }
1231
1232     mImpl->RequestRelayout();
1233   }
1234 }
1235
1236 Controller::~Controller()
1237 {
1238   delete mImpl;
1239 }
1240
1241 void Controller::ShowPlaceholderText()
1242 {
1243   if( mImpl->IsPlaceholderAvailable() )
1244   {
1245     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1246
1247     mImpl->mEventData->mIsShowingPlaceholderText = true;
1248
1249     // Cancel previously queued inserts etc.
1250     mImpl->mModifyEvents.clear();
1251
1252     // Disable handles when showing place-holder text
1253     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1254     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1255     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1256
1257     const char* text( NULL );
1258     size_t size( 0 );
1259
1260     // TODO - Switch placeholder text styles when changing state
1261     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1262         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1263     {
1264       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1265       size = mImpl->mEventData->mPlaceholderTextActive.size();
1266     }
1267     else
1268     {
1269       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1270       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1271     }
1272
1273     // Reset model for showing placeholder.
1274     mImpl->mLogicalModel->mText.Clear();
1275     ClearModelData();
1276     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1277
1278     // Convert text into UTF-32
1279     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1280     utf32Characters.Resize( size );
1281
1282     // This is a bit horrible but std::string returns a (signed) char*
1283     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1284
1285     // Transform a text array encoded in utf8 into an array encoded in utf32.
1286     // It returns the actual number of characters.
1287     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1288     utf32Characters.Resize( characterCount );
1289
1290     // Reset the cursor position
1291     mImpl->mEventData->mPrimaryCursorPosition = 0;
1292
1293     // The natural size needs to be re-calculated.
1294     mImpl->mRecalculateNaturalSize = true;
1295
1296     // Apply modifications to the model
1297     mImpl->mOperationsPending = ALL_OPERATIONS;
1298     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1299   }
1300 }
1301
1302 void Controller::ClearModelData()
1303 {
1304   // n.b. This does not Clear the mText from mLogicalModel
1305   mImpl->mLogicalModel->mScriptRuns.Clear();
1306   mImpl->mLogicalModel->mFontRuns.Clear();
1307   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1308   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1309   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1310   mImpl->mLogicalModel->mCharacterDirections.Clear();
1311   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1312   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1313   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1314   mImpl->mVisualModel->mGlyphs.Clear();
1315   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1316   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1317   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1318   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1319   mImpl->mVisualModel->mGlyphPositions.Clear();
1320   mImpl->mVisualModel->mLines.Clear();
1321   mImpl->mVisualModel->ClearCaches();
1322 }
1323
1324 void Controller::ClearFontData()
1325 {
1326   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1327   mImpl->mLogicalModel->mFontRuns.Clear();
1328   mImpl->mVisualModel->mGlyphs.Clear();
1329   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1330   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1331   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1332   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1333   mImpl->mVisualModel->mGlyphPositions.Clear();
1334   mImpl->mVisualModel->mLines.Clear();
1335   mImpl->mVisualModel->ClearCaches();
1336 }
1337
1338 Controller::Controller( ControlInterface& controlInterface )
1339 : mImpl( NULL )
1340 {
1341   mImpl = new Controller::Impl( controlInterface );
1342 }
1343
1344 } // namespace Text
1345
1346 } // namespace Toolkit
1347
1348 } // namespace Dali