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