37110dcb4932ec1aa13e0404a0597f240814ecc6
[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( 0u,
1535                                                        mImpl->mLogicalModel->mText.Count() );
1536
1537           // Re-layout the text. Reorder those lines with right to left characters.
1538           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1539                                                          startIndex,
1540                                                          requestedNumberOfCharacters,
1541                                                          glyphPositions );
1542
1543         }
1544       } // REORDER
1545
1546       // Sets the actual size.
1547       if( UPDATE_ACTUAL_SIZE & operations )
1548       {
1549         mImpl->mVisualModel->SetLayoutSize( layoutSize );
1550       }
1551     } // view updated
1552
1553     // Store the size used to layout the text.
1554     mImpl->mVisualModel->mControlSize = size;
1555   }
1556   else
1557   {
1558     layoutSize = mImpl->mVisualModel->GetLayoutSize();
1559   }
1560
1561   if( ALIGN & operations )
1562   {
1563     // The laid-out lines.
1564     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1565
1566     mImpl->mLayoutEngine.Align( size,
1567                                 startIndex,
1568                                 requestedNumberOfCharacters,
1569                                 lines );
1570
1571     viewUpdated = true;
1572   }
1573
1574   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
1575   return viewUpdated;
1576 }
1577
1578 void Controller::SetMultiLineEnabled( bool enable )
1579 {
1580   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
1581
1582   if( layout != mImpl->mLayoutEngine.GetLayout() )
1583   {
1584     // Set the layout type.
1585     mImpl->mLayoutEngine.SetLayout( layout );
1586
1587     // Set the flags to redo the layout operations
1588     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
1589                                                                           UPDATE_ACTUAL_SIZE |
1590                                                                           ALIGN              |
1591                                                                           REORDER );
1592
1593     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
1594
1595     mImpl->RequestRelayout();
1596   }
1597 }
1598
1599 bool Controller::IsMultiLineEnabled() const
1600 {
1601   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
1602 }
1603
1604 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
1605 {
1606   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1607   {
1608     // Set the alignment.
1609     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1610
1611     // Set the flag to redo the alignment operation.
1612     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1613
1614     mImpl->RequestRelayout();
1615   }
1616 }
1617
1618 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1619 {
1620   return mImpl->mLayoutEngine.GetHorizontalAlignment();
1621 }
1622
1623 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1624 {
1625   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1626   {
1627     // Set the alignment.
1628     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1629
1630     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1631
1632     mImpl->RequestRelayout();
1633   }
1634 }
1635
1636 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1637 {
1638   return mImpl->mLayoutEngine.GetVerticalAlignment();
1639 }
1640
1641 void Controller::CalculateTextAlignment( const Size& controlSize )
1642 {
1643   Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
1644
1645   if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
1646   {
1647     // Get the line height of the default font.
1648     layoutSize.height = mImpl->GetDefaultFontLineHeight();
1649   }
1650
1651   if( LayoutEngine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
1652   {
1653     // Get the direction of the first character.
1654     const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1655
1656     // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1657     LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1658     if( firstParagraphDirection )
1659     {
1660       switch( horizontalAlignment )
1661       {
1662         case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1663         {
1664           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1665           break;
1666         }
1667         case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1668         {
1669           // Nothing to do.
1670           break;
1671         }
1672         case LayoutEngine::HORIZONTAL_ALIGN_END:
1673         {
1674           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1675           break;
1676         }
1677       }
1678     }
1679
1680     switch( horizontalAlignment )
1681     {
1682       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1683       {
1684         mImpl->mAlignmentOffset.x = 0.f;
1685         break;
1686       }
1687       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1688       {
1689         mImpl->mAlignmentOffset.x = floorf( 0.5f * ( controlSize.width - layoutSize.width ) ); // try to avoid pixel alignment.
1690         break;
1691       }
1692       case LayoutEngine::HORIZONTAL_ALIGN_END:
1693       {
1694         mImpl->mAlignmentOffset.x = controlSize.width - layoutSize.width;
1695         break;
1696       }
1697     }
1698   }
1699
1700   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1701   switch( verticalAlignment )
1702   {
1703     case LayoutEngine::VERTICAL_ALIGN_TOP:
1704     {
1705       mImpl->mAlignmentOffset.y = 0.f;
1706       break;
1707     }
1708     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1709     {
1710       mImpl->mAlignmentOffset.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
1711       break;
1712     }
1713     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1714     {
1715       mImpl->mAlignmentOffset.y = controlSize.height - layoutSize.height;
1716       break;
1717     }
1718   }
1719 }
1720
1721 LayoutEngine& Controller::GetLayoutEngine()
1722 {
1723   return mImpl->mLayoutEngine;
1724 }
1725
1726 View& Controller::GetView()
1727 {
1728   return mImpl->mView;
1729 }
1730
1731 void Controller::KeyboardFocusGainEvent()
1732 {
1733   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1734
1735   if( NULL != mImpl->mEventData )
1736   {
1737     if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1738         ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1739     {
1740       mImpl->ChangeState( EventData::EDITING );
1741       mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1742     }
1743
1744     if( mImpl->IsShowingPlaceholderText() )
1745     {
1746       // Show alternative placeholder-text when editing
1747       ShowPlaceholderText();
1748     }
1749
1750     mImpl->RequestRelayout();
1751   }
1752 }
1753
1754 void Controller::KeyboardFocusLostEvent()
1755 {
1756   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1757
1758   if( NULL != mImpl->mEventData )
1759   {
1760     if( EventData::INTERRUPTED != mImpl->mEventData->mState )
1761     {
1762       mImpl->ChangeState( EventData::INACTIVE );
1763
1764       if( !mImpl->IsShowingRealText() )
1765       {
1766         // Revert to regular placeholder-text when not editing
1767         ShowPlaceholderText();
1768       }
1769     }
1770   }
1771   mImpl->RequestRelayout();
1772 }
1773
1774 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1775 {
1776   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1777
1778   bool textChanged( false );
1779
1780   if( ( NULL != mImpl->mEventData ) &&
1781       ( keyEvent.state == KeyEvent::Down ) )
1782   {
1783     int keyCode = keyEvent.keyCode;
1784     const std::string& keyString = keyEvent.keyPressed;
1785
1786     // Pre-process to separate modifying events from non-modifying input events.
1787     if( Dali::DALI_KEY_ESCAPE == keyCode )
1788     {
1789       // Escape key is a special case which causes focus loss
1790       KeyboardFocusLostEvent();
1791     }
1792     else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
1793              ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
1794              ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
1795              ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
1796     {
1797       Event event( Event::CURSOR_KEY_EVENT );
1798       event.p1.mInt = keyCode;
1799       mImpl->mEventData->mEventQueue.push_back( event );
1800     }
1801     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1802     {
1803       textChanged = BackspaceKeyEvent();
1804     }
1805     else if( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
1806     {
1807       mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
1808       // Avoids calling the InsertText() method which can delete selected text
1809     }
1810     else if( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
1811              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
1812     {
1813       mImpl->ChangeState( EventData::INACTIVE );
1814       // Menu/Home key behaviour does not allow edit mode to resume like Power key
1815       // Avoids calling the InsertText() method which can delete selected text
1816     }
1817     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
1818     {
1819       // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
1820       // and a character is typed after the type of a upper case latin character.
1821
1822       // Do nothing.
1823     }
1824     else
1825     {
1826       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1827
1828       // IMF manager is no longer handling key-events
1829       mImpl->ClearPreEditFlag();
1830
1831       InsertText( keyString, COMMIT );
1832       textChanged = true;
1833     }
1834
1835     if( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
1836         ( mImpl->mEventData->mState != EventData::INACTIVE ) )
1837     {
1838       mImpl->ChangeState( EventData::EDITING );
1839     }
1840
1841     mImpl->RequestRelayout();
1842   }
1843
1844   if( textChanged )
1845   {
1846     // Do this last since it provides callbacks into application code
1847     mImpl->mControlInterface.TextChanged();
1848   }
1849
1850   return true;
1851 }
1852
1853 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1854 {
1855   bool removedPrevious( false );
1856   bool maxLengthReached( false );
1857
1858   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1859
1860   if( NULL == mImpl->mEventData )
1861   {
1862     return;
1863   }
1864
1865   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1866                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1867                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1868
1869   // TODO: At the moment the underline runs are only for pre-edit.
1870   mImpl->mVisualModel->mUnderlineRuns.Clear();
1871
1872   Vector<Character> utf32Characters;
1873   Length characterCount( 0u );
1874
1875   // Remove the previous IMF pre-edit (predicitive text)
1876   if( mImpl->mEventData->mPreEditFlag &&
1877       ( 0u != mImpl->mEventData->mPreEditLength ) )
1878   {
1879     const CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1880
1881     removedPrevious = RemoveText( -static_cast<int>( offset ),
1882                                   mImpl->mEventData->mPreEditLength,
1883                                   DONT_UPDATE_INPUT_STYLE );
1884
1885     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1886     mImpl->mEventData->mPreEditLength = 0u;
1887   }
1888   else
1889   {
1890     // Remove the previous Selection
1891     removedPrevious = RemoveSelectedText();
1892   }
1893
1894   if( !text.empty() )
1895   {
1896     //  Convert text into UTF-32
1897     utf32Characters.Resize( text.size() );
1898
1899     // This is a bit horrible but std::string returns a (signed) char*
1900     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1901
1902     // Transform a text array encoded in utf8 into an array encoded in utf32.
1903     // It returns the actual number of characters.
1904     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1905     utf32Characters.Resize( characterCount );
1906
1907     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1908     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1909   }
1910
1911   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1912   {
1913     // The placeholder text is no longer needed
1914     if( mImpl->IsShowingPlaceholderText() )
1915     {
1916       ResetText();
1917     }
1918
1919     mImpl->ChangeState( EventData::EDITING );
1920
1921     // Handle the IMF (predicitive text) state changes
1922     if( COMMIT == type )
1923     {
1924       // IMF manager is no longer handling key-events
1925       mImpl->ClearPreEditFlag();
1926     }
1927     else // PRE_EDIT
1928     {
1929       if( !mImpl->mEventData->mPreEditFlag )
1930       {
1931         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1932
1933         // Record the start of the pre-edit text
1934         mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1935       }
1936
1937       mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1938       mImpl->mEventData->mPreEditFlag = true;
1939
1940       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1941     }
1942
1943     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1944
1945     // Restrict new text to fit within Maximum characters setting.
1946     Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1947     maxLengthReached = ( characterCount > maxSizeOfNewText );
1948
1949     // The cursor position.
1950     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1951
1952     // Update the text's style.
1953
1954     // Updates the text style runs by adding characters.
1955     mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
1956
1957     // Get the character index from the cursor index.
1958     const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
1959
1960     // Retrieve the text's style for the given index.
1961     InputStyle style;
1962     mImpl->RetrieveDefaultInputStyle( style );
1963     mImpl->mLogicalModel->RetrieveStyle( styleIndex, style );
1964
1965     // Whether to add a new text color run.
1966     const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
1967
1968     // Whether to add a new font run.
1969     const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
1970     const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
1971     const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
1972     const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
1973     const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
1974
1975     // Add style runs.
1976     if( addColorRun )
1977     {
1978       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
1979       mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
1980
1981       ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
1982       colorRun.color = mImpl->mEventData->mInputStyle.textColor;
1983       colorRun.characterRun.characterIndex = cursorIndex;
1984       colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
1985     }
1986
1987     if( addFontNameRun   ||
1988         addFontWeightRun ||
1989         addFontWidthRun  ||
1990         addFontSlantRun  ||
1991         addFontSizeRun )
1992     {
1993       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mFontDescriptionRuns.Count();
1994       mImpl->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
1995
1996       FontDescriptionRun& fontDescriptionRun = *( mImpl->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
1997
1998       if( addFontNameRun )
1999       {
2000         fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
2001         fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
2002         memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
2003         fontDescriptionRun.familyDefined = true;
2004
2005         // The memory allocated for the font family name is freed when the font description is removed from the logical model.
2006       }
2007
2008       if( addFontWeightRun )
2009       {
2010         fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
2011         fontDescriptionRun.weightDefined = true;
2012       }
2013
2014       if( addFontWidthRun )
2015       {
2016         fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
2017         fontDescriptionRun.widthDefined = true;
2018       }
2019
2020       if( addFontSlantRun )
2021       {
2022         fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
2023         fontDescriptionRun.slantDefined = true;
2024       }
2025
2026       if( addFontSizeRun )
2027       {
2028         fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
2029         fontDescriptionRun.sizeDefined = true;
2030       }
2031
2032       fontDescriptionRun.characterRun.characterIndex = cursorIndex;
2033       fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
2034     }
2035
2036     // Insert at current cursor position.
2037     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
2038
2039     if( cursorIndex < numberOfCharactersInModel )
2040     {
2041       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
2042     }
2043     else
2044     {
2045       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
2046     }
2047
2048     // Mark the first paragraph to be updated.
2049     mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
2050     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
2051
2052     // Update the cursor index.
2053     cursorIndex += maxSizeOfNewText;
2054
2055     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
2056   }
2057
2058   if( ( 0u == mImpl->mLogicalModel->mText.Count() ) &&
2059       mImpl->IsPlaceholderAvailable() )
2060   {
2061     // Show place-holder if empty after removing the pre-edit text
2062     ShowPlaceholderText();
2063     mImpl->mEventData->mUpdateCursorPosition = true;
2064     mImpl->ClearPreEditFlag();
2065   }
2066   else if( removedPrevious ||
2067            ( 0 != utf32Characters.Count() ) )
2068   {
2069     // Queue an inserted event
2070     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
2071   }
2072
2073   if( maxLengthReached )
2074   {
2075     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
2076
2077     mImpl->ResetImfManager();
2078
2079     // Do this last since it provides callbacks into application code
2080     mImpl->mControlInterface.MaxLengthReached();
2081   }
2082 }
2083
2084 bool Controller::RemoveSelectedText()
2085 {
2086   bool textRemoved( false );
2087
2088   if( EventData::SELECTING == mImpl->mEventData->mState )
2089   {
2090     std::string removedString;
2091     mImpl->RetrieveSelection( removedString, true );
2092
2093     if( !removedString.empty() )
2094     {
2095       textRemoved = true;
2096       mImpl->ChangeState( EventData::EDITING );
2097     }
2098   }
2099
2100   return textRemoved;
2101 }
2102
2103 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2104 {
2105   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
2106
2107   if( NULL != mImpl->mEventData )
2108   {
2109     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
2110
2111     if( 1u == tapCount )
2112     {
2113       // This is to avoid unnecessary relayouts when tapping an empty text-field
2114       bool relayoutNeeded( false );
2115
2116       if( ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) ||
2117           ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
2118       {
2119         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE);  // If Popup shown hide it here so can be shown again if required.
2120       }
2121
2122       if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != mImpl->mEventData->mState ) )
2123       {
2124         // Already in an active state so show a popup
2125         if( !mImpl->IsClipboardEmpty() )
2126         {
2127           // Shows Paste popup but could show full popup with Selection options. ( EDITING_WITH_POPUP )
2128           mImpl->ChangeState( EventData::EDITING_WITH_PASTE_POPUP );
2129         }
2130         else
2131         {
2132           mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2133         }
2134         relayoutNeeded = true;
2135       }
2136       else
2137       {
2138         if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
2139         {
2140           // Hide placeholder text
2141           ResetText();
2142         }
2143
2144         if( EventData::INACTIVE == mImpl->mEventData->mState )
2145         {
2146           mImpl->ChangeState( EventData::EDITING );
2147         }
2148         else if( !mImpl->IsClipboardEmpty() )
2149         {
2150           mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
2151         }
2152         relayoutNeeded = true;
2153       }
2154
2155       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
2156       if( relayoutNeeded )
2157       {
2158         Event event( Event::TAP_EVENT );
2159         event.p1.mUint = tapCount;
2160         event.p2.mFloat = x;
2161         event.p3.mFloat = y;
2162         mImpl->mEventData->mEventQueue.push_back( event );
2163
2164         mImpl->RequestRelayout();
2165       }
2166     }
2167     else if( 2u == tapCount )
2168     {
2169       if( mImpl->mEventData->mSelectionEnabled &&
2170           mImpl->IsShowingRealText() )
2171       {
2172         SelectEvent( x, y, false );
2173       }
2174     }
2175   }
2176
2177   // Reset keyboard as tap event has occurred.
2178   mImpl->ResetImfManager();
2179 }
2180
2181 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2182         // Show cursor and grabhandle on first tap, this matches the behaviour of tapping when already editing
2183 {
2184   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
2185
2186   if( NULL != mImpl->mEventData )
2187   {
2188     Event event( Event::PAN_EVENT );
2189     event.p1.mInt = state;
2190     event.p2.mFloat = displacement.x;
2191     event.p3.mFloat = displacement.y;
2192     mImpl->mEventData->mEventQueue.push_back( event );
2193
2194     mImpl->RequestRelayout();
2195   }
2196 }
2197
2198 void Controller::LongPressEvent( Gesture::State state, float x, float y  )
2199 {
2200   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
2201
2202   if( ( state == Gesture::Started ) &&
2203       ( NULL != mImpl->mEventData ) )
2204   {
2205     if( !mImpl->IsShowingRealText() )
2206     {
2207       Event event( Event::LONG_PRESS_EVENT );
2208       event.p1.mInt = state;
2209       mImpl->mEventData->mEventQueue.push_back( event );
2210       mImpl->RequestRelayout();
2211     }
2212     else
2213     {
2214       // The 1st long-press on inactive text-field is treated as tap
2215       if( EventData::INACTIVE == mImpl->mEventData->mState )
2216       {
2217         mImpl->ChangeState( EventData::EDITING );
2218
2219         Event event( Event::TAP_EVENT );
2220         event.p1.mUint = 1;
2221         event.p2.mFloat = x;
2222         event.p3.mFloat = y;
2223         mImpl->mEventData->mEventQueue.push_back( event );
2224
2225         mImpl->RequestRelayout();
2226       }
2227       else
2228       {
2229         // Reset the imf manger to commit the pre-edit before selecting the text.
2230         mImpl->ResetImfManager();
2231
2232         SelectEvent( x, y, false );
2233       }
2234     }
2235   }
2236 }
2237
2238 void Controller::SelectEvent( float x, float y, bool selectAll )
2239 {
2240   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
2241
2242   if( NULL != mImpl->mEventData )
2243   {
2244     mImpl->ChangeState( EventData::SELECTING );
2245
2246     if( selectAll )
2247     {
2248       Event event( Event::SELECT_ALL );
2249       mImpl->mEventData->mEventQueue.push_back( event );
2250     }
2251     else
2252     {
2253       Event event( Event::SELECT );
2254       event.p2.mFloat = x;
2255       event.p3.mFloat = y;
2256       mImpl->mEventData->mEventQueue.push_back( event );
2257     }
2258
2259     mImpl->RequestRelayout();
2260   }
2261 }
2262
2263 void Controller::GetTargetSize( Vector2& targetSize )
2264 {
2265   targetSize = mImpl->mVisualModel->mControlSize;
2266 }
2267
2268 void Controller::AddDecoration( Actor& actor, bool needsClipping )
2269 {
2270   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
2271 }
2272
2273 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
2274 {
2275   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
2276
2277   if( NULL != mImpl->mEventData )
2278   {
2279     switch( handleType )
2280     {
2281       case GRAB_HANDLE:
2282       {
2283         Event event( Event::GRAB_HANDLE_EVENT );
2284         event.p1.mUint  = state;
2285         event.p2.mFloat = x;
2286         event.p3.mFloat = y;
2287
2288         mImpl->mEventData->mEventQueue.push_back( event );
2289         break;
2290       }
2291       case LEFT_SELECTION_HANDLE:
2292       {
2293         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
2294         event.p1.mUint  = state;
2295         event.p2.mFloat = x;
2296         event.p3.mFloat = y;
2297
2298         mImpl->mEventData->mEventQueue.push_back( event );
2299         break;
2300       }
2301       case RIGHT_SELECTION_HANDLE:
2302       {
2303         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
2304         event.p1.mUint  = state;
2305         event.p2.mFloat = x;
2306         event.p3.mFloat = y;
2307
2308         mImpl->mEventData->mEventQueue.push_back( event );
2309         break;
2310       }
2311       case LEFT_SELECTION_HANDLE_MARKER:
2312       case RIGHT_SELECTION_HANDLE_MARKER:
2313       {
2314         // Markers do not move the handles.
2315         break;
2316       }
2317       case HANDLE_TYPE_COUNT:
2318       {
2319         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
2320       }
2321     }
2322
2323     mImpl->RequestRelayout();
2324   }
2325 }
2326
2327 void Controller::PasteText( const std::string& stringToPaste )
2328 {
2329   InsertText( stringToPaste, Text::Controller::COMMIT );
2330   mImpl->ChangeState( EventData::EDITING );
2331   mImpl->RequestRelayout();
2332
2333   // Do this last since it provides callbacks into application code
2334   mImpl->mControlInterface.TextChanged();
2335 }
2336
2337 void Controller::PasteClipboardItemEvent()
2338 {
2339   // Retrieve the clipboard contents first
2340   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
2341   std::string stringToPaste( notifier.GetContent() );
2342
2343   // Commit the current pre-edit text; the contents of the clipboard should be appended
2344   mImpl->ResetImfManager();
2345
2346   // Paste
2347   PasteText( stringToPaste );
2348 }
2349
2350 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
2351 {
2352   if( NULL == mImpl->mEventData )
2353   {
2354     return;
2355   }
2356
2357   switch( button )
2358   {
2359     case Toolkit::TextSelectionPopup::CUT:
2360     {
2361       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
2362       mImpl->mOperationsPending = ALL_OPERATIONS;
2363
2364       // This is to reset the virtual keyboard to Upper-case
2365       if( 0u == mImpl->mLogicalModel->mText.Count() )
2366       {
2367         NotifyImfManager();
2368       }
2369
2370       if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2371           !mImpl->IsPlaceholderAvailable() )
2372       {
2373         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2374       }
2375       else
2376       {
2377         ShowPlaceholderText();
2378         mImpl->mEventData->mUpdateCursorPosition = true;
2379       }
2380       mImpl->RequestRelayout();
2381       mImpl->mControlInterface.TextChanged();
2382       break;
2383     }
2384     case Toolkit::TextSelectionPopup::COPY:
2385     {
2386       mImpl->SendSelectionToClipboard( false ); // Text not modified
2387       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
2388       break;
2389     }
2390     case Toolkit::TextSelectionPopup::PASTE:
2391     {
2392       std::string stringToPaste("");
2393       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
2394       PasteText( stringToPaste );
2395       break;
2396     }
2397     case Toolkit::TextSelectionPopup::SELECT:
2398     {
2399       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2400
2401       if( mImpl->mEventData->mSelectionEnabled )
2402       {
2403         // Creates a SELECT event.
2404         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
2405       }
2406       break;
2407     }
2408     case Toolkit::TextSelectionPopup::SELECT_ALL:
2409     {
2410       // Creates a SELECT_ALL event
2411       SelectEvent( 0.f, 0.f, true );
2412       break;
2413     }
2414     case Toolkit::TextSelectionPopup::CLIPBOARD:
2415     {
2416       mImpl->ShowClipboard();
2417       break;
2418     }
2419     case Toolkit::TextSelectionPopup::NONE:
2420     {
2421       // Nothing to do.
2422       break;
2423     }
2424   }
2425 }
2426
2427 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2428 {
2429   bool update = false;
2430   bool requestRelayout = false;
2431
2432   std::string text;
2433   unsigned int cursorPosition = 0u;
2434
2435   switch( imfEvent.eventName )
2436   {
2437     case ImfManager::COMMIT:
2438     {
2439       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
2440       update = true;
2441       requestRelayout = true;
2442       break;
2443     }
2444     case ImfManager::PREEDIT:
2445     {
2446       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
2447       update = true;
2448       requestRelayout = true;
2449       break;
2450     }
2451     case ImfManager::DELETESURROUNDING:
2452     {
2453       update = RemoveText( imfEvent.cursorOffset,
2454                            imfEvent.numberOfChars,
2455                            DONT_UPDATE_INPUT_STYLE );
2456
2457       if( update )
2458       {
2459         if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2460             !mImpl->IsPlaceholderAvailable() )
2461         {
2462           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2463         }
2464         else
2465         {
2466           ShowPlaceholderText();
2467           mImpl->mEventData->mUpdateCursorPosition = true;
2468         }
2469       }
2470       requestRelayout = true;
2471       break;
2472     }
2473     case ImfManager::GETSURROUNDING:
2474     {
2475       GetText( text );
2476       cursorPosition = GetLogicalCursorPosition();
2477
2478       imfManager.SetSurroundingText( text );
2479       imfManager.SetCursorPosition( cursorPosition );
2480       break;
2481     }
2482     case ImfManager::VOID:
2483     {
2484       // do nothing
2485       break;
2486     }
2487   } // end switch
2488
2489   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
2490   {
2491     GetText( text );
2492     cursorPosition = GetLogicalCursorPosition();
2493   }
2494
2495   if( requestRelayout )
2496   {
2497     mImpl->mOperationsPending = ALL_OPERATIONS;
2498     mImpl->RequestRelayout();
2499
2500     // Do this last since it provides callbacks into application code
2501     mImpl->mControlInterface.TextChanged();
2502   }
2503
2504   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
2505
2506   return callbackData;
2507 }
2508
2509 Controller::~Controller()
2510 {
2511   delete mImpl;
2512 }
2513
2514 bool Controller::BackspaceKeyEvent()
2515 {
2516   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
2517
2518   bool removed = false;
2519
2520   if( NULL == mImpl->mEventData )
2521   {
2522     return removed;
2523   }
2524
2525   // IMF manager is no longer handling key-events
2526   mImpl->ClearPreEditFlag();
2527
2528   if( EventData::SELECTING == mImpl->mEventData->mState )
2529   {
2530     removed = RemoveSelectedText();
2531   }
2532   else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
2533   {
2534     // Remove the character before the current cursor position
2535     removed = RemoveText( -1,
2536                           1,
2537                           UPDATE_INPUT_STYLE );
2538   }
2539
2540   if( removed )
2541   {
2542     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE RemovedText\n", this );
2543     // Notifiy the IMF manager after text changed
2544     // Automatic  Upper-case and restarting prediction on an existing word require this.
2545     NotifyImfManager();
2546
2547     if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2548         !mImpl->IsPlaceholderAvailable() )
2549     {
2550       mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2551     }
2552     else
2553     {
2554       ShowPlaceholderText();
2555       mImpl->mEventData->mUpdateCursorPosition = true;
2556     }
2557   }
2558
2559   return removed;
2560 }
2561
2562 void Controller::NotifyImfManager()
2563 {
2564   if( NULL != mImpl->mEventData )
2565   {
2566     if( mImpl->mEventData->mImfManager )
2567     {
2568       // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2569       std::string text;
2570       GetText( text );
2571       mImpl->mEventData->mImfManager.SetSurroundingText( text );
2572
2573       mImpl->mEventData->mImfManager.SetCursorPosition( GetLogicalCursorPosition() );
2574       mImpl->mEventData->mImfManager.NotifyCursorPosition();
2575     }
2576   }
2577 }
2578
2579 void Controller::ShowPlaceholderText()
2580 {
2581   if( mImpl->IsPlaceholderAvailable() )
2582   {
2583     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
2584
2585     if( NULL == mImpl->mEventData )
2586     {
2587       return;
2588     }
2589
2590     mImpl->mEventData->mIsShowingPlaceholderText = true;
2591
2592     // Disable handles when showing place-holder text
2593     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2594     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2595     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2596
2597     const char* text( NULL );
2598     size_t size( 0 );
2599
2600     // TODO - Switch placeholder text styles when changing state
2601     if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
2602         ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
2603     {
2604       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
2605       size = mImpl->mEventData->mPlaceholderTextActive.size();
2606     }
2607     else
2608     {
2609       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
2610       size = mImpl->mEventData->mPlaceholderTextInactive.size();
2611     }
2612
2613     mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2614     mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2615
2616     // Reset model for showing placeholder.
2617     mImpl->mLogicalModel->mText.Clear();
2618     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
2619
2620     // Convert text into UTF-32
2621     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
2622     utf32Characters.Resize( size );
2623
2624     // This is a bit horrible but std::string returns a (signed) char*
2625     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
2626
2627     // Transform a text array encoded in utf8 into an array encoded in utf32.
2628     // It returns the actual number of characters.
2629     const Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
2630     utf32Characters.Resize( characterCount );
2631
2632     // The characters to be added.
2633     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = characterCount;
2634
2635     // Reset the cursor position
2636     mImpl->mEventData->mPrimaryCursorPosition = 0;
2637
2638     // The natural size needs to be re-calculated.
2639     mImpl->mRecalculateNaturalSize = true;
2640
2641     // Apply modifications to the model
2642     mImpl->mOperationsPending = ALL_OPERATIONS;
2643
2644     // Update the rest of the model during size negotiation
2645     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
2646   }
2647 }
2648
2649 void Controller::ClearFontData()
2650 {
2651   if( mImpl->mFontDefaults )
2652   {
2653     mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
2654   }
2655
2656   // Set flags to update the model.
2657   mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2658   mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2659   mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mLogicalModel->mText.Count();
2660
2661   mImpl->mTextUpdateInfo.mClearAll = true;
2662   mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
2663   mImpl->mRecalculateNaturalSize = true;
2664
2665   mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2666                                                            VALIDATE_FONTS            |
2667                                                            SHAPE_TEXT                |
2668                                                            GET_GLYPH_METRICS         |
2669                                                            LAYOUT                    |
2670                                                            UPDATE_ACTUAL_SIZE        |
2671                                                            REORDER                   |
2672                                                            ALIGN );
2673 }
2674
2675 void Controller::ClearStyleData()
2676 {
2677   mImpl->mLogicalModel->mColorRuns.Clear();
2678   mImpl->mLogicalModel->ClearFontDescriptionRuns();
2679 }
2680
2681 Controller::Controller( ControlInterface& controlInterface )
2682 : mImpl( NULL )
2683 {
2684   mImpl = new Controller::Impl( controlInterface );
2685 }
2686
2687 } // namespace Text
2688
2689 } // namespace Toolkit
2690
2691 } // namespace Dali