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