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