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