Merge "Move tts-player.h from devel-api to public-api" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <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->mScrollAfterDelete = true;
776 }
777
778 bool Controller::DoRelayout( const Size& size,
779                              OperationsMask operationsRequired,
780                              Size& layoutSize )
781 {
782   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
783   bool viewUpdated( false );
784
785   // Calculate the operations to be done.
786   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
787
788   if( LAYOUT & operations )
789   {
790     // Some vectors with data needed to layout and reorder may be void
791     // after the first time the text has been laid out.
792     // Fill the vectors again.
793
794     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
795
796     if( 0u == numberOfGlyphs )
797     {
798       // Nothing else to do if there is no glyphs.
799       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
800       return true;
801     }
802
803     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
804     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
805     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
806     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
807     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
808     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
809
810     // Set the layout parameters.
811     LayoutParameters layoutParameters( size,
812                                        mImpl->mLogicalModel->mText.Begin(),
813                                        lineBreakInfo.Begin(),
814                                        wordBreakInfo.Begin(),
815                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
816                                        numberOfGlyphs,
817                                        glyphs.Begin(),
818                                        glyphsToCharactersMap.Begin(),
819                                        charactersPerGlyph.Begin() );
820
821     // The laid-out lines.
822     // It's not possible to know in how many lines the text is going to be laid-out,
823     // but it can be resized at least with the number of 'paragraphs' to avoid
824     // some re-allocations.
825     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
826
827     // Delete any previous laid out lines before setting the new ones.
828     lines.Clear();
829
830     // The capacity of the bidirectional paragraph info is the number of paragraphs.
831     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
832
833     // Resize the vector of positions to have the same size than the vector of glyphs.
834     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
835     glyphPositions.Resize( numberOfGlyphs );
836
837     // Update the visual model.
838     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
839                                                    glyphPositions,
840                                                    lines,
841                                                    layoutSize );
842
843     if( viewUpdated )
844     {
845       // Reorder the lines
846       if( REORDER & operations )
847       {
848         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
849
850         // Check first if there are paragraphs with bidirectional info.
851         if( 0u != bidirectionalInfo.Count() )
852         {
853           // Get the lines
854           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
855
856           // Reorder the lines.
857           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
858           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
859           ReorderLines( bidirectionalInfo,
860                         lines,
861                         lineBidirectionalInfoRuns );
862
863           // Set the bidirectional info into the model.
864           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
865           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
866                                                        numberOfBidirectionalInfoRuns );
867
868           // Set the bidirectional info per line into the layout parameters.
869           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
870           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
871
872           // Get the character to glyph conversion table and set into the layout.
873           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
874
875           // Get the glyphs per character table and set into the layout.
876           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
877
878           // Re-layout the text. Reorder those lines with right to left characters.
879           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
880                                                          glyphPositions );
881
882           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
883           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
884                  endIt = lineBidirectionalInfoRuns.End();
885                it != endIt;
886                ++it )
887           {
888             BidirectionalLineInfoRun& bidiLineInfo = *it;
889
890             free( bidiLineInfo.visualToLogicalMap );
891           }
892         }
893       } // REORDER
894
895       // Sets the actual size.
896       if( UPDATE_ACTUAL_SIZE & operations )
897       {
898         mImpl->mVisualModel->SetActualSize( layoutSize );
899       }
900     } // view updated
901   }
902   else
903   {
904     layoutSize = mImpl->mVisualModel->GetActualSize();
905   }
906
907   if( ALIGN & operations )
908   {
909     // The laid-out lines.
910     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
911
912     mImpl->mLayoutEngine.Align( layoutSize,
913                                 lines );
914
915     viewUpdated = true;
916   }
917
918   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
919   return viewUpdated;
920 }
921
922 void Controller::SetMultiLineEnabled( bool enable )
923 {
924   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
925
926   if( layout != mImpl->mLayoutEngine.GetLayout() )
927   {
928     // Set the layout type.
929     mImpl->mLayoutEngine.SetLayout( layout );
930
931     // Set the flags to redo the layout operations
932     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
933                                                                           UPDATE_ACTUAL_SIZE |
934                                                                           ALIGN              |
935                                                                           REORDER );
936
937     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
938
939     mImpl->RequestRelayout();
940   }
941 }
942
943 bool Controller::IsMultiLineEnabled() const
944 {
945   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
946 }
947
948 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
949 {
950   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
951   {
952     // Set the alignment.
953     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
954
955     // Set the flag to redo the alignment operation.
956     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
957
958     mImpl->RequestRelayout();
959   }
960 }
961
962 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
963 {
964   return mImpl->mLayoutEngine.GetHorizontalAlignment();
965 }
966
967 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
968 {
969   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
970   {
971     // Set the alignment.
972     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
973
974     // Set the flag to redo the alignment operation.
975     // TODO : Is not needed re-layout and reorder again but with the current implementation it is.
976     //        Im working on a different patch to fix an issue with the alignment. When that patch
977     //        is in, this issue can be fixed.
978     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
979                                                                           UPDATE_ACTUAL_SIZE |
980                                                                           ALIGN              |
981                                                                           REORDER );
982
983     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
984
985     mImpl->RequestRelayout();
986   }
987 }
988
989 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
990 {
991   return mImpl->mLayoutEngine.GetVerticalAlignment();
992 }
993
994 void Controller::CalculateTextAlignment( const Size& size )
995 {
996   // Get the direction of the first character.
997   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
998
999   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1000
1001   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1002   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1003   if( firstParagraphDirection &&
1004       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1005   {
1006     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1007     {
1008       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1009     }
1010     else
1011     {
1012       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1013     }
1014   }
1015
1016   switch( horizontalAlignment )
1017   {
1018     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1019     {
1020       mImpl->mAlignmentOffset.x = 0.f;
1021       break;
1022     }
1023     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1024     {
1025       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1026       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1027       break;
1028     }
1029     case LayoutEngine::HORIZONTAL_ALIGN_END:
1030     {
1031       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1032       break;
1033     }
1034   }
1035
1036   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1037   switch( verticalAlignment )
1038   {
1039     case LayoutEngine::VERTICAL_ALIGN_TOP:
1040     {
1041       mImpl->mAlignmentOffset.y = 0.f;
1042       break;
1043     }
1044     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1045     {
1046       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1047       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1048       break;
1049     }
1050     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1051     {
1052       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1053       break;
1054     }
1055   }
1056 }
1057
1058 LayoutEngine& Controller::GetLayoutEngine()
1059 {
1060   return mImpl->mLayoutEngine;
1061 }
1062
1063 View& Controller::GetView()
1064 {
1065   return mImpl->mView;
1066 }
1067
1068 void Controller::KeyboardFocusGainEvent()
1069 {
1070   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1071
1072   if( mImpl->mEventData )
1073   {
1074     mImpl->ChangeState( EventData::EDITING );
1075
1076     if( mImpl->IsShowingPlaceholderText() )
1077     {
1078       // Show alternative placeholder-text when editing
1079       ShowPlaceholderText();
1080     }
1081
1082     mImpl->RequestRelayout();
1083   }
1084 }
1085
1086 void Controller::KeyboardFocusLostEvent()
1087 {
1088   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1089
1090   if( mImpl->mEventData )
1091   {
1092     mImpl->ChangeState( EventData::INACTIVE );
1093
1094     if( mImpl->IsShowingPlaceholderText() )
1095     {
1096       // Revert to regular placeholder-text when not editing
1097       ShowPlaceholderText();
1098     }
1099
1100     mImpl->RequestRelayout();
1101   }
1102 }
1103
1104 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1105 {
1106   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1107
1108   bool textChanged( false );
1109
1110   if( mImpl->mEventData &&
1111       keyEvent.state == KeyEvent::Down )
1112   {
1113     int keyCode = keyEvent.keyCode;
1114     const std::string& keyString = keyEvent.keyPressed;
1115
1116     // Hide the grab handle.
1117     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1118
1119     // Pre-process to separate modifying events from non-modifying input events.
1120     if( Dali::DALI_KEY_ESCAPE == keyCode )
1121     {
1122       // Escape key is a special case which causes focus loss
1123       KeyboardFocusLostEvent();
1124     }
1125     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1126              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1127              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1128              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1129     {
1130       Event event( Event::CURSOR_KEY_EVENT );
1131       event.p1.mInt = keyCode;
1132       mImpl->mEventData->mEventQueue.push_back( event );
1133     }
1134     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1135     {
1136       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1137
1138       // IMF manager is no longer handling key-events
1139       mImpl->ClearPreEditFlag();
1140
1141       // Remove the character before the current cursor position
1142       bool removed = RemoveText( -1, 1 );
1143
1144       if( removed )
1145       {
1146         if( 0u == mImpl->mLogicalModel->mText.Count() )
1147         {
1148           ShowPlaceholderText();
1149           mImpl->mEventData->mUpdateCursorPosition = true;
1150         }
1151         else
1152         {
1153           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1154         }
1155
1156         textChanged = true;
1157       }
1158     }
1159     else
1160     {
1161       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1162
1163       // IMF manager is no longer handling key-events
1164       mImpl->ClearPreEditFlag();
1165
1166       InsertText( keyString, COMMIT );
1167
1168       textChanged = true;
1169     }
1170
1171     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1172
1173     mImpl->RequestRelayout();
1174   }
1175
1176   if( textChanged )
1177   {
1178     // Do this last since it provides callbacks into application code
1179     mImpl->mControlInterface.TextChanged();
1180   }
1181
1182   return false;
1183 }
1184
1185 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1186 {
1187   bool removedPreEdit( false );
1188   bool maxLengthReached( false );
1189
1190   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1191   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1192                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1193                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1194
1195   Vector<Character> utf32Characters;
1196   Length characterCount( 0u );
1197
1198   if( ! text.empty() )
1199   {
1200     // The placeholder text is no longer needed
1201     if( mImpl->IsShowingPlaceholderText() )
1202     {
1203       ResetText();
1204     }
1205
1206     //  Convert text into UTF-32
1207     utf32Characters.Resize( text.size() );
1208
1209     // This is a bit horrible but std::string returns a (signed) char*
1210     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1211
1212     // Transform a text array encoded in utf8 into an array encoded in utf32.
1213     // It returns the actual number of characters.
1214     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1215     utf32Characters.Resize( characterCount );
1216
1217     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1218     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1219   }
1220
1221   if( 0u != utf32Characters.Count() )
1222   {
1223     // Handle the IMF (predicitive text) state changes
1224     if( mImpl->mEventData )
1225     {
1226       if( mImpl->mEventData->mPreEditFlag &&
1227           0 != mImpl->mEventData->mPreEditLength )
1228       {
1229         // Remove previous pre-edit text
1230         CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1231         removedPreEdit = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1232
1233         mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1234         mImpl->mEventData->mPreEditLength = 0;
1235       }
1236
1237       if( COMMIT == type )
1238       {
1239         // IMF manager is no longer handling key-events
1240         mImpl->ClearPreEditFlag();
1241       }
1242       else // PRE_EDIT
1243       {
1244         if( ! mImpl->mEventData->mPreEditFlag )
1245         {
1246           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1247
1248           // Record the start of the pre-edit text
1249           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1250         }
1251
1252         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1253         mImpl->mEventData->mPreEditFlag = true;
1254
1255         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1256       }
1257     }
1258
1259     const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1260
1261     // Restrict new text to fit within Maximum characters setting
1262     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1263     maxLengthReached = ( characterCount > maxSizeOfNewText );
1264
1265     // Insert at current cursor position
1266     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1267
1268     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1269
1270     if( cursorIndex < numberOfCharactersInModel )
1271     {
1272       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1273     }
1274     else
1275     {
1276       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1277     }
1278
1279     cursorIndex += maxSizeOfNewText;
1280
1281     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1282   }
1283
1284   if( removedPreEdit ||
1285       0 != utf32Characters.Count() )
1286   {
1287     // Queue an inserted event
1288     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1289   }
1290
1291   if( maxLengthReached )
1292   {
1293     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1294
1295     mImpl->ResetImfManager();
1296
1297     // Do this last since it provides callbacks into application code
1298     mImpl->mControlInterface.MaxLengthReached();
1299   }
1300 }
1301
1302 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1303 {
1304   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1305
1306   if( NULL != mImpl->mEventData )
1307   {
1308     if( 1u == tapCount )
1309     {
1310       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
1311
1312       if( ! mImpl->IsShowingPlaceholderText() &&
1313           EventData::EDITING == mImpl->mEventData->mState )
1314       {
1315         // Grab handle is not shown until a tap is received whilst EDITING
1316         if( tapDuringEditMode )
1317         {
1318           mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1319         }
1320         mImpl->mEventData->mDecorator->SetPopupActive( false );
1321       }
1322
1323       mImpl->ChangeState( EventData::EDITING );
1324     }
1325     else if( mImpl->mEventData->mSelectionEnabled &&
1326              ( 2u == tapCount ) )
1327     {
1328       mImpl->ChangeState( EventData::SELECTING );
1329     }
1330   }
1331
1332   // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1333   if( mImpl->mEventData )
1334   {
1335     Event event( Event::TAP_EVENT );
1336     event.p1.mUint = tapCount;
1337     event.p2.mFloat = x;
1338     event.p3.mFloat = y;
1339     mImpl->mEventData->mEventQueue.push_back( event );
1340
1341     mImpl->RequestRelayout();
1342   }
1343
1344   // Reset keyboard as tap event has occurred.
1345   mImpl->ResetImfManager();
1346 }
1347
1348 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1349 {
1350   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1351
1352   if( mImpl->mEventData )
1353   {
1354     Event event( Event::PAN_EVENT );
1355     event.p1.mInt = state;
1356     event.p2.mFloat = displacement.x;
1357     event.p3.mFloat = displacement.y;
1358     mImpl->mEventData->mEventQueue.push_back( event );
1359
1360     mImpl->RequestRelayout();
1361   }
1362 }
1363
1364 void Controller::GetTargetSize( Vector2& targetSize )
1365 {
1366   targetSize = mImpl->mControlSize;
1367 }
1368
1369 void Controller::AddDecoration( Actor& actor )
1370 {
1371   mImpl->mControlInterface.AddDecoration( actor );
1372 }
1373
1374 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1375 {
1376   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1377
1378   if( mImpl->mEventData )
1379   {
1380     switch( handleType )
1381     {
1382       case GRAB_HANDLE:
1383       {
1384         Event event( Event::GRAB_HANDLE_EVENT );
1385         event.p1.mUint  = state;
1386         event.p2.mFloat = x;
1387         event.p3.mFloat = y;
1388
1389         mImpl->mEventData->mEventQueue.push_back( event );
1390         break;
1391       }
1392       case LEFT_SELECTION_HANDLE:
1393       {
1394         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1395         event.p1.mUint  = state;
1396         event.p2.mFloat = x;
1397         event.p3.mFloat = y;
1398
1399         mImpl->mEventData->mEventQueue.push_back( event );
1400         break;
1401       }
1402       case RIGHT_SELECTION_HANDLE:
1403       {
1404         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1405         event.p1.mUint  = state;
1406         event.p2.mFloat = x;
1407         event.p3.mFloat = y;
1408
1409         mImpl->mEventData->mEventQueue.push_back( event );
1410         break;
1411       }
1412       case HANDLE_TYPE_COUNT:
1413       {
1414         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1415       }
1416     }
1417
1418     mImpl->RequestRelayout();
1419   }
1420 }
1421
1422 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1423 {
1424   bool update( false );
1425   bool requestRelayout = false;
1426
1427   std::string text;
1428   unsigned int cursorPosition( 0 );
1429
1430   switch ( imfEvent.eventName )
1431   {
1432     case ImfManager::COMMIT:
1433     {
1434       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1435       requestRelayout = true;
1436       break;
1437     }
1438     case ImfManager::PREEDIT:
1439     {
1440       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1441       update = true;
1442       requestRelayout = true;
1443       break;
1444     }
1445     case ImfManager::DELETESURROUNDING:
1446     {
1447       RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1448       requestRelayout = true;
1449       break;
1450     }
1451     case ImfManager::GETSURROUNDING:
1452     {
1453       GetText( text );
1454       cursorPosition = GetLogicalCursorPosition();
1455
1456       imfManager.SetSurroundingText( text );
1457       imfManager.SetCursorPosition( cursorPosition );
1458       break;
1459     }
1460     case ImfManager::VOID:
1461     {
1462       // do nothing
1463       break;
1464     }
1465   } // end switch
1466
1467   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1468   {
1469     GetText( text );
1470     cursorPosition = GetLogicalCursorPosition();
1471   }
1472
1473   if( requestRelayout )
1474   {
1475     mImpl->mOperationsPending = ALL_OPERATIONS;
1476     mImpl->RequestRelayout();
1477
1478     // Do this last since it provides callbacks into application code
1479     mImpl->mControlInterface.TextChanged();
1480   }
1481
1482   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1483
1484   return callbackData;
1485 }
1486
1487
1488 Controller::~Controller()
1489 {
1490   delete mImpl;
1491 }
1492
1493 void Controller::ShowPlaceholderText()
1494 {
1495   if( mImpl->IsPlaceholderAvailable() )
1496   {
1497     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1498
1499     mImpl->mEventData->mIsShowingPlaceholderText = true;
1500
1501     // Disable handles when showing place-holder text
1502     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1503     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1504     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1505
1506     const char* text( NULL );
1507     size_t size( 0 );
1508
1509     // TODO - Switch placeholder text styles when changing state
1510     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1511         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1512     {
1513       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1514       size = mImpl->mEventData->mPlaceholderTextActive.size();
1515     }
1516     else
1517     {
1518       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1519       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1520     }
1521
1522     // Reset model for showing placeholder.
1523     mImpl->mLogicalModel->mText.Clear();
1524     ClearModelData();
1525     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1526
1527     // Convert text into UTF-32
1528     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1529     utf32Characters.Resize( size );
1530
1531     // This is a bit horrible but std::string returns a (signed) char*
1532     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1533
1534     // Transform a text array encoded in utf8 into an array encoded in utf32.
1535     // It returns the actual number of characters.
1536     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1537     utf32Characters.Resize( characterCount );
1538
1539     // Reset the cursor position
1540     mImpl->mEventData->mPrimaryCursorPosition = 0;
1541
1542     // The natural size needs to be re-calculated.
1543     mImpl->mRecalculateNaturalSize = true;
1544
1545     // Apply modifications to the model
1546     mImpl->mOperationsPending = ALL_OPERATIONS;
1547
1548     // Update the rest of the model during size negotiation
1549     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1550   }
1551 }
1552
1553 void Controller::ClearModelData()
1554 {
1555   // n.b. This does not Clear the mText from mLogicalModel
1556   mImpl->mLogicalModel->mScriptRuns.Clear();
1557   mImpl->mLogicalModel->mFontRuns.Clear();
1558   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1559   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1560   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1561   mImpl->mLogicalModel->mCharacterDirections.Clear();
1562   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1563   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1564   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1565   mImpl->mVisualModel->mGlyphs.Clear();
1566   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1567   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1568   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1569   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1570   mImpl->mVisualModel->mGlyphPositions.Clear();
1571   mImpl->mVisualModel->mLines.Clear();
1572   mImpl->mVisualModel->ClearCaches();
1573 }
1574
1575 void Controller::ClearFontData()
1576 {
1577   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1578   mImpl->mLogicalModel->mFontRuns.Clear();
1579   mImpl->mVisualModel->mGlyphs.Clear();
1580   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1581   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1582   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1583   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1584   mImpl->mVisualModel->mGlyphPositions.Clear();
1585   mImpl->mVisualModel->mLines.Clear();
1586   mImpl->mVisualModel->ClearCaches();
1587 }
1588
1589 Controller::Controller( ControlInterface& controlInterface )
1590 : mImpl( NULL )
1591 {
1592   mImpl = new Controller::Impl( controlInterface );
1593 }
1594
1595 } // namespace Text
1596
1597 } // namespace Toolkit
1598
1599 } // namespace Dali