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