Merge "Fix VD prevent issues" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <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   // Keep the current offset and alignment as it will be used to update the decorator's positions.
674   Vector2 offset;
675   if( mImpl->mEventData )
676   {
677     offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
678   }
679
680   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
681   CalculateTextAlignment( size );
682
683   if( mImpl->mEventData )
684   {
685     // If there is a nex size, the scroll position needs to be clamped.
686     mImpl->ClampHorizontalScroll( layoutSize );
687
688     // Update the decorator's positions.
689     mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
690
691     // Move the cursor, grab handle etc.
692     updated = mImpl->ProcessInputEvents() || updated;
693   }
694
695   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
696   return updated;
697 }
698
699 void Controller::ProcessModifyEvents()
700 {
701   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
702
703   for( unsigned int i=0; i<events.size(); ++i )
704   {
705     if( ModifyEvent::TEXT_REPLACED == events[0].type )
706     {
707       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
708       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
709
710       TextReplacedEvent();
711     }
712     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
713     {
714       TextInsertedEvent();
715     }
716     else if( ModifyEvent::TEXT_DELETED == events[0].type )
717     {
718       // Placeholder-text cannot be deleted
719       if( !mImpl->IsShowingPlaceholderText() )
720       {
721         TextDeletedEvent();
722       }
723     }
724   }
725
726   if( mImpl->mEventData &&
727       0 != events.size() )
728   {
729     // When the text is being modified, delay cursor blinking
730     mImpl->mEventData->mDecorator->DelayCursorBlink();
731   }
732
733   // Discard temporary text
734   events.clear();
735 }
736
737 void Controller::ResetText()
738 {
739   // Reset buffers.
740   mImpl->mLogicalModel->mText.Clear();
741   ClearModelData();
742
743   // We have cleared everything including the placeholder-text
744   mImpl->PlaceholderCleared();
745
746   // The natural size needs to be re-calculated.
747   mImpl->mRecalculateNaturalSize = true;
748
749   // Apply modifications to the model
750   mImpl->mOperationsPending = ALL_OPERATIONS;
751 }
752
753 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
754 {
755   // Reset the cursor position
756   if( NULL != mImpl->mEventData )
757   {
758     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
759
760     // Update the cursor if it's in editing mode.
761     if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
762         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
763         ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
764     {
765       mImpl->mEventData->mUpdateCursorPosition = true;
766     }
767   }
768 }
769
770 void Controller::ResetScrollPosition()
771 {
772   if( NULL != mImpl->mEventData )
773   {
774     // Reset the scroll position.
775     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
776     mImpl->mEventData->mScrollAfterUpdatePosition = true;
777   }
778 }
779
780 void Controller::TextReplacedEvent()
781 {
782   // Reset buffers.
783   ClearModelData();
784
785   // The natural size needs to be re-calculated.
786   mImpl->mRecalculateNaturalSize = true;
787
788   // Apply modifications to the model
789   mImpl->mOperationsPending = ALL_OPERATIONS;
790   mImpl->UpdateModel( ALL_OPERATIONS );
791   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
792                                                            ALIGN              |
793                                                            UPDATE_ACTUAL_SIZE |
794                                                            REORDER );
795 }
796
797 void Controller::TextInsertedEvent()
798 {
799   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
800
801   // TODO - Optimize this
802   ClearModelData();
803
804   // The natural size needs to be re-calculated.
805   mImpl->mRecalculateNaturalSize = true;
806
807   // Apply modifications to the model; TODO - Optimize this
808   mImpl->mOperationsPending = ALL_OPERATIONS;
809   mImpl->UpdateModel( ALL_OPERATIONS );
810   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
811                                                            ALIGN              |
812                                                            UPDATE_ACTUAL_SIZE |
813                                                            REORDER );
814
815   // Queue a cursor reposition event; this must wait until after DoRelayout()
816   if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
817       ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
818       ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
819   {
820     mImpl->mEventData->mUpdateCursorPosition = true;
821     mImpl->mEventData->mScrollAfterUpdatePosition = true;
822   }
823 }
824
825 void Controller::TextDeletedEvent()
826 {
827   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
828
829   // TODO - Optimize this
830   ClearModelData();
831
832   // The natural size needs to be re-calculated.
833   mImpl->mRecalculateNaturalSize = true;
834
835   // Apply modifications to the model; TODO - Optimize this
836   mImpl->mOperationsPending = ALL_OPERATIONS;
837   mImpl->UpdateModel( ALL_OPERATIONS );
838   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
839                                                            ALIGN              |
840                                                            UPDATE_ACTUAL_SIZE |
841                                                            REORDER );
842
843   // Queue a cursor reposition event; this must wait until after DoRelayout()
844   if( 0u == mImpl->mLogicalModel->mText.Count() )
845   {
846     mImpl->mEventData->mUpdateCursorPosition = true;
847   }
848   else
849   {
850     mImpl->mEventData->mScrollAfterDelete = true;
851   }
852 }
853
854 bool Controller::DoRelayout( const Size& size,
855                              OperationsMask operationsRequired,
856                              Size& layoutSize )
857 {
858   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
859   bool viewUpdated( false );
860
861   // Calculate the operations to be done.
862   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
863
864   if( LAYOUT & operations )
865   {
866     // Some vectors with data needed to layout and reorder may be void
867     // after the first time the text has been laid out.
868     // Fill the vectors again.
869
870     const Length numberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
871
872     if( 0u == numberOfGlyphs )
873     {
874       // Nothing else to do if there is no glyphs.
875       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
876       return true;
877     }
878
879     const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
880     const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
881     const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
882     const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
883     const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
884     const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
885     const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
886
887     // Set the layout parameters.
888     LayoutParameters layoutParameters( size,
889                                        textBuffer,
890                                        lineBreakInfo.Begin(),
891                                        wordBreakInfo.Begin(),
892                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
893                                        numberOfGlyphs,
894                                        glyphs.Begin(),
895                                        glyphsToCharactersMap.Begin(),
896                                        charactersPerGlyph.Begin() );
897
898     // The laid-out lines.
899     // It's not possible to know in how many lines the text is going to be laid-out,
900     // but it can be resized at least with the number of 'paragraphs' to avoid
901     // some re-allocations.
902     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
903
904     // Delete any previous laid out lines before setting the new ones.
905     lines.Clear();
906
907     // The capacity of the bidirectional paragraph info is the number of paragraphs.
908     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
909
910     // Resize the vector of positions to have the same size than the vector of glyphs.
911     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
912     glyphPositions.Resize( numberOfGlyphs );
913
914     // Whether the last character is a new paragraph character.
915     layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
916
917     // Update the visual model.
918     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
919                                                    glyphPositions,
920                                                    lines,
921                                                    layoutSize );
922
923     if( viewUpdated )
924     {
925       // Reorder the lines
926       if( REORDER & operations )
927       {
928         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
929
930         // Check first if there are paragraphs with bidirectional info.
931         if( 0u != bidirectionalInfo.Count() )
932         {
933           // Get the lines
934           const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
935
936           // Reorder the lines.
937           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
938           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
939           ReorderLines( bidirectionalInfo,
940                         lines,
941                         lineBidirectionalInfoRuns );
942
943           // Set the bidirectional info into the model.
944           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
945           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
946                                                        numberOfBidirectionalInfoRuns );
947
948           // Set the bidirectional info per line into the layout parameters.
949           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
950           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
951
952           // Get the character to glyph conversion table and set into the layout.
953           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
954
955           // Get the glyphs per character table and set into the layout.
956           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
957
958           // Re-layout the text. Reorder those lines with right to left characters.
959           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
960                                                          glyphPositions );
961
962           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
963           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
964                  endIt = lineBidirectionalInfoRuns.End();
965                it != endIt;
966                ++it )
967           {
968             BidirectionalLineInfoRun& bidiLineInfo = *it;
969
970             free( bidiLineInfo.visualToLogicalMap );
971           }
972         }
973       } // REORDER
974
975       // Sets the actual size.
976       if( UPDATE_ACTUAL_SIZE & operations )
977       {
978         mImpl->mVisualModel->SetActualSize( layoutSize );
979       }
980     } // view updated
981   }
982   else
983   {
984     layoutSize = mImpl->mVisualModel->GetActualSize();
985   }
986
987   if( ALIGN & operations )
988   {
989     // The laid-out lines.
990     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
991
992     mImpl->mLayoutEngine.Align( layoutSize,
993                                 lines );
994
995     viewUpdated = true;
996   }
997
998   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
999   return viewUpdated;
1000 }
1001
1002 void Controller::SetMultiLineEnabled( bool enable )
1003 {
1004   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
1005
1006   if( layout != mImpl->mLayoutEngine.GetLayout() )
1007   {
1008     // Set the layout type.
1009     mImpl->mLayoutEngine.SetLayout( layout );
1010
1011     // Set the flags to redo the layout operations
1012     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
1013                                                                           UPDATE_ACTUAL_SIZE |
1014                                                                           ALIGN              |
1015                                                                           REORDER );
1016
1017     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
1018
1019     mImpl->RequestRelayout();
1020   }
1021 }
1022
1023 bool Controller::IsMultiLineEnabled() const
1024 {
1025   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
1026 }
1027
1028 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
1029 {
1030   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1031   {
1032     // Set the alignment.
1033     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1034
1035     // Set the flag to redo the alignment operation.
1036     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1037
1038     mImpl->RequestRelayout();
1039   }
1040 }
1041
1042 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1043 {
1044   return mImpl->mLayoutEngine.GetHorizontalAlignment();
1045 }
1046
1047 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1048 {
1049   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1050   {
1051     // Set the alignment.
1052     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1053
1054     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1055
1056     mImpl->RequestRelayout();
1057   }
1058 }
1059
1060 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1061 {
1062   return mImpl->mLayoutEngine.GetVerticalAlignment();
1063 }
1064
1065 void Controller::CalculateTextAlignment( const Size& size )
1066 {
1067   // Get the direction of the first character.
1068   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1069
1070   Size actualSize = mImpl->mVisualModel->GetActualSize();
1071   if( fabsf( actualSize.height ) < Math::MACHINE_EPSILON_1000 )
1072   {
1073     // Get the line height of the default font.
1074     actualSize.height = mImpl->GetDefaultFontLineHeight();
1075   }
1076
1077   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1078   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1079   if( firstParagraphDirection &&
1080       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1081   {
1082     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1083     {
1084       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1085     }
1086     else
1087     {
1088       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1089     }
1090   }
1091
1092   switch( horizontalAlignment )
1093   {
1094     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1095     {
1096       mImpl->mAlignmentOffset.x = 0.f;
1097       break;
1098     }
1099     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1100     {
1101       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1102       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1103       break;
1104     }
1105     case LayoutEngine::HORIZONTAL_ALIGN_END:
1106     {
1107       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1108       break;
1109     }
1110   }
1111
1112   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1113   switch( verticalAlignment )
1114   {
1115     case LayoutEngine::VERTICAL_ALIGN_TOP:
1116     {
1117       mImpl->mAlignmentOffset.y = 0.f;
1118       break;
1119     }
1120     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1121     {
1122       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1123       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1124       break;
1125     }
1126     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1127     {
1128       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1129       break;
1130     }
1131   }
1132 }
1133
1134 LayoutEngine& Controller::GetLayoutEngine()
1135 {
1136   return mImpl->mLayoutEngine;
1137 }
1138
1139 View& Controller::GetView()
1140 {
1141   return mImpl->mView;
1142 }
1143
1144 void Controller::KeyboardFocusGainEvent()
1145 {
1146   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1147
1148   if( mImpl->mEventData )
1149   {
1150     if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1151         ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1152     {
1153       mImpl->ChangeState( EventData::EDITING );
1154       mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1155     }
1156
1157     if( mImpl->IsShowingPlaceholderText() )
1158     {
1159       // Show alternative placeholder-text when editing
1160       ShowPlaceholderText();
1161     }
1162
1163     mImpl->RequestRelayout();
1164   }
1165 }
1166
1167 void Controller::KeyboardFocusLostEvent()
1168 {
1169   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1170
1171   if( mImpl->mEventData )
1172   {
1173     if ( EventData::INTERRUPTED != mImpl->mEventData->mState )
1174     {
1175       mImpl->ChangeState( EventData::INACTIVE );
1176
1177       if( mImpl->IsShowingPlaceholderText() )
1178       {
1179         // Revert to regular placeholder-text when not editing
1180         ShowPlaceholderText();
1181       }
1182     }
1183   }
1184   mImpl->RequestRelayout();
1185 }
1186
1187 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1188 {
1189   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1190
1191   bool textChanged( false );
1192
1193   if( mImpl->mEventData &&
1194       keyEvent.state == KeyEvent::Down )
1195   {
1196     int keyCode = keyEvent.keyCode;
1197     const std::string& keyString = keyEvent.keyPressed;
1198
1199     // Pre-process to separate modifying events from non-modifying input events.
1200     if( Dali::DALI_KEY_ESCAPE == keyCode )
1201     {
1202       // Escape key is a special case which causes focus loss
1203       KeyboardFocusLostEvent();
1204     }
1205     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1206              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1207              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1208              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1209     {
1210       Event event( Event::CURSOR_KEY_EVENT );
1211       event.p1.mInt = keyCode;
1212       mImpl->mEventData->mEventQueue.push_back( event );
1213     }
1214     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1215     {
1216       textChanged = BackspaceKeyEvent();
1217     }
1218     else if ( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
1219     {
1220       mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
1221       // Avoids calling the InsertText() method which can delete selected text
1222     }
1223     else if ( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
1224               IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
1225     {
1226       mImpl->ChangeState( EventData::INACTIVE );
1227       // Menu/Home key behaviour does not allow edit mode to resume like Power key
1228       // Avoids calling the InsertText() method which can delete selected text
1229     }
1230     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
1231     {
1232       // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
1233       // and a character is typed after the type of a upper case latin character.
1234
1235       // Do nothing.
1236     }
1237     else
1238     {
1239       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1240
1241       // IMF manager is no longer handling key-events
1242       mImpl->ClearPreEditFlag();
1243
1244       InsertText( keyString, COMMIT );
1245       textChanged = true;
1246     }
1247
1248     if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
1249          ( mImpl->mEventData->mState != EventData::INACTIVE ) )
1250     {
1251       mImpl->ChangeState( EventData::EDITING );
1252     }
1253
1254     mImpl->RequestRelayout();
1255   }
1256
1257   if( textChanged )
1258   {
1259     // Do this last since it provides callbacks into application code
1260     mImpl->mControlInterface.TextChanged();
1261   }
1262
1263   return false;
1264 }
1265
1266 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1267 {
1268   bool removedPrevious( false );
1269   bool maxLengthReached( false );
1270
1271   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1272   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1273                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1274                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1275
1276   // TODO: At the moment the underline runs are only for pre-edit.
1277   mImpl->mVisualModel->mUnderlineRuns.Clear();
1278
1279   Vector<Character> utf32Characters;
1280   Length characterCount( 0u );
1281
1282   // Remove the previous IMF pre-edit (predicitive text)
1283   if( mImpl->mEventData &&
1284       mImpl->mEventData->mPreEditFlag &&
1285       0 != mImpl->mEventData->mPreEditLength )
1286   {
1287     CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1288
1289     removedPrevious = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1290
1291     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1292     mImpl->mEventData->mPreEditLength = 0;
1293   }
1294   else
1295   {
1296     // Remove the previous Selection
1297     removedPrevious = RemoveSelectedText();
1298   }
1299
1300   if( !text.empty() )
1301   {
1302     //  Convert text into UTF-32
1303     utf32Characters.Resize( text.size() );
1304
1305     // This is a bit horrible but std::string returns a (signed) char*
1306     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1307
1308     // Transform a text array encoded in utf8 into an array encoded in utf32.
1309     // It returns the actual number of characters.
1310     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1311     utf32Characters.Resize( characterCount );
1312
1313     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1314     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1315   }
1316
1317   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1318   {
1319     // The placeholder text is no longer needed
1320     if( mImpl->IsShowingPlaceholderText() )
1321     {
1322       ResetText();
1323     }
1324
1325     mImpl->ChangeState( EventData::EDITING );
1326
1327     // Handle the IMF (predicitive text) state changes
1328     if( mImpl->mEventData )
1329     {
1330       if( COMMIT == type )
1331       {
1332         // IMF manager is no longer handling key-events
1333         mImpl->ClearPreEditFlag();
1334       }
1335       else // PRE_EDIT
1336       {
1337         if( !mImpl->mEventData->mPreEditFlag )
1338         {
1339           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1340
1341           // Record the start of the pre-edit text
1342           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1343         }
1344
1345         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1346         mImpl->mEventData->mPreEditFlag = true;
1347
1348         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1349       }
1350     }
1351
1352     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1353
1354     // Restrict new text to fit within Maximum characters setting
1355     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1356     maxLengthReached = ( characterCount > maxSizeOfNewText );
1357
1358     // Insert at current cursor position
1359     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1360
1361     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1362
1363     if( cursorIndex < numberOfCharactersInModel )
1364     {
1365       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1366     }
1367     else
1368     {
1369       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1370     }
1371
1372     cursorIndex += maxSizeOfNewText;
1373
1374     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1375   }
1376
1377   if( 0u == mImpl->mLogicalModel->mText.Count() &&
1378       mImpl->IsPlaceholderAvailable() )
1379   {
1380     // Show place-holder if empty after removing the pre-edit text
1381     ShowPlaceholderText();
1382     mImpl->mEventData->mUpdateCursorPosition = true;
1383     mImpl->ClearPreEditFlag();
1384   }
1385   else if( removedPrevious ||
1386            0 != utf32Characters.Count() )
1387   {
1388     // Queue an inserted event
1389     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1390   }
1391
1392   if( maxLengthReached )
1393   {
1394     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1395
1396     mImpl->ResetImfManager();
1397
1398     // Do this last since it provides callbacks into application code
1399     mImpl->mControlInterface.MaxLengthReached();
1400   }
1401 }
1402
1403 bool Controller::RemoveSelectedText()
1404 {
1405   bool textRemoved( false );
1406
1407   if ( EventData::SELECTING         == mImpl->mEventData->mState ||
1408        EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1409   {
1410     std::string removedString;
1411     mImpl->RetrieveSelection( removedString, true );
1412
1413     if( !removedString.empty() )
1414     {
1415       textRemoved = true;
1416       mImpl->ChangeState( EventData::EDITING );
1417     }
1418   }
1419
1420   return textRemoved;
1421 }
1422
1423 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1424 {
1425   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1426
1427   if( NULL != mImpl->mEventData )
1428   {
1429     if( 1u == tapCount )
1430     {
1431       // This is to avoid unnecessary relayouts when tapping an empty text-field
1432       bool relayoutNeeded( false );
1433
1434       if( mImpl->IsShowingRealText() &&
1435           EventData::EDITING == mImpl->mEventData->mState )
1436       {
1437         // Show grab handle on second tap
1438         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
1439         relayoutNeeded = true;
1440       }
1441       else if( EventData::EDITING                  != mImpl->mEventData->mState &&
1442                EventData::EDITING_WITH_GRAB_HANDLE != mImpl->mEventData->mState )
1443       {
1444         // Show cursor on first tap
1445         mImpl->ChangeState( EventData::EDITING );
1446         relayoutNeeded = true;
1447       }
1448       else if( mImpl->IsShowingRealText() )
1449       {
1450         // Move the cursor
1451         relayoutNeeded = true;
1452       }
1453
1454       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1455       if( relayoutNeeded )
1456       {
1457         Event event( Event::TAP_EVENT );
1458         event.p1.mUint = tapCount;
1459         event.p2.mFloat = x;
1460         event.p3.mFloat = y;
1461         mImpl->mEventData->mEventQueue.push_back( event );
1462
1463         mImpl->RequestRelayout();
1464       }
1465     }
1466     else if( 2u == tapCount )
1467     {
1468       if( mImpl->mEventData->mSelectionEnabled &&
1469           mImpl->IsShowingRealText() )
1470       {
1471         SelectEvent( x, y, false );
1472       }
1473     }
1474   }
1475
1476   // Reset keyboard as tap event has occurred.
1477   mImpl->ResetImfManager();
1478 }
1479
1480 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1481 {
1482   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1483
1484   if( mImpl->mEventData )
1485   {
1486     Event event( Event::PAN_EVENT );
1487     event.p1.mInt = state;
1488     event.p2.mFloat = displacement.x;
1489     event.p3.mFloat = displacement.y;
1490     mImpl->mEventData->mEventQueue.push_back( event );
1491
1492     mImpl->RequestRelayout();
1493   }
1494 }
1495
1496 void Controller::LongPressEvent( Gesture::State state, float x, float y  )
1497 {
1498   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1499
1500   if  ( mImpl->IsShowingPlaceholderText() || mImpl->mLogicalModel->mText.Count() == 0u )
1501   {
1502     if ( mImpl->mEventData )
1503     {
1504       Event event( Event::LONG_PRESS_EVENT );
1505       event.p1.mInt = state;
1506       mImpl->mEventData->mEventQueue.push_back( event );
1507       mImpl->RequestRelayout();
1508     }
1509   }
1510   else if( mImpl->mEventData )
1511   {
1512     SelectEvent( x, y, false );
1513   }
1514 }
1515
1516 void Controller::SelectEvent( float x, float y, bool selectAll )
1517 {
1518   if( mImpl->mEventData )
1519   {
1520     if ( mImpl->mEventData->mState == EventData::SELECTING )
1521     {
1522       mImpl->ChangeState( EventData::SELECTION_CHANGED );
1523     }
1524     else
1525     {
1526       mImpl->ChangeState( EventData::SELECTING );
1527     }
1528
1529     if( selectAll )
1530     {
1531       Event event( Event::SELECT_ALL );
1532       mImpl->mEventData->mEventQueue.push_back( event );
1533     }
1534     else
1535     {
1536       Event event( Event::SELECT );
1537       event.p2.mFloat = x;
1538       event.p3.mFloat = y;
1539       mImpl->mEventData->mEventQueue.push_back( event );
1540     }
1541
1542     mImpl->RequestRelayout();
1543   }
1544 }
1545
1546 void Controller::GetTargetSize( Vector2& targetSize )
1547 {
1548   targetSize = mImpl->mVisualModel->mControlSize;
1549 }
1550
1551 void Controller::AddDecoration( Actor& actor, bool needsClipping )
1552 {
1553   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
1554 }
1555
1556 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1557 {
1558   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1559
1560   if( mImpl->mEventData )
1561   {
1562     switch( handleType )
1563     {
1564       case GRAB_HANDLE:
1565       {
1566         Event event( Event::GRAB_HANDLE_EVENT );
1567         event.p1.mUint  = state;
1568         event.p2.mFloat = x;
1569         event.p3.mFloat = y;
1570
1571         mImpl->mEventData->mEventQueue.push_back( event );
1572         break;
1573       }
1574       case LEFT_SELECTION_HANDLE:
1575       {
1576         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1577         event.p1.mUint  = state;
1578         event.p2.mFloat = x;
1579         event.p3.mFloat = y;
1580
1581         mImpl->mEventData->mEventQueue.push_back( event );
1582         break;
1583       }
1584       case RIGHT_SELECTION_HANDLE:
1585       {
1586         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1587         event.p1.mUint  = state;
1588         event.p2.mFloat = x;
1589         event.p3.mFloat = y;
1590
1591         mImpl->mEventData->mEventQueue.push_back( event );
1592         break;
1593       }
1594       case LEFT_SELECTION_HANDLE_MARKER:
1595       case RIGHT_SELECTION_HANDLE_MARKER:
1596       {
1597         // Markers do not move the handles.
1598         break;
1599       }
1600       case HANDLE_TYPE_COUNT:
1601       {
1602         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1603       }
1604     }
1605
1606     mImpl->RequestRelayout();
1607   }
1608 }
1609
1610 void Controller::PasteText( const std::string& stringToPaste )
1611 {
1612   InsertText( stringToPaste, Text::Controller::COMMIT );
1613   mImpl->ChangeState( EventData::EDITING );
1614   mImpl->RequestRelayout();
1615 }
1616
1617 void Controller::PasteClipboardItemEvent()
1618 {
1619   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1620   std::string stringToPaste( notifier.GetContent() );
1621   PasteText( stringToPaste );
1622 }
1623
1624 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
1625 {
1626   if( NULL == mImpl->mEventData )
1627   {
1628     return;
1629   }
1630
1631   switch( button )
1632   {
1633     case Toolkit::TextSelectionPopup::CUT:
1634     {
1635       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
1636       mImpl->mOperationsPending = ALL_OPERATIONS;
1637       if( 0u != mImpl->mLogicalModel->mText.Count() ||
1638           !mImpl->IsPlaceholderAvailable() )
1639       {
1640         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1641       }
1642       else
1643       {
1644         ShowPlaceholderText();
1645         mImpl->mEventData->mUpdateCursorPosition = true;
1646       }
1647       mImpl->RequestRelayout();
1648       mImpl->mControlInterface.TextChanged();
1649       break;
1650     }
1651     case Toolkit::TextSelectionPopup::COPY:
1652     {
1653       mImpl->SendSelectionToClipboard( false ); // Text not modified
1654       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
1655       break;
1656     }
1657     case Toolkit::TextSelectionPopup::PASTE:
1658     {
1659       std::string stringToPaste("");
1660       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
1661       PasteText( stringToPaste );
1662       break;
1663     }
1664     case Toolkit::TextSelectionPopup::SELECT:
1665     {
1666       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1667
1668       if( mImpl->mEventData->mSelectionEnabled  )
1669       {
1670         // Creates a SELECT event.
1671         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
1672       }
1673       break;
1674     }
1675     case Toolkit::TextSelectionPopup::SELECT_ALL:
1676     {
1677       // Creates a SELECT_ALL event
1678       SelectEvent( 0.f, 0.f, true );
1679       break;
1680     }
1681     case Toolkit::TextSelectionPopup::CLIPBOARD:
1682     {
1683       mImpl->ShowClipboard();
1684       break;
1685     }
1686     case Toolkit::TextSelectionPopup::NONE:
1687     {
1688       // Nothing to do.
1689       break;
1690     }
1691   }
1692 }
1693
1694 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1695 {
1696   bool update( false );
1697   bool requestRelayout = false;
1698
1699   std::string text;
1700   unsigned int cursorPosition( 0 );
1701
1702   switch ( imfEvent.eventName )
1703   {
1704     case ImfManager::COMMIT:
1705     {
1706       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1707       requestRelayout = true;
1708       break;
1709     }
1710     case ImfManager::PREEDIT:
1711     {
1712       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1713       update = true;
1714       requestRelayout = true;
1715       break;
1716     }
1717     case ImfManager::DELETESURROUNDING:
1718     {
1719       update = RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1720
1721       if( update )
1722       {
1723         if( 0u != mImpl->mLogicalModel->mText.Count() ||
1724             !mImpl->IsPlaceholderAvailable() )
1725         {
1726           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1727         }
1728         else
1729         {
1730           ShowPlaceholderText();
1731           mImpl->mEventData->mUpdateCursorPosition = true;
1732         }
1733       }
1734       requestRelayout = true;
1735       break;
1736     }
1737     case ImfManager::GETSURROUNDING:
1738     {
1739       GetText( text );
1740       cursorPosition = GetLogicalCursorPosition();
1741
1742       imfManager.SetSurroundingText( text );
1743       imfManager.SetCursorPosition( cursorPosition );
1744       break;
1745     }
1746     case ImfManager::VOID:
1747     {
1748       // do nothing
1749       break;
1750     }
1751   } // end switch
1752
1753   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1754   {
1755     GetText( text );
1756     cursorPosition = GetLogicalCursorPosition();
1757   }
1758
1759   if( requestRelayout )
1760   {
1761     mImpl->mOperationsPending = ALL_OPERATIONS;
1762     mImpl->RequestRelayout();
1763
1764     // Do this last since it provides callbacks into application code
1765     mImpl->mControlInterface.TextChanged();
1766   }
1767
1768   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1769
1770   return callbackData;
1771 }
1772
1773 Controller::~Controller()
1774 {
1775   delete mImpl;
1776 }
1777
1778 bool Controller::BackspaceKeyEvent()
1779 {
1780   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1781
1782   // IMF manager is no longer handling key-events
1783   mImpl->ClearPreEditFlag();
1784
1785   bool removed( false );
1786
1787   if ( EventData::SELECTING         == mImpl->mEventData->mState ||
1788        EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1789   {
1790     removed = RemoveSelectedText();
1791   }
1792   else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
1793   {
1794     // Remove the character before the current cursor position
1795     removed = RemoveText( -1, 1 );
1796   }
1797
1798   if( removed )
1799   {
1800     if( 0u != mImpl->mLogicalModel->mText.Count() ||
1801         !mImpl->IsPlaceholderAvailable() )
1802     {
1803       mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1804     }
1805     else
1806     {
1807       ShowPlaceholderText();
1808       mImpl->mEventData->mUpdateCursorPosition = true;
1809     }
1810   }
1811
1812   return removed;
1813 }
1814
1815 void Controller::ShowPlaceholderText()
1816 {
1817   if( mImpl->IsPlaceholderAvailable() )
1818   {
1819     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1820
1821     mImpl->mEventData->mIsShowingPlaceholderText = true;
1822
1823     // Disable handles when showing place-holder text
1824     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1825     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1826     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1827
1828     const char* text( NULL );
1829     size_t size( 0 );
1830
1831     // TODO - Switch placeholder text styles when changing state
1832     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1833         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1834     {
1835       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1836       size = mImpl->mEventData->mPlaceholderTextActive.size();
1837     }
1838     else
1839     {
1840       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1841       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1842     }
1843
1844     // Reset model for showing placeholder.
1845     mImpl->mLogicalModel->mText.Clear();
1846     ClearModelData();
1847     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1848
1849     // Convert text into UTF-32
1850     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1851     utf32Characters.Resize( size );
1852
1853     // This is a bit horrible but std::string returns a (signed) char*
1854     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1855
1856     // Transform a text array encoded in utf8 into an array encoded in utf32.
1857     // It returns the actual number of characters.
1858     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1859     utf32Characters.Resize( characterCount );
1860
1861     // Reset the cursor position
1862     mImpl->mEventData->mPrimaryCursorPosition = 0;
1863
1864     // The natural size needs to be re-calculated.
1865     mImpl->mRecalculateNaturalSize = true;
1866
1867     // Apply modifications to the model
1868     mImpl->mOperationsPending = ALL_OPERATIONS;
1869
1870     // Update the rest of the model during size negotiation
1871     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1872   }
1873 }
1874
1875 void Controller::ClearModelData()
1876 {
1877   // n.b. This does not Clear the mText from mLogicalModel
1878   mImpl->mLogicalModel->mScriptRuns.Clear();
1879   mImpl->mLogicalModel->mFontRuns.Clear();
1880   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1881   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1882   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1883   mImpl->mLogicalModel->mCharacterDirections.Clear();
1884   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1885   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1886   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1887   mImpl->mVisualModel->mGlyphs.Clear();
1888   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1889   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1890   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1891   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1892   mImpl->mVisualModel->mGlyphPositions.Clear();
1893   mImpl->mVisualModel->mLines.Clear();
1894   mImpl->mVisualModel->ClearCaches();
1895 }
1896
1897 void Controller::ClearFontData()
1898 {
1899   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1900   mImpl->mLogicalModel->mFontRuns.Clear();
1901   mImpl->mVisualModel->mGlyphs.Clear();
1902   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1903   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1904   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1905   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1906   mImpl->mVisualModel->mGlyphPositions.Clear();
1907   mImpl->mVisualModel->mLines.Clear();
1908   mImpl->mVisualModel->ClearCaches();
1909 }
1910
1911 Controller::Controller( ControlInterface& controlInterface )
1912 : mImpl( NULL )
1913 {
1914   mImpl = new Controller::Impl( controlInterface );
1915 }
1916
1917 } // namespace Text
1918
1919 } // namespace Toolkit
1920
1921 } // namespace Dali