8a42bcbccd6b07b79ae3f6f2df147d18fa9036e3
[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     // Pre-process to separate modifying events from non-modifying input events.
1125     if( Dali::DALI_KEY_ESCAPE == keyCode )
1126     {
1127       // Escape key is a special case which causes focus loss
1128       KeyboardFocusLostEvent();
1129     }
1130     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1131              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1132              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1133              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1134     {
1135       Event event( Event::CURSOR_KEY_EVENT );
1136       event.p1.mInt = keyCode;
1137       mImpl->mEventData->mEventQueue.push_back( event );
1138     }
1139     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1140     {
1141       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1142
1143       // IMF manager is no longer handling key-events
1144       mImpl->ClearPreEditFlag();
1145
1146       bool removed( false );
1147
1148       if ( EventData::SELECTING         == mImpl->mEventData->mState ||
1149            EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1150       {
1151         removed = RemoveSelectedText();
1152       }
1153       else
1154       {
1155         // Remove the character before the current cursor position
1156         removed = RemoveText( -1, 1 );
1157       }
1158
1159       if( removed )
1160       {
1161         if( 0u != mImpl->mLogicalModel->mText.Count() ||
1162             !mImpl->IsPlaceholderAvailable() )
1163         {
1164           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1165         }
1166         else
1167         {
1168           ShowPlaceholderText();
1169           mImpl->mEventData->mUpdateCursorPosition = true;
1170         }
1171
1172         textChanged = true;
1173       }
1174     }
1175     else
1176     {
1177       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1178
1179       // IMF manager is no longer handling key-events
1180       mImpl->ClearPreEditFlag();
1181
1182       InsertText( keyString, COMMIT );
1183       textChanged = true;
1184     }
1185
1186     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1187
1188     mImpl->RequestRelayout();
1189   }
1190
1191   if( textChanged )
1192   {
1193     // Do this last since it provides callbacks into application code
1194     mImpl->mControlInterface.TextChanged();
1195   }
1196
1197   return false;
1198 }
1199
1200 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1201 {
1202   bool removedPrevious( false );
1203   bool maxLengthReached( false );
1204
1205   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1206   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1207                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1208                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1209
1210   Vector<Character> utf32Characters;
1211   Length characterCount( 0u );
1212
1213   // Remove the previous IMF pre-edit (predicitive text)
1214   if( mImpl->mEventData &&
1215       mImpl->mEventData->mPreEditFlag &&
1216       0 != mImpl->mEventData->mPreEditLength )
1217   {
1218     CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1219     removedPrevious = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1220
1221     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1222     mImpl->mEventData->mPreEditLength = 0;
1223   }
1224   else
1225   {
1226     // Remove the previous Selection
1227     removedPrevious = RemoveSelectedText();
1228   }
1229
1230   if( ! text.empty() )
1231   {
1232     //  Convert text into UTF-32
1233     utf32Characters.Resize( text.size() );
1234
1235     // This is a bit horrible but std::string returns a (signed) char*
1236     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1237
1238     // Transform a text array encoded in utf8 into an array encoded in utf32.
1239     // It returns the actual number of characters.
1240     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1241     utf32Characters.Resize( characterCount );
1242
1243     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1244     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1245   }
1246
1247   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1248   {
1249     // The placeholder text is no longer needed
1250     if( mImpl->IsShowingPlaceholderText() )
1251     {
1252       ResetText();
1253     }
1254
1255     // Handle the IMF (predicitive text) state changes
1256     if( mImpl->mEventData )
1257     {
1258       if( COMMIT == type )
1259       {
1260         // IMF manager is no longer handling key-events
1261         mImpl->ClearPreEditFlag();
1262       }
1263       else // PRE_EDIT
1264       {
1265         if( ! mImpl->mEventData->mPreEditFlag )
1266         {
1267           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1268
1269           // Record the start of the pre-edit text
1270           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1271         }
1272
1273         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1274         mImpl->mEventData->mPreEditFlag = true;
1275
1276         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1277       }
1278     }
1279
1280     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1281
1282     // Restrict new text to fit within Maximum characters setting
1283     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1284     maxLengthReached = ( characterCount > maxSizeOfNewText );
1285
1286     // Insert at current cursor position
1287     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1288
1289     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1290
1291     if( cursorIndex < numberOfCharactersInModel )
1292     {
1293       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1294     }
1295     else
1296     {
1297       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1298     }
1299
1300     cursorIndex += maxSizeOfNewText;
1301
1302     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1303   }
1304
1305   if( 0u == mImpl->mLogicalModel->mText.Count() &&
1306       mImpl->IsPlaceholderAvailable() )
1307   {
1308     // Show place-holder if empty after removing the pre-edit text
1309     ShowPlaceholderText();
1310     mImpl->mEventData->mUpdateCursorPosition = true;
1311     mImpl->ClearPreEditFlag();
1312   }
1313   else if( removedPrevious ||
1314            0 != utf32Characters.Count() )
1315   {
1316     // Queue an inserted event
1317     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1318   }
1319
1320   if( maxLengthReached )
1321   {
1322     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1323
1324     mImpl->ResetImfManager();
1325
1326     // Do this last since it provides callbacks into application code
1327     mImpl->mControlInterface.MaxLengthReached();
1328   }
1329 }
1330
1331 bool Controller::RemoveSelectedText()
1332 {
1333   bool textRemoved( false );
1334
1335   if ( EventData::SELECTING         == mImpl->mEventData->mState ||
1336        EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1337   {
1338     std::string removedString;
1339     mImpl->RetrieveSelection( removedString, true );
1340
1341     if( !removedString.empty() )
1342     {
1343       textRemoved = true;
1344       mImpl->ChangeState( EventData::EDITING );
1345     }
1346   }
1347
1348   return textRemoved;
1349 }
1350
1351 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1352 {
1353   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1354
1355   if( NULL != mImpl->mEventData )
1356   {
1357     const bool isShowingPlaceholderText = mImpl->IsShowingPlaceholderText();
1358     if( 1u == tapCount )
1359     {
1360       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
1361
1362       if( !isShowingPlaceholderText && tapDuringEditMode )
1363       {
1364         mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1365         mImpl->mEventData->mDecorator->SetPopupActive( false );
1366       }
1367
1368       mImpl->ChangeState( EventData::EDITING );
1369
1370       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1371       if( mImpl->mEventData )
1372       {
1373         Event event( Event::TAP_EVENT );
1374         event.p1.mUint = tapCount;
1375         event.p2.mFloat = x;
1376         event.p3.mFloat = y;
1377         mImpl->mEventData->mEventQueue.push_back( event );
1378
1379         mImpl->RequestRelayout();
1380       }
1381     }
1382     else if( !isShowingPlaceholderText &&
1383              mImpl->mEventData->mSelectionEnabled &&
1384              ( 2u == tapCount ) )
1385     {
1386       SelectEvent( x, y, false );
1387     }
1388   }
1389
1390   // Reset keyboard as tap event has occurred.
1391   mImpl->ResetImfManager();
1392 }
1393
1394 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1395 {
1396   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1397
1398   if( mImpl->mEventData )
1399   {
1400     Event event( Event::PAN_EVENT );
1401     event.p1.mInt = state;
1402     event.p2.mFloat = displacement.x;
1403     event.p3.mFloat = displacement.y;
1404     mImpl->mEventData->mEventQueue.push_back( event );
1405
1406     mImpl->RequestRelayout();
1407   }
1408 }
1409
1410 void Controller::SelectEvent( float x, float y, bool selectAll )
1411 {
1412   if( mImpl->mEventData )
1413   {
1414     if ( mImpl->mEventData->mState == EventData::SELECTING )
1415     {
1416       mImpl->ChangeState( EventData::SELECTION_CHANGED );
1417     }
1418     else
1419     {
1420       mImpl->ChangeState( EventData::SELECTING );
1421     }
1422
1423     if( selectAll )
1424     {
1425       Event event( Event::SELECT_ALL );
1426       mImpl->mEventData->mEventQueue.push_back( event );
1427     }
1428     else
1429     {
1430       Event event( Event::SELECT );
1431       event.p2.mFloat = x;
1432       event.p3.mFloat = y;
1433       mImpl->mEventData->mEventQueue.push_back( event );
1434     }
1435
1436     mImpl->RequestRelayout();
1437   }
1438 }
1439
1440 void Controller::GetTargetSize( Vector2& targetSize )
1441 {
1442   targetSize = mImpl->mControlSize;
1443 }
1444
1445 void Controller::AddDecoration( Actor& actor, bool needsClipping )
1446 {
1447   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
1448 }
1449
1450 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1451 {
1452   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1453
1454   if( mImpl->mEventData )
1455   {
1456     switch( handleType )
1457     {
1458       case GRAB_HANDLE:
1459       {
1460         Event event( Event::GRAB_HANDLE_EVENT );
1461         event.p1.mUint  = state;
1462         event.p2.mFloat = x;
1463         event.p3.mFloat = y;
1464
1465         mImpl->mEventData->mEventQueue.push_back( event );
1466         break;
1467       }
1468       case LEFT_SELECTION_HANDLE:
1469       {
1470         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1471         event.p1.mUint  = state;
1472         event.p2.mFloat = x;
1473         event.p3.mFloat = y;
1474
1475         mImpl->mEventData->mEventQueue.push_back( event );
1476         break;
1477       }
1478       case RIGHT_SELECTION_HANDLE:
1479       {
1480         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1481         event.p1.mUint  = state;
1482         event.p2.mFloat = x;
1483         event.p3.mFloat = y;
1484
1485         mImpl->mEventData->mEventQueue.push_back( event );
1486         break;
1487       }
1488       case HANDLE_TYPE_COUNT:
1489       {
1490         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1491       }
1492     }
1493
1494     mImpl->RequestRelayout();
1495   }
1496 }
1497
1498 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
1499 {
1500   if( NULL == mImpl->mEventData )
1501   {
1502     return;
1503   }
1504
1505   switch( button )
1506   {
1507     case Toolkit::TextSelectionPopup::CUT:
1508     {
1509       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
1510       mImpl->mOperationsPending = ALL_OPERATIONS;
1511       if( 0u != mImpl->mLogicalModel->mText.Count() ||
1512           !mImpl->IsPlaceholderAvailable() )
1513       {
1514         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1515       }
1516       else
1517       {
1518         ShowPlaceholderText();
1519         mImpl->mEventData->mUpdateCursorPosition = true;
1520       }
1521       mImpl->RequestRelayout();
1522       mImpl->mControlInterface.TextChanged();
1523       break;
1524     }
1525     case Toolkit::TextSelectionPopup::COPY:
1526     {
1527       mImpl->SendSelectionToClipboard( false ); // Text not modified
1528       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
1529       break;
1530     }
1531     case Toolkit::TextSelectionPopup::PASTE:
1532     {
1533       std::string stringToPaste("");
1534       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
1535       InsertText( stringToPaste, Text::Controller::CLIPBOARD );
1536       mImpl->ChangeState( EventData::EDITING );
1537       mImpl->RequestRelayout();
1538       break;
1539     }
1540     case Toolkit::TextSelectionPopup::SELECT:
1541     {
1542       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1543
1544       if( mImpl->mEventData->mSelectionEnabled  )
1545       {
1546         // Creates a SELECT event.
1547         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
1548       }
1549       break;
1550     }
1551     case Toolkit::TextSelectionPopup::SELECT_ALL:
1552     {
1553       // Creates a SELECT_ALL event
1554       SelectEvent( 0.f, 0.f, true );
1555       break;
1556     }
1557     case Toolkit::TextSelectionPopup::CLIPBOARD:
1558     {
1559       break;
1560     }
1561     case Toolkit::TextSelectionPopup::NONE:
1562     {
1563       // Nothing to do.
1564       break;
1565     }
1566   }
1567 }
1568
1569 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1570 {
1571   bool update( false );
1572   bool requestRelayout = false;
1573
1574   std::string text;
1575   unsigned int cursorPosition( 0 );
1576
1577   switch ( imfEvent.eventName )
1578   {
1579     case ImfManager::COMMIT:
1580     {
1581       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1582       requestRelayout = true;
1583       break;
1584     }
1585     case ImfManager::PREEDIT:
1586     {
1587       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1588       update = true;
1589       requestRelayout = true;
1590       break;
1591     }
1592     case ImfManager::DELETESURROUNDING:
1593     {
1594       RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1595       requestRelayout = true;
1596       break;
1597     }
1598     case ImfManager::GETSURROUNDING:
1599     {
1600       GetText( text );
1601       cursorPosition = GetLogicalCursorPosition();
1602
1603       imfManager.SetSurroundingText( text );
1604       imfManager.SetCursorPosition( cursorPosition );
1605       break;
1606     }
1607     case ImfManager::VOID:
1608     {
1609       // do nothing
1610       break;
1611     }
1612   } // end switch
1613
1614   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1615   {
1616     GetText( text );
1617     cursorPosition = GetLogicalCursorPosition();
1618   }
1619
1620   if( requestRelayout )
1621   {
1622     mImpl->mOperationsPending = ALL_OPERATIONS;
1623     mImpl->RequestRelayout();
1624
1625     // Do this last since it provides callbacks into application code
1626     mImpl->mControlInterface.TextChanged();
1627   }
1628
1629   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1630
1631   return callbackData;
1632 }
1633
1634
1635 Controller::~Controller()
1636 {
1637   delete mImpl;
1638 }
1639
1640 void Controller::ShowPlaceholderText()
1641 {
1642   if( mImpl->IsPlaceholderAvailable() )
1643   {
1644     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1645
1646     mImpl->mEventData->mIsShowingPlaceholderText = true;
1647
1648     // Disable handles when showing place-holder text
1649     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1650     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1651     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1652
1653     const char* text( NULL );
1654     size_t size( 0 );
1655
1656     // TODO - Switch placeholder text styles when changing state
1657     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1658         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1659     {
1660       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1661       size = mImpl->mEventData->mPlaceholderTextActive.size();
1662     }
1663     else
1664     {
1665       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1666       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1667     }
1668
1669     // Reset model for showing placeholder.
1670     mImpl->mLogicalModel->mText.Clear();
1671     ClearModelData();
1672     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1673
1674     // Convert text into UTF-32
1675     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1676     utf32Characters.Resize( size );
1677
1678     // This is a bit horrible but std::string returns a (signed) char*
1679     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1680
1681     // Transform a text array encoded in utf8 into an array encoded in utf32.
1682     // It returns the actual number of characters.
1683     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1684     utf32Characters.Resize( characterCount );
1685
1686     // Reset the cursor position
1687     mImpl->mEventData->mPrimaryCursorPosition = 0;
1688
1689     // The natural size needs to be re-calculated.
1690     mImpl->mRecalculateNaturalSize = true;
1691
1692     // Apply modifications to the model
1693     mImpl->mOperationsPending = ALL_OPERATIONS;
1694
1695     // Update the rest of the model during size negotiation
1696     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1697   }
1698 }
1699
1700 void Controller::ClearModelData()
1701 {
1702   // n.b. This does not Clear the mText from mLogicalModel
1703   mImpl->mLogicalModel->mScriptRuns.Clear();
1704   mImpl->mLogicalModel->mFontRuns.Clear();
1705   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1706   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1707   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1708   mImpl->mLogicalModel->mCharacterDirections.Clear();
1709   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1710   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1711   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1712   mImpl->mVisualModel->mGlyphs.Clear();
1713   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1714   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1715   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1716   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1717   mImpl->mVisualModel->mGlyphPositions.Clear();
1718   mImpl->mVisualModel->mLines.Clear();
1719   mImpl->mVisualModel->ClearCaches();
1720 }
1721
1722 void Controller::ClearFontData()
1723 {
1724   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1725   mImpl->mLogicalModel->mFontRuns.Clear();
1726   mImpl->mVisualModel->mGlyphs.Clear();
1727   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1728   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1729   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1730   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1731   mImpl->mVisualModel->mGlyphPositions.Clear();
1732   mImpl->mVisualModel->mLines.Clear();
1733   mImpl->mVisualModel->ClearCaches();
1734 }
1735
1736 Controller::Controller( ControlInterface& controlInterface )
1737 : mImpl( NULL )
1738 {
1739   mImpl = new Controller::Impl( controlInterface );
1740 }
1741
1742 } // namespace Text
1743
1744 } // namespace Toolkit
1745
1746 } // namespace Dali