Text cleaning.
[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 <dali/public-api/adaptor-framework/key.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.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/text-controller-impl.h>
32
33 namespace
34 {
35
36 #if defined(DEBUG_ENABLED)
37   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
38 #endif
39
40 const float MAX_FLOAT = std::numeric_limits<float>::max();
41 const unsigned int POINTS_PER_INCH = 72;
42
43 const std::string EMPTY_STRING("");
44 const unsigned int ZERO = 0u;
45
46 float ConvertToEven( float value )
47 {
48   int intValue(static_cast<int>( value ));
49   return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
50 }
51
52 } // namespace
53
54 namespace Dali
55 {
56
57 namespace Toolkit
58 {
59
60 namespace Text
61 {
62
63 ControllerPtr Controller::New( ControlInterface& controlInterface )
64 {
65   return ControllerPtr( new Controller( controlInterface ) );
66 }
67
68 void Controller::EnableTextInput( DecoratorPtr decorator )
69 {
70   if( !mImpl->mEventData )
71   {
72     mImpl->mEventData = new EventData( decorator );
73   }
74 }
75
76 void Controller::SetText( const std::string& text )
77 {
78   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
79
80   // Reset keyboard as text changed
81   mImpl->ResetImfManager();
82
83   // Remove the previously set text
84   ResetText();
85
86   CharacterIndex lastCursorIndex = 0u;
87
88   if( mImpl->mEventData )
89   {
90     // If popup shown then hide it by switching to Editing state
91     if( ( EventData::SELECTING == mImpl->mEventData->mState )          ||
92         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
93         ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) ||
94         ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
95     {
96       mImpl->ChangeState( EventData::EDITING );
97     }
98   }
99
100   if( !text.empty() )
101   {
102     //  Convert text into UTF-32
103     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
104     utf32Characters.Resize( text.size() );
105
106     // This is a bit horrible but std::string returns a (signed) char*
107     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
108
109     // Transform a text array encoded in utf8 into an array encoded in utf32.
110     // It returns the actual number of characters.
111     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
112     utf32Characters.Resize( characterCount );
113
114     DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
115     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
116
117     // To reset the cursor position
118     lastCursorIndex = characterCount;
119
120     // Update the rest of the model during size negotiation
121     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
122
123     // The natural size needs to be re-calculated.
124     mImpl->mRecalculateNaturalSize = true;
125
126     // Apply modifications to the model
127     mImpl->mOperationsPending = ALL_OPERATIONS;
128   }
129   else
130   {
131     ShowPlaceholderText();
132   }
133
134   // Resets the cursor position.
135   ResetCursorPosition( lastCursorIndex );
136
137   // Scrolls the text to make the cursor visible.
138   ResetScrollPosition();
139
140   mImpl->RequestRelayout();
141
142   if( mImpl->mEventData )
143   {
144     // Cancel previously queued events
145     mImpl->mEventData->mEventQueue.clear();
146   }
147
148   // Notify IMF as text changed
149   NotifyImfManager();
150
151   // Do this last since it provides callbacks into application code
152   mImpl->mControlInterface.TextChanged();
153 }
154
155 void Controller::GetText( std::string& text ) const
156 {
157   if( ! mImpl->IsShowingPlaceholderText() )
158   {
159     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
160
161     if( 0u != utf32Characters.Count() )
162     {
163       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
164     }
165   }
166   else
167   {
168     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
169   }
170 }
171
172 unsigned int Controller::GetLogicalCursorPosition() const
173 {
174   if( mImpl->mEventData )
175   {
176     return mImpl->mEventData->mPrimaryCursorPosition;
177   }
178
179   return 0u;
180 }
181
182 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
183 {
184   if( mImpl->mEventData )
185   {
186     if( PLACEHOLDER_TYPE_INACTIVE == type )
187     {
188       mImpl->mEventData->mPlaceholderTextInactive = text;
189     }
190     else
191     {
192       mImpl->mEventData->mPlaceholderTextActive = text;
193     }
194
195     // Update placeholder if there is no text
196     if( mImpl->IsShowingPlaceholderText() ||
197         0u == mImpl->mLogicalModel->mText.Count() )
198     {
199       ShowPlaceholderText();
200     }
201   }
202 }
203
204 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
205 {
206   if( mImpl->mEventData )
207   {
208     if( PLACEHOLDER_TYPE_INACTIVE == type )
209     {
210       text = mImpl->mEventData->mPlaceholderTextInactive;
211     }
212     else
213     {
214       text = mImpl->mEventData->mPlaceholderTextActive;
215     }
216   }
217 }
218
219 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
220 {
221   if ( maxCharacters >= 0 )
222   {
223     mImpl->mMaximumNumberOfCharacters = maxCharacters;
224   }
225 }
226
227 int Controller::GetMaximumNumberOfCharacters()
228 {
229   return mImpl->mMaximumNumberOfCharacters;
230 }
231
232 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
233 {
234   if( !mImpl->mFontDefaults )
235   {
236     mImpl->mFontDefaults = new FontDefaults();
237   }
238
239   mImpl->mFontDefaults->mFontDescription.family = defaultFontFamily;
240   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str());
241   mImpl->mUserDefinedFontFamily = true;
242
243   // Clear the font-specific data
244   ClearFontData();
245
246   mImpl->mOperationsPending = ALL_OPERATIONS;
247   mImpl->mRecalculateNaturalSize = true;
248
249   mImpl->RequestRelayout();
250 }
251
252 const std::string& Controller::GetDefaultFontFamily() const
253 {
254   if( mImpl->mFontDefaults )
255   {
256     return mImpl->mFontDefaults->mFontDescription.family;
257   }
258
259   return EMPTY_STRING;
260 }
261
262 void Controller::SetDefaultFontStyle( const std::string& style )
263 {
264   if( !mImpl->mFontDefaults )
265   {
266     mImpl->mFontDefaults = new FontDefaults();
267   }
268
269   mImpl->mFontDefaults->mFontStyle = style;
270 }
271
272 const std::string& Controller::GetDefaultFontStyle() const
273 {
274   if( mImpl->mFontDefaults )
275   {
276     return mImpl->mFontDefaults->mFontStyle;
277   }
278
279   return EMPTY_STRING;
280 }
281
282 void Controller::SetDefaultFontWidth( FontWidth width )
283 {
284   if( !mImpl->mFontDefaults )
285   {
286     mImpl->mFontDefaults = new FontDefaults();
287   }
288
289   mImpl->mFontDefaults->mFontDescription.width = width;
290
291   // Clear the font-specific data
292   ClearFontData();
293
294   mImpl->mOperationsPending = ALL_OPERATIONS;
295   mImpl->mRecalculateNaturalSize = true;
296
297   mImpl->RequestRelayout();
298 }
299
300 FontWidth Controller::GetDefaultFontWidth() const
301 {
302   if( mImpl->mFontDefaults )
303   {
304     return mImpl->mFontDefaults->mFontDescription.width;
305   }
306
307   return TextAbstraction::FontWidth::NORMAL;
308 }
309
310 void Controller::SetDefaultFontWeight( FontWeight weight )
311 {
312   if( !mImpl->mFontDefaults )
313   {
314     mImpl->mFontDefaults = new FontDefaults();
315   }
316
317   mImpl->mFontDefaults->mFontDescription.weight = weight;
318
319   // Clear the font-specific data
320   ClearFontData();
321
322   mImpl->mOperationsPending = ALL_OPERATIONS;
323   mImpl->mRecalculateNaturalSize = true;
324
325   mImpl->RequestRelayout();
326 }
327
328 FontWeight Controller::GetDefaultFontWeight() const
329 {
330   if( mImpl->mFontDefaults )
331   {
332     return mImpl->mFontDefaults->mFontDescription.weight;
333   }
334
335   return TextAbstraction::FontWeight::NORMAL;
336 }
337
338 void Controller::SetDefaultFontSlant( FontSlant slant )
339 {
340   if( !mImpl->mFontDefaults )
341   {
342     mImpl->mFontDefaults = new FontDefaults();
343   }
344
345   mImpl->mFontDefaults->mFontDescription.slant = slant;
346
347   // Clear the font-specific data
348   ClearFontData();
349
350   mImpl->mOperationsPending = ALL_OPERATIONS;
351   mImpl->mRecalculateNaturalSize = true;
352
353   mImpl->RequestRelayout();
354 }
355
356 FontSlant Controller::GetDefaultFontSlant() const
357 {
358   if( mImpl->mFontDefaults )
359   {
360     return mImpl->mFontDefaults->mFontDescription.slant;
361   }
362
363   return TextAbstraction::FontSlant::NORMAL;
364 }
365
366 void Controller::SetDefaultPointSize( float pointSize )
367 {
368   if( !mImpl->mFontDefaults )
369   {
370     mImpl->mFontDefaults = new FontDefaults();
371   }
372
373   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
374
375   unsigned int horizontalDpi( 0u );
376   unsigned int verticalDpi( 0u );
377   mImpl->mFontClient.GetDpi( horizontalDpi, verticalDpi );
378
379   // Adjust the metrics if the fixed-size font should be down-scaled
380   int maxEmojiSize( pointSize/POINTS_PER_INCH * verticalDpi );
381   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultPointSize %p setting MaxEmojiSize %d\n", this, maxEmojiSize );
382   mImpl->mMetrics->SetMaxEmojiSize( maxEmojiSize );
383
384   // Clear the font-specific data
385   ClearFontData();
386
387   mImpl->mOperationsPending = ALL_OPERATIONS;
388   mImpl->mRecalculateNaturalSize = true;
389
390   mImpl->RequestRelayout();
391 }
392
393 float Controller::GetDefaultPointSize() const
394 {
395   if( mImpl->mFontDefaults )
396   {
397     return mImpl->mFontDefaults->mDefaultPointSize;
398   }
399
400   return 0.0f;
401 }
402
403 void Controller::UpdateAfterFontChange( std::string& newDefaultFont )
404 {
405   DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange");
406
407   if ( !mImpl->mUserDefinedFontFamily ) // If user defined font then should not update when system font changes
408   {
409     DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
410     ClearFontData();
411     mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
412     mImpl->UpdateModel( ALL_OPERATIONS );
413     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
414     mImpl->mRecalculateNaturalSize = true;
415     mImpl->RequestRelayout();
416   }
417 }
418
419 void Controller::SetTextColor( const Vector4& textColor )
420 {
421   mImpl->mTextColor = textColor;
422
423   if( !mImpl->IsShowingPlaceholderText() )
424   {
425     mImpl->mVisualModel->SetTextColor( textColor );
426
427     mImpl->RequestRelayout();
428   }
429 }
430
431 const Vector4& Controller::GetTextColor() const
432 {
433   return mImpl->mTextColor;
434 }
435
436 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
437 {
438   bool removed( false );
439
440   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
441                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
442
443   if( !mImpl->IsShowingPlaceholderText() )
444   {
445     // Delete at current cursor position
446     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
447     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
448
449     CharacterIndex cursorIndex = oldCursorIndex;
450
451     // Validate the cursor position & number of characters
452     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
453     {
454       cursorIndex = oldCursorIndex + cursorOffset;
455     }
456
457     if( ( cursorIndex + numberOfChars ) > currentText.Count() )
458     {
459       numberOfChars = currentText.Count() - cursorIndex;
460     }
461
462     if( ( cursorIndex + numberOfChars ) <= currentText.Count() )
463     {
464       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
465       Vector<Character>::Iterator last  = first + numberOfChars;
466
467       currentText.Erase( first, last );
468
469       // Cursor position retreat
470       oldCursorIndex = cursorIndex;
471
472       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
473       removed = true;
474     }
475   }
476
477   return removed;
478 }
479
480 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
481 {
482   if( mImpl->mEventData )
483   {
484     mImpl->mEventData->mPlaceholderTextColor = textColor;
485   }
486
487   if( mImpl->IsShowingPlaceholderText() )
488   {
489     mImpl->mVisualModel->SetTextColor( textColor );
490     mImpl->RequestRelayout();
491   }
492 }
493
494 const Vector4& Controller::GetPlaceholderTextColor() const
495 {
496   if( mImpl->mEventData )
497   {
498     return mImpl->mEventData->mPlaceholderTextColor;
499   }
500
501   return Color::BLACK;
502 }
503
504 void Controller::SetShadowOffset( const Vector2& shadowOffset )
505 {
506   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
507
508   mImpl->RequestRelayout();
509 }
510
511 const Vector2& Controller::GetShadowOffset() const
512 {
513   return mImpl->mVisualModel->GetShadowOffset();
514 }
515
516 void Controller::SetShadowColor( const Vector4& shadowColor )
517 {
518   mImpl->mVisualModel->SetShadowColor( shadowColor );
519
520   mImpl->RequestRelayout();
521 }
522
523 const Vector4& Controller::GetShadowColor() const
524 {
525   return mImpl->mVisualModel->GetShadowColor();
526 }
527
528 void Controller::SetUnderlineColor( const Vector4& color )
529 {
530   mImpl->mVisualModel->SetUnderlineColor( color );
531
532   mImpl->RequestRelayout();
533 }
534
535 const Vector4& Controller::GetUnderlineColor() const
536 {
537   return mImpl->mVisualModel->GetUnderlineColor();
538 }
539
540 void Controller::SetUnderlineEnabled( bool enabled )
541 {
542   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
543
544   mImpl->RequestRelayout();
545 }
546
547 bool Controller::IsUnderlineEnabled() const
548 {
549   return mImpl->mVisualModel->IsUnderlineEnabled();
550 }
551
552 void Controller::SetUnderlineHeight( float height )
553 {
554   mImpl->mVisualModel->SetUnderlineHeight( height );
555
556   mImpl->RequestRelayout();
557 }
558
559 float Controller::GetUnderlineHeight() const
560 {
561   return mImpl->mVisualModel->GetUnderlineHeight();
562 }
563
564 void Controller::SetEnableCursorBlink( bool enable )
565 {
566   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
567
568   if( mImpl->mEventData )
569   {
570     mImpl->mEventData->mCursorBlinkEnabled = enable;
571
572     if( !enable &&
573         mImpl->mEventData->mDecorator )
574     {
575       mImpl->mEventData->mDecorator->StopCursorBlink();
576     }
577   }
578 }
579
580 bool Controller::GetEnableCursorBlink() const
581 {
582   if( mImpl->mEventData )
583   {
584     return mImpl->mEventData->mCursorBlinkEnabled;
585   }
586
587   return false;
588 }
589
590 const Vector2& Controller::GetScrollPosition() const
591 {
592   if( mImpl->mEventData )
593   {
594     return mImpl->mEventData->mScrollPosition;
595   }
596
597   return Vector2::ZERO;
598 }
599
600 const Vector2& Controller::GetAlignmentOffset() const
601 {
602   return mImpl->mAlignmentOffset;
603 }
604
605 Vector3 Controller::GetNaturalSize()
606 {
607   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
608   Vector3 naturalSize;
609
610   // Make sure the model is up-to-date before layouting
611   ProcessModifyEvents();
612
613   if( mImpl->mRecalculateNaturalSize )
614   {
615     // Operations that can be done only once until the text changes.
616     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
617                                                                            GET_SCRIPTS       |
618                                                                            VALIDATE_FONTS    |
619                                                                            GET_LINE_BREAKS   |
620                                                                            GET_WORD_BREAKS   |
621                                                                            BIDI_INFO         |
622                                                                            SHAPE_TEXT        |
623                                                                            GET_GLYPH_METRICS );
624     // Make sure the model is up-to-date before layouting
625     mImpl->UpdateModel( onlyOnceOperations );
626
627     // Operations that need to be done if the size changes.
628     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
629                                                                         ALIGN  |
630                                                                         REORDER );
631
632     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
633                 static_cast<OperationsMask>( onlyOnceOperations |
634                                              sizeOperations ),
635                 naturalSize.GetVectorXY() );
636
637     // Do not do again the only once operations.
638     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
639
640     // Do the size related operations again.
641     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
642
643     // Stores the natural size to avoid recalculate it again
644     // unless the text/style changes.
645     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
646
647     mImpl->mRecalculateNaturalSize = false;
648
649     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
650   }
651   else
652   {
653     naturalSize = mImpl->mVisualModel->GetNaturalSize();
654
655     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
656   }
657
658   naturalSize.x = ConvertToEven( naturalSize.x );
659   naturalSize.y = ConvertToEven( naturalSize.y );
660
661   return naturalSize;
662 }
663
664 float Controller::GetHeightForWidth( float width )
665 {
666   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
667   // Make sure the model is up-to-date before layouting
668   ProcessModifyEvents();
669
670   Size layoutSize;
671   if( fabsf( width - mImpl->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 )
672   {
673     // Operations that can be done only once until the text changes.
674     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
675                                                                            GET_SCRIPTS       |
676                                                                            VALIDATE_FONTS    |
677                                                                            GET_LINE_BREAKS   |
678                                                                            GET_WORD_BREAKS   |
679                                                                            BIDI_INFO         |
680                                                                            SHAPE_TEXT        |
681                                                                            GET_GLYPH_METRICS );
682     // Make sure the model is up-to-date before layouting
683     mImpl->UpdateModel( onlyOnceOperations );
684
685     // Operations that need to be done if the size changes.
686     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
687                                                                         ALIGN  |
688                                                                         REORDER );
689
690     DoRelayout( Size( width, MAX_FLOAT ),
691                 static_cast<OperationsMask>( onlyOnceOperations |
692                                              sizeOperations ),
693                 layoutSize );
694
695     // Do not do again the only once operations.
696     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
697
698     // Do the size related operations again.
699     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
700     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
701   }
702   else
703   {
704     layoutSize = mImpl->mVisualModel->GetActualSize();
705     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
706   }
707
708   return layoutSize.height;
709 }
710
711 bool Controller::Relayout( const Size& size )
712 {
713   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
714
715   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
716   {
717     bool glyphsRemoved( false );
718     if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
719     {
720       mImpl->mVisualModel->mGlyphPositions.Clear();
721       glyphsRemoved = true;
722     }
723     // Not worth to relayout if width or height is equal to zero.
724     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
725     return glyphsRemoved;
726   }
727
728   const bool newSize = ( size != mImpl->mVisualModel->mControlSize );
729
730   if( newSize )
731   {
732     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
733
734     // Operations that need to be done if the size changes.
735     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
736                                                              LAYOUT                    |
737                                                              ALIGN                     |
738                                                              UPDATE_ACTUAL_SIZE        |
739                                                              REORDER );
740
741     mImpl->mVisualModel->mControlSize = size;
742   }
743
744   // Make sure the model is up-to-date before layouting
745   ProcessModifyEvents();
746   mImpl->UpdateModel( mImpl->mOperationsPending );
747
748   Size layoutSize;
749   bool updated = DoRelayout( mImpl->mVisualModel->mControlSize,
750                              mImpl->mOperationsPending,
751                              layoutSize );
752
753   // Do not re-do any operation until something changes.
754   mImpl->mOperationsPending = NO_OPERATION;
755
756   // Keep the current offset and alignment as it will be used to update the decorator's positions (if the size changes).
757   Vector2 offset;
758   if( newSize && mImpl->mEventData )
759   {
760     offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
761   }
762
763   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
764   CalculateTextAlignment( size );
765
766   if( mImpl->mEventData )
767   {
768     if( newSize )
769     {
770       // If there is a new size, the scroll position needs to be clamped.
771       mImpl->ClampHorizontalScroll( layoutSize );
772
773       // Update the decorator's positions is needed if there is a new size.
774       mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
775     }
776
777     // Move the cursor, grab handle etc.
778     updated = mImpl->ProcessInputEvents() || updated;
779   }
780
781   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
782   return updated;
783 }
784
785 void Controller::ProcessModifyEvents()
786 {
787   Vector<ModifyEvent>& events = mImpl->mModifyEvents;
788
789   if( 0u == events.Count() )
790   {
791     // Nothing to do.
792     return;
793   }
794
795   for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
796          endIt = events.End();
797        it != endIt;
798        ++it )
799   {
800     const ModifyEvent& event = *it;
801
802     if( ModifyEvent::TEXT_REPLACED == event.type )
803     {
804       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
805       DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
806
807       TextReplacedEvent();
808     }
809     else if( ModifyEvent::TEXT_INSERTED == event.type )
810     {
811       TextInsertedEvent();
812     }
813     else if( ModifyEvent::TEXT_DELETED == event.type )
814     {
815       // Placeholder-text cannot be deleted
816       if( !mImpl->IsShowingPlaceholderText() )
817       {
818         TextDeletedEvent();
819       }
820     }
821   }
822
823   if( mImpl->mEventData )
824   {
825     // When the text is being modified, delay cursor blinking
826     mImpl->mEventData->mDecorator->DelayCursorBlink();
827   }
828
829   // Discard temporary text
830   events.Clear();
831 }
832
833 void Controller::ResetText()
834 {
835   // Reset buffers.
836   mImpl->mLogicalModel->mText.Clear();
837   ClearModelData();
838
839   // We have cleared everything including the placeholder-text
840   mImpl->PlaceholderCleared();
841
842   // The natural size needs to be re-calculated.
843   mImpl->mRecalculateNaturalSize = true;
844
845   // Apply modifications to the model
846   mImpl->mOperationsPending = ALL_OPERATIONS;
847 }
848
849 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
850 {
851   // Reset the cursor position
852   if( NULL != mImpl->mEventData )
853   {
854     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
855
856     // Update the cursor if it's in editing mode.
857     if ( EventData::IsEditingState( mImpl->mEventData->mState )  )
858     {
859       mImpl->mEventData->mUpdateCursorPosition = true;
860     }
861   }
862 }
863
864 void Controller::ResetScrollPosition()
865 {
866   if( NULL != mImpl->mEventData )
867   {
868     // Reset the scroll position.
869     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
870     mImpl->mEventData->mScrollAfterUpdatePosition = true;
871   }
872 }
873
874 void Controller::TextReplacedEvent()
875 {
876   // Reset buffers.
877   ClearModelData();
878
879   // The natural size needs to be re-calculated.
880   mImpl->mRecalculateNaturalSize = true;
881
882   // Apply modifications to the model
883   mImpl->mOperationsPending = ALL_OPERATIONS;
884   mImpl->UpdateModel( ALL_OPERATIONS );
885   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
886                                                            ALIGN              |
887                                                            UPDATE_ACTUAL_SIZE |
888                                                            REORDER );
889 }
890
891 void Controller::TextInsertedEvent()
892 {
893   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
894
895   // TODO - Optimize this
896   ClearModelData();
897
898   // The natural size needs to be re-calculated.
899   mImpl->mRecalculateNaturalSize = true;
900
901   // Apply modifications to the model; TODO - Optimize this
902   mImpl->mOperationsPending = ALL_OPERATIONS;
903   mImpl->UpdateModel( ALL_OPERATIONS );
904   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
905                                                            ALIGN              |
906                                                            UPDATE_ACTUAL_SIZE |
907                                                            REORDER );
908
909   // Queue a cursor reposition event; this must wait until after DoRelayout()
910   if ( EventData::IsEditingState( mImpl->mEventData->mState )  )
911   {
912     mImpl->mEventData->mUpdateCursorPosition = true;
913     mImpl->mEventData->mScrollAfterUpdatePosition = true;
914   }
915 }
916
917 void Controller::TextDeletedEvent()
918 {
919   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
920
921   // TODO - Optimize this
922   ClearModelData();
923
924   // The natural size needs to be re-calculated.
925   mImpl->mRecalculateNaturalSize = true;
926
927   // Apply modifications to the model; TODO - Optimize this
928   mImpl->mOperationsPending = ALL_OPERATIONS;
929   mImpl->UpdateModel( ALL_OPERATIONS );
930   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
931                                                            ALIGN              |
932                                                            UPDATE_ACTUAL_SIZE |
933                                                            REORDER );
934
935   // Queue a cursor reposition event; this must wait until after DoRelayout()
936   mImpl->mEventData->mUpdateCursorPosition = true;
937   if( 0u != mImpl->mLogicalModel->mText.Count() )
938   {
939     mImpl->mEventData->mScrollAfterDelete = true;
940   }
941 }
942
943 bool Controller::DoRelayout( const Size& size,
944                              OperationsMask operationsRequired,
945                              Size& layoutSize )
946 {
947   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
948   bool viewUpdated( false );
949
950   // Calculate the operations to be done.
951   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
952
953   if( LAYOUT & operations )
954   {
955     // Some vectors with data needed to layout and reorder may be void
956     // after the first time the text has been laid out.
957     // Fill the vectors again.
958
959     const Length numberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
960
961     if( 0u == numberOfGlyphs )
962     {
963       // Nothing else to do if there is no glyphs.
964       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
965       return true;
966     }
967
968     const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
969     const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
970     const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
971     const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
972     const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
973     const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
974     const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
975
976     // Set the layout parameters.
977     LayoutParameters layoutParameters( size,
978                                        textBuffer,
979                                        lineBreakInfo.Begin(),
980                                        wordBreakInfo.Begin(),
981                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
982                                        numberOfGlyphs,
983                                        glyphs.Begin(),
984                                        glyphsToCharactersMap.Begin(),
985                                        charactersPerGlyph.Begin() );
986
987     // The laid-out lines.
988     // It's not possible to know in how many lines the text is going to be laid-out,
989     // but it can be resized at least with the number of 'paragraphs' to avoid
990     // some re-allocations.
991     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
992
993     // Delete any previous laid out lines before setting the new ones.
994     lines.Clear();
995
996     // The capacity of the bidirectional paragraph info is the number of paragraphs.
997     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
998
999     // Resize the vector of positions to have the same size than the vector of glyphs.
1000     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1001     glyphPositions.Resize( numberOfGlyphs );
1002
1003     // Whether the last character is a new paragraph character.
1004     layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
1005
1006     // Update the visual model.
1007     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1008                                                    glyphPositions,
1009                                                    lines,
1010                                                    layoutSize );
1011
1012     if( viewUpdated )
1013     {
1014       // Reorder the lines
1015       if( REORDER & operations )
1016       {
1017         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1018
1019         // Check first if there are paragraphs with bidirectional info.
1020         if( 0u != bidirectionalInfo.Count() )
1021         {
1022           // Get the lines
1023           const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
1024
1025           // Reorder the lines.
1026           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1027           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1028           ReorderLines( bidirectionalInfo,
1029                         lines,
1030                         lineBidirectionalInfoRuns );
1031
1032           // Set the bidirectional info into the model.
1033           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1034           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1035                                                        numberOfBidirectionalInfoRuns );
1036
1037           // Set the bidirectional info per line into the layout parameters.
1038           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1039           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1040
1041           // Get the character to glyph conversion table and set into the layout.
1042           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1043
1044           // Get the glyphs per character table and set into the layout.
1045           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1046
1047           // Re-layout the text. Reorder those lines with right to left characters.
1048           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1049                                                          glyphPositions );
1050
1051           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1052           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1053                  endIt = lineBidirectionalInfoRuns.End();
1054                it != endIt;
1055                ++it )
1056           {
1057             BidirectionalLineInfoRun& bidiLineInfo = *it;
1058
1059             free( bidiLineInfo.visualToLogicalMap );
1060           }
1061         }
1062       } // REORDER
1063
1064       // Sets the actual size.
1065       if( UPDATE_ACTUAL_SIZE & operations )
1066       {
1067         mImpl->mVisualModel->SetActualSize( layoutSize );
1068       }
1069     } // view updated
1070   }
1071   else
1072   {
1073     layoutSize = mImpl->mVisualModel->GetActualSize();
1074   }
1075
1076   if( ALIGN & operations )
1077   {
1078     // The laid-out lines.
1079     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1080
1081     mImpl->mLayoutEngine.Align( layoutSize,
1082                                 lines );
1083
1084     viewUpdated = true;
1085   }
1086
1087   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
1088   return viewUpdated;
1089 }
1090
1091 void Controller::SetMultiLineEnabled( bool enable )
1092 {
1093   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
1094
1095   if( layout != mImpl->mLayoutEngine.GetLayout() )
1096   {
1097     // Set the layout type.
1098     mImpl->mLayoutEngine.SetLayout( layout );
1099
1100     // Set the flags to redo the layout operations
1101     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
1102                                                                           UPDATE_ACTUAL_SIZE |
1103                                                                           ALIGN              |
1104                                                                           REORDER );
1105
1106     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
1107
1108     mImpl->RequestRelayout();
1109   }
1110 }
1111
1112 bool Controller::IsMultiLineEnabled() const
1113 {
1114   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
1115 }
1116
1117 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
1118 {
1119   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1120   {
1121     // Set the alignment.
1122     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1123
1124     // Set the flag to redo the alignment operation.
1125     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1126
1127     mImpl->RequestRelayout();
1128   }
1129 }
1130
1131 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1132 {
1133   return mImpl->mLayoutEngine.GetHorizontalAlignment();
1134 }
1135
1136 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1137 {
1138   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1139   {
1140     // Set the alignment.
1141     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1142
1143     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1144
1145     mImpl->RequestRelayout();
1146   }
1147 }
1148
1149 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1150 {
1151   return mImpl->mLayoutEngine.GetVerticalAlignment();
1152 }
1153
1154 void Controller::CalculateTextAlignment( const Size& size )
1155 {
1156   // Get the direction of the first character.
1157   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1158
1159   Size actualSize = mImpl->mVisualModel->GetActualSize();
1160   if( fabsf( actualSize.height ) < Math::MACHINE_EPSILON_1000 )
1161   {
1162     // Get the line height of the default font.
1163     actualSize.height = mImpl->GetDefaultFontLineHeight();
1164   }
1165
1166   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1167   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1168   if( firstParagraphDirection &&
1169       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1170   {
1171     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1172     {
1173       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1174     }
1175     else
1176     {
1177       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1178     }
1179   }
1180
1181   switch( horizontalAlignment )
1182   {
1183     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1184     {
1185       mImpl->mAlignmentOffset.x = 0.f;
1186       break;
1187     }
1188     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1189     {
1190       mImpl->mAlignmentOffset.x = floorf( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1191       break;
1192     }
1193     case LayoutEngine::HORIZONTAL_ALIGN_END:
1194     {
1195       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1196       break;
1197     }
1198   }
1199
1200   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1201   switch( verticalAlignment )
1202   {
1203     case LayoutEngine::VERTICAL_ALIGN_TOP:
1204     {
1205       mImpl->mAlignmentOffset.y = 0.f;
1206       break;
1207     }
1208     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1209     {
1210       mImpl->mAlignmentOffset.y = floorf( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1211       break;
1212     }
1213     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1214     {
1215       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1216       break;
1217     }
1218   }
1219 }
1220
1221 LayoutEngine& Controller::GetLayoutEngine()
1222 {
1223   return mImpl->mLayoutEngine;
1224 }
1225
1226 View& Controller::GetView()
1227 {
1228   return mImpl->mView;
1229 }
1230
1231 void Controller::KeyboardFocusGainEvent()
1232 {
1233   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1234
1235   if( mImpl->mEventData )
1236   {
1237     if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1238         ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1239     {
1240       mImpl->ChangeState( EventData::EDITING );
1241       mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1242     }
1243
1244     if( mImpl->IsShowingPlaceholderText() )
1245     {
1246       // Show alternative placeholder-text when editing
1247       ShowPlaceholderText();
1248     }
1249
1250     mImpl->RequestRelayout();
1251   }
1252 }
1253
1254 void Controller::KeyboardFocusLostEvent()
1255 {
1256   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1257
1258   if( mImpl->mEventData )
1259   {
1260     if ( EventData::INTERRUPTED != mImpl->mEventData->mState )
1261     {
1262       mImpl->ChangeState( EventData::INACTIVE );
1263
1264       if( !mImpl->IsShowingRealText() )
1265       {
1266         // Revert to regular placeholder-text when not editing
1267         ShowPlaceholderText();
1268       }
1269     }
1270   }
1271   mImpl->RequestRelayout();
1272 }
1273
1274 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1275 {
1276   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1277
1278   bool textChanged( false );
1279
1280   if( mImpl->mEventData &&
1281       keyEvent.state == KeyEvent::Down )
1282   {
1283     int keyCode = keyEvent.keyCode;
1284     const std::string& keyString = keyEvent.keyPressed;
1285
1286     // Pre-process to separate modifying events from non-modifying input events.
1287     if( Dali::DALI_KEY_ESCAPE == keyCode )
1288     {
1289       // Escape key is a special case which causes focus loss
1290       KeyboardFocusLostEvent();
1291     }
1292     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1293              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1294              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1295              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1296     {
1297       Event event( Event::CURSOR_KEY_EVENT );
1298       event.p1.mInt = keyCode;
1299       mImpl->mEventData->mEventQueue.push_back( event );
1300     }
1301     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1302     {
1303       textChanged = BackspaceKeyEvent();
1304     }
1305     else if ( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
1306     {
1307       mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
1308       // Avoids calling the InsertText() method which can delete selected text
1309     }
1310     else if ( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
1311               IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
1312     {
1313       mImpl->ChangeState( EventData::INACTIVE );
1314       // Menu/Home key behaviour does not allow edit mode to resume like Power key
1315       // Avoids calling the InsertText() method which can delete selected text
1316     }
1317     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
1318     {
1319       // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
1320       // and a character is typed after the type of a upper case latin character.
1321
1322       // Do nothing.
1323     }
1324     else
1325     {
1326       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1327
1328       // IMF manager is no longer handling key-events
1329       mImpl->ClearPreEditFlag();
1330
1331       InsertText( keyString, COMMIT );
1332       textChanged = true;
1333     }
1334
1335     if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
1336          ( mImpl->mEventData->mState != EventData::INACTIVE ) )
1337     {
1338       mImpl->ChangeState( EventData::EDITING );
1339     }
1340
1341     mImpl->RequestRelayout();
1342   }
1343
1344   if( textChanged )
1345   {
1346     // Do this last since it provides callbacks into application code
1347     mImpl->mControlInterface.TextChanged();
1348   }
1349
1350   return false;
1351 }
1352
1353 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1354 {
1355   bool removedPrevious( false );
1356   bool maxLengthReached( false );
1357
1358   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1359   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1360                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1361                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1362
1363   // TODO: At the moment the underline runs are only for pre-edit.
1364   mImpl->mVisualModel->mUnderlineRuns.Clear();
1365
1366   Vector<Character> utf32Characters;
1367   Length characterCount( 0u );
1368
1369   // Remove the previous IMF pre-edit (predicitive text)
1370   if( mImpl->mEventData &&
1371       mImpl->mEventData->mPreEditFlag &&
1372       0 != mImpl->mEventData->mPreEditLength )
1373   {
1374     CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1375
1376     removedPrevious = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1377
1378     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1379     mImpl->mEventData->mPreEditLength = 0;
1380   }
1381   else
1382   {
1383     // Remove the previous Selection
1384     removedPrevious = RemoveSelectedText();
1385   }
1386
1387   if( !text.empty() )
1388   {
1389     //  Convert text into UTF-32
1390     utf32Characters.Resize( text.size() );
1391
1392     // This is a bit horrible but std::string returns a (signed) char*
1393     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1394
1395     // Transform a text array encoded in utf8 into an array encoded in utf32.
1396     // It returns the actual number of characters.
1397     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1398     utf32Characters.Resize( characterCount );
1399
1400     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1401     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1402   }
1403
1404   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1405   {
1406     // The placeholder text is no longer needed
1407     if( mImpl->IsShowingPlaceholderText() )
1408     {
1409       ResetText();
1410     }
1411
1412     mImpl->ChangeState( EventData::EDITING );
1413
1414     // Handle the IMF (predicitive text) state changes
1415     if( mImpl->mEventData )
1416     {
1417       if( COMMIT == type )
1418       {
1419         // IMF manager is no longer handling key-events
1420         mImpl->ClearPreEditFlag();
1421       }
1422       else // PRE_EDIT
1423       {
1424         if( !mImpl->mEventData->mPreEditFlag )
1425         {
1426           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1427
1428           // Record the start of the pre-edit text
1429           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1430         }
1431
1432         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1433         mImpl->mEventData->mPreEditFlag = true;
1434
1435         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1436       }
1437     }
1438
1439     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1440
1441     // Restrict new text to fit within Maximum characters setting
1442     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1443     maxLengthReached = ( characterCount > maxSizeOfNewText );
1444
1445     // The cursor position.
1446     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1447
1448     // Insert at current cursor position.
1449     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1450
1451     if( cursorIndex < numberOfCharactersInModel )
1452     {
1453       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1454     }
1455     else
1456     {
1457       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1458     }
1459
1460     cursorIndex += maxSizeOfNewText;
1461
1462     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1463   }
1464
1465   if( 0u == mImpl->mLogicalModel->mText.Count() &&
1466       mImpl->IsPlaceholderAvailable() )
1467   {
1468     // Show place-holder if empty after removing the pre-edit text
1469     ShowPlaceholderText();
1470     mImpl->mEventData->mUpdateCursorPosition = true;
1471     mImpl->ClearPreEditFlag();
1472   }
1473   else if( removedPrevious ||
1474            0 != utf32Characters.Count() )
1475   {
1476     // Queue an inserted event
1477     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1478   }
1479
1480   if( maxLengthReached )
1481   {
1482     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1483
1484     mImpl->ResetImfManager();
1485
1486     // Do this last since it provides callbacks into application code
1487     mImpl->mControlInterface.MaxLengthReached();
1488   }
1489 }
1490
1491 bool Controller::RemoveSelectedText()
1492 {
1493   bool textRemoved( false );
1494
1495   if( EventData::SELECTING == mImpl->mEventData->mState )
1496   {
1497     std::string removedString;
1498     mImpl->RetrieveSelection( removedString, true );
1499
1500     if( !removedString.empty() )
1501     {
1502       textRemoved = true;
1503       mImpl->ChangeState( EventData::EDITING );
1504     }
1505   }
1506
1507   return textRemoved;
1508 }
1509
1510 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1511 {
1512   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1513
1514   if( NULL != mImpl->mEventData )
1515   {
1516     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
1517
1518     if( 1u == tapCount )
1519     {
1520       // This is to avoid unnecessary relayouts when tapping an empty text-field
1521       bool relayoutNeeded( false );
1522
1523       if ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState || EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState )
1524       {
1525         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE);  // If Popup shown hide it here so can be shown again if required.
1526       }
1527
1528       if( mImpl->IsShowingRealText() && ( EventData::INACTIVE !=  mImpl->mEventData->mState ) )
1529       {
1530         // Already in an active state so show a popup
1531         if ( !mImpl->IsClipboardEmpty() )
1532         {
1533           // Shows Paste popup but could show full popup with Selection options. ( EDITING_WITH_POPUP )
1534           mImpl->ChangeState( EventData::EDITING_WITH_PASTE_POPUP );
1535         }
1536         else
1537         {
1538           mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
1539         }
1540         relayoutNeeded = true;
1541       }
1542       else
1543       {
1544         if( mImpl->IsShowingPlaceholderText() &&  !mImpl->IsFocusedPlaceholderAvailable() )
1545         {
1546           // Hide placeholder text
1547           ResetText();
1548         }
1549
1550         if ( EventData::INACTIVE == mImpl->mEventData->mState )
1551         {
1552           mImpl->ChangeState( EventData::EDITING );
1553         }
1554         else if ( !mImpl->IsClipboardEmpty() )
1555         {
1556           mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
1557         }
1558         relayoutNeeded = true;
1559       }
1560
1561       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1562       if( relayoutNeeded )
1563       {
1564         Event event( Event::TAP_EVENT );
1565         event.p1.mUint = tapCount;
1566         event.p2.mFloat = x;
1567         event.p3.mFloat = y;
1568         mImpl->mEventData->mEventQueue.push_back( event );
1569
1570         mImpl->RequestRelayout();
1571       }
1572     }
1573     else if( 2u == tapCount )
1574     {
1575       if( mImpl->mEventData->mSelectionEnabled &&
1576           mImpl->IsShowingRealText() )
1577       {
1578         SelectEvent( x, y, false );
1579       }
1580     }
1581   }
1582
1583   // Reset keyboard as tap event has occurred.
1584   mImpl->ResetImfManager();
1585 }
1586
1587 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1588         // Show cursor and grabhandle on first tap, this matches the behaviour of tapping when already editing
1589 {
1590   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1591
1592   if( mImpl->mEventData )
1593   {
1594     Event event( Event::PAN_EVENT );
1595     event.p1.mInt = state;
1596     event.p2.mFloat = displacement.x;
1597     event.p3.mFloat = displacement.y;
1598     mImpl->mEventData->mEventQueue.push_back( event );
1599
1600     mImpl->RequestRelayout();
1601   }
1602 }
1603
1604 void Controller::LongPressEvent( Gesture::State state, float x, float y  )
1605 {
1606   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
1607
1608   if( state == Gesture::Started &&
1609       mImpl->mEventData )
1610   {
1611     if( ! mImpl->IsShowingRealText() )
1612     {
1613       Event event( Event::LONG_PRESS_EVENT );
1614       event.p1.mInt = state;
1615       mImpl->mEventData->mEventQueue.push_back( event );
1616       mImpl->RequestRelayout();
1617     }
1618     else
1619     {
1620       // The 1st long-press on inactive text-field is treated as tap
1621       if( EventData::INACTIVE == mImpl->mEventData->mState )
1622       {
1623         mImpl->ChangeState( EventData::EDITING );
1624
1625         Event event( Event::TAP_EVENT );
1626         event.p1.mUint = 1;
1627         event.p2.mFloat = x;
1628         event.p3.mFloat = y;
1629         mImpl->mEventData->mEventQueue.push_back( event );
1630
1631         mImpl->RequestRelayout();
1632       }
1633       else
1634       {
1635         // Reset the imf manger to commit the pre-edit before selecting the text.
1636         mImpl->ResetImfManager();
1637
1638         SelectEvent( x, y, false );
1639       }
1640     }
1641   }
1642 }
1643
1644 void Controller::SelectEvent( float x, float y, bool selectAll )
1645 {
1646   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
1647
1648   if( mImpl->mEventData )
1649   {
1650     mImpl->ChangeState( EventData::SELECTING );
1651
1652     if( selectAll )
1653     {
1654       Event event( Event::SELECT_ALL );
1655       mImpl->mEventData->mEventQueue.push_back( event );
1656     }
1657     else
1658     {
1659       Event event( Event::SELECT );
1660       event.p2.mFloat = x;
1661       event.p3.mFloat = y;
1662       mImpl->mEventData->mEventQueue.push_back( event );
1663     }
1664
1665     mImpl->RequestRelayout();
1666   }
1667 }
1668
1669 void Controller::GetTargetSize( Vector2& targetSize )
1670 {
1671   targetSize = mImpl->mVisualModel->mControlSize;
1672 }
1673
1674 void Controller::AddDecoration( Actor& actor, bool needsClipping )
1675 {
1676   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
1677 }
1678
1679 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1680 {
1681   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1682
1683   if( mImpl->mEventData )
1684   {
1685     switch( handleType )
1686     {
1687       case GRAB_HANDLE:
1688       {
1689         Event event( Event::GRAB_HANDLE_EVENT );
1690         event.p1.mUint  = state;
1691         event.p2.mFloat = x;
1692         event.p3.mFloat = y;
1693
1694         mImpl->mEventData->mEventQueue.push_back( event );
1695         break;
1696       }
1697       case LEFT_SELECTION_HANDLE:
1698       {
1699         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1700         event.p1.mUint  = state;
1701         event.p2.mFloat = x;
1702         event.p3.mFloat = y;
1703
1704         mImpl->mEventData->mEventQueue.push_back( event );
1705         break;
1706       }
1707       case RIGHT_SELECTION_HANDLE:
1708       {
1709         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1710         event.p1.mUint  = state;
1711         event.p2.mFloat = x;
1712         event.p3.mFloat = y;
1713
1714         mImpl->mEventData->mEventQueue.push_back( event );
1715         break;
1716       }
1717       case LEFT_SELECTION_HANDLE_MARKER:
1718       case RIGHT_SELECTION_HANDLE_MARKER:
1719       {
1720         // Markers do not move the handles.
1721         break;
1722       }
1723       case HANDLE_TYPE_COUNT:
1724       {
1725         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1726       }
1727     }
1728
1729     mImpl->RequestRelayout();
1730   }
1731 }
1732
1733 void Controller::PasteText( const std::string& stringToPaste )
1734 {
1735   InsertText( stringToPaste, Text::Controller::COMMIT );
1736   mImpl->ChangeState( EventData::EDITING );
1737   mImpl->RequestRelayout();
1738
1739   // Do this last since it provides callbacks into application code
1740   mImpl->mControlInterface.TextChanged();
1741 }
1742
1743 void Controller::PasteClipboardItemEvent()
1744 {
1745   // Retrieve the clipboard contents first
1746   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1747   std::string stringToPaste( notifier.GetContent() );
1748
1749   // Commit the current pre-edit text; the contents of the clipboard should be appended
1750   mImpl->ResetImfManager();
1751
1752   // Paste
1753   PasteText( stringToPaste );
1754 }
1755
1756 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
1757 {
1758   if( NULL == mImpl->mEventData )
1759   {
1760     return;
1761   }
1762
1763   switch( button )
1764   {
1765     case Toolkit::TextSelectionPopup::CUT:
1766     {
1767       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
1768       mImpl->mOperationsPending = ALL_OPERATIONS;
1769
1770       // This is to reset the virtual keyboard to Upper-case
1771       if( 0u == mImpl->mLogicalModel->mText.Count() )
1772       {
1773         NotifyImfManager();
1774       }
1775
1776       if( 0u != mImpl->mLogicalModel->mText.Count() ||
1777           !mImpl->IsPlaceholderAvailable() )
1778       {
1779         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1780       }
1781       else
1782       {
1783         ShowPlaceholderText();
1784         mImpl->mEventData->mUpdateCursorPosition = true;
1785       }
1786       mImpl->RequestRelayout();
1787       mImpl->mControlInterface.TextChanged();
1788       break;
1789     }
1790     case Toolkit::TextSelectionPopup::COPY:
1791     {
1792       mImpl->SendSelectionToClipboard( false ); // Text not modified
1793       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
1794       break;
1795     }
1796     case Toolkit::TextSelectionPopup::PASTE:
1797     {
1798       std::string stringToPaste("");
1799       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
1800       PasteText( stringToPaste );
1801       break;
1802     }
1803     case Toolkit::TextSelectionPopup::SELECT:
1804     {
1805       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1806
1807       if( mImpl->mEventData->mSelectionEnabled  )
1808       {
1809         // Creates a SELECT event.
1810         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
1811       }
1812       break;
1813     }
1814     case Toolkit::TextSelectionPopup::SELECT_ALL:
1815     {
1816       // Creates a SELECT_ALL event
1817       SelectEvent( 0.f, 0.f, true );
1818       break;
1819     }
1820     case Toolkit::TextSelectionPopup::CLIPBOARD:
1821     {
1822       mImpl->ShowClipboard();
1823       break;
1824     }
1825     case Toolkit::TextSelectionPopup::NONE:
1826     {
1827       // Nothing to do.
1828       break;
1829     }
1830   }
1831 }
1832
1833 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1834 {
1835   bool update = false;
1836   bool requestRelayout = false;
1837
1838   std::string text;
1839   unsigned int cursorPosition( 0 );
1840
1841   switch ( imfEvent.eventName )
1842   {
1843     case ImfManager::COMMIT:
1844     {
1845       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1846       update=true;
1847       requestRelayout = true;
1848       break;
1849     }
1850     case ImfManager::PREEDIT:
1851     {
1852       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1853       update = true;
1854       requestRelayout = true;
1855       break;
1856     }
1857     case ImfManager::DELETESURROUNDING:
1858     {
1859       update = RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1860
1861       if( update )
1862       {
1863         if( 0u != mImpl->mLogicalModel->mText.Count() ||
1864             !mImpl->IsPlaceholderAvailable() )
1865         {
1866           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1867         }
1868         else
1869         {
1870           ShowPlaceholderText();
1871           mImpl->mEventData->mUpdateCursorPosition = true;
1872         }
1873       }
1874       requestRelayout = true;
1875       break;
1876     }
1877     case ImfManager::GETSURROUNDING:
1878     {
1879       GetText( text );
1880       cursorPosition = GetLogicalCursorPosition();
1881
1882       imfManager.SetSurroundingText( text );
1883       imfManager.SetCursorPosition( cursorPosition );
1884       break;
1885     }
1886     case ImfManager::VOID:
1887     {
1888       // do nothing
1889       break;
1890     }
1891   } // end switch
1892
1893   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1894   {
1895     GetText( text );
1896     cursorPosition = GetLogicalCursorPosition();
1897   }
1898
1899   if( requestRelayout )
1900   {
1901     mImpl->mOperationsPending = ALL_OPERATIONS;
1902     mImpl->RequestRelayout();
1903
1904     // Do this last since it provides callbacks into application code
1905     mImpl->mControlInterface.TextChanged();
1906   }
1907
1908   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1909
1910   return callbackData;
1911 }
1912
1913 Controller::~Controller()
1914 {
1915   delete mImpl;
1916 }
1917
1918 bool Controller::BackspaceKeyEvent()
1919 {
1920   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1921
1922   // IMF manager is no longer handling key-events
1923   mImpl->ClearPreEditFlag();
1924
1925   bool removed( false );
1926
1927   if( EventData::SELECTING == mImpl->mEventData->mState )
1928   {
1929     removed = RemoveSelectedText();
1930   }
1931   else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
1932   {
1933     // Remove the character before the current cursor position
1934     removed = RemoveText( -1, 1 );
1935   }
1936
1937   if( removed )
1938   {
1939     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE RemovedText\n", this );
1940     // Notifiy the IMF manager after text changed
1941     // Automatic  Upper-case and restarting prediction on an existing word require this.
1942     NotifyImfManager();
1943
1944     if( 0u != mImpl->mLogicalModel->mText.Count() ||
1945         !mImpl->IsPlaceholderAvailable() )
1946     {
1947       mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1948     }
1949     else
1950     {
1951       ShowPlaceholderText();
1952       mImpl->mEventData->mUpdateCursorPosition = true;
1953     }
1954   }
1955
1956   return removed;
1957 }
1958
1959 void Controller::NotifyImfManager()
1960 {
1961   if( mImpl->mEventData )
1962   {
1963     if( mImpl->mEventData->mImfManager )
1964     {
1965       // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
1966       std::string text;
1967       GetText( text );
1968       mImpl->mEventData->mImfManager.SetSurroundingText( text );
1969
1970       mImpl->mEventData->mImfManager.SetCursorPosition( GetLogicalCursorPosition() );
1971       mImpl->mEventData->mImfManager.NotifyCursorPosition();
1972     }
1973   }
1974 }
1975
1976 void Controller::ShowPlaceholderText()
1977 {
1978   if( mImpl->IsPlaceholderAvailable() )
1979   {
1980     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1981
1982     mImpl->mEventData->mIsShowingPlaceholderText = true;
1983
1984     // Disable handles when showing place-holder text
1985     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1986     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1987     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1988
1989     const char* text( NULL );
1990     size_t size( 0 );
1991
1992     // TODO - Switch placeholder text styles when changing state
1993     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1994         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1995     {
1996       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1997       size = mImpl->mEventData->mPlaceholderTextActive.size();
1998     }
1999     else
2000     {
2001       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
2002       size = mImpl->mEventData->mPlaceholderTextInactive.size();
2003     }
2004
2005     // Reset model for showing placeholder.
2006     mImpl->mLogicalModel->mText.Clear();
2007     ClearModelData();
2008     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
2009
2010     // Convert text into UTF-32
2011     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
2012     utf32Characters.Resize( size );
2013
2014     // This is a bit horrible but std::string returns a (signed) char*
2015     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
2016
2017     // Transform a text array encoded in utf8 into an array encoded in utf32.
2018     // It returns the actual number of characters.
2019     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
2020     utf32Characters.Resize( characterCount );
2021
2022     // Reset the cursor position
2023     mImpl->mEventData->mPrimaryCursorPosition = 0;
2024
2025     // The natural size needs to be re-calculated.
2026     mImpl->mRecalculateNaturalSize = true;
2027
2028     // Apply modifications to the model
2029     mImpl->mOperationsPending = ALL_OPERATIONS;
2030
2031     // Update the rest of the model during size negotiation
2032     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
2033   }
2034 }
2035
2036 void Controller::ClearModelData()
2037 {
2038   // n.b. This does not Clear the mText from mLogicalModel
2039   mImpl->mLogicalModel->mScriptRuns.Clear();
2040   mImpl->mLogicalModel->mFontRuns.Clear();
2041   mImpl->mLogicalModel->mLineBreakInfo.Clear();
2042   mImpl->mLogicalModel->mWordBreakInfo.Clear();
2043   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
2044   mImpl->mLogicalModel->mCharacterDirections.Clear();
2045   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
2046   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
2047   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
2048   mImpl->mVisualModel->mGlyphs.Clear();
2049   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
2050   mImpl->mVisualModel->mCharactersToGlyph.Clear();
2051   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
2052   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
2053   mImpl->mVisualModel->mGlyphPositions.Clear();
2054   mImpl->mVisualModel->mLines.Clear();
2055   mImpl->mVisualModel->ClearCaches();
2056 }
2057
2058 void Controller::ClearFontData()
2059 {
2060   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
2061   mImpl->mLogicalModel->mFontRuns.Clear();
2062   mImpl->mVisualModel->mGlyphs.Clear();
2063   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
2064   mImpl->mVisualModel->mCharactersToGlyph.Clear();
2065   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
2066   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
2067   mImpl->mVisualModel->mGlyphPositions.Clear();
2068   mImpl->mVisualModel->mLines.Clear();
2069   mImpl->mVisualModel->ClearCaches();
2070 }
2071
2072 Controller::Controller( ControlInterface& controlInterface )
2073 : mImpl( NULL )
2074 {
2075   mImpl = new Controller::Impl( controlInterface );
2076 }
2077
2078 } // namespace Text
2079
2080 } // namespace Toolkit
2081
2082 } // namespace Dali