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