Fix prevent issues
[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 + numberOfChars) <= currentText.Count() )
341     {
342       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
343       Vector<Character>::Iterator last  = first + numberOfChars;
344
345       currentText.Erase( first, last );
346
347       // Cursor position retreat
348       oldCursorIndex = cursorIndex;
349
350       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
351       removed = true;
352     }
353   }
354
355   return removed;
356 }
357
358 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
359 {
360   if( mImpl->mEventData )
361   {
362     mImpl->mEventData->mPlaceholderTextColor = textColor;
363   }
364
365   if( mImpl->IsShowingPlaceholderText() )
366   {
367     mImpl->mVisualModel->SetTextColor( textColor );
368     mImpl->RequestRelayout();
369   }
370 }
371
372 const Vector4& Controller::GetPlaceholderTextColor() const
373 {
374   if( mImpl->mEventData )
375   {
376     return mImpl->mEventData->mPlaceholderTextColor;
377   }
378
379   return Color::BLACK;
380 }
381
382 void Controller::SetShadowOffset( const Vector2& shadowOffset )
383 {
384   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
385
386   mImpl->RequestRelayout();
387 }
388
389 const Vector2& Controller::GetShadowOffset() const
390 {
391   return mImpl->mVisualModel->GetShadowOffset();
392 }
393
394 void Controller::SetShadowColor( const Vector4& shadowColor )
395 {
396   mImpl->mVisualModel->SetShadowColor( shadowColor );
397
398   mImpl->RequestRelayout();
399 }
400
401 const Vector4& Controller::GetShadowColor() const
402 {
403   return mImpl->mVisualModel->GetShadowColor();
404 }
405
406 void Controller::SetUnderlineColor( const Vector4& color )
407 {
408   mImpl->mVisualModel->SetUnderlineColor( color );
409
410   mImpl->RequestRelayout();
411 }
412
413 const Vector4& Controller::GetUnderlineColor() const
414 {
415   return mImpl->mVisualModel->GetUnderlineColor();
416 }
417
418 void Controller::SetUnderlineEnabled( bool enabled )
419 {
420   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
421
422   mImpl->RequestRelayout();
423 }
424
425 bool Controller::IsUnderlineEnabled() const
426 {
427   return mImpl->mVisualModel->IsUnderlineEnabled();
428 }
429
430 void Controller::SetUnderlineHeight( float height )
431 {
432   mImpl->mVisualModel->SetUnderlineHeight( height );
433
434   mImpl->RequestRelayout();
435 }
436
437 float Controller::GetUnderlineHeight() const
438 {
439   return mImpl->mVisualModel->GetUnderlineHeight();
440 }
441
442 void Controller::SetEnableCursorBlink( bool enable )
443 {
444   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
445
446   if( mImpl->mEventData )
447   {
448     mImpl->mEventData->mCursorBlinkEnabled = enable;
449
450     if( !enable &&
451         mImpl->mEventData->mDecorator )
452     {
453       mImpl->mEventData->mDecorator->StopCursorBlink();
454     }
455   }
456 }
457
458 bool Controller::GetEnableCursorBlink() const
459 {
460   if( mImpl->mEventData )
461   {
462     return mImpl->mEventData->mCursorBlinkEnabled;
463   }
464
465   return false;
466 }
467
468 const Vector2& Controller::GetScrollPosition() const
469 {
470   if( mImpl->mEventData )
471   {
472     return mImpl->mEventData->mScrollPosition;
473   }
474
475   return Vector2::ZERO;
476 }
477
478 const Vector2& Controller::GetAlignmentOffset() const
479 {
480   return mImpl->mAlignmentOffset;
481 }
482
483 Vector3 Controller::GetNaturalSize()
484 {
485   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
486   Vector3 naturalSize;
487
488   // Make sure the model is up-to-date before layouting
489   ProcessModifyEvents();
490
491   if( mImpl->mRecalculateNaturalSize )
492   {
493     // Operations that can be done only once until the text changes.
494     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
495                                                                            GET_SCRIPTS       |
496                                                                            VALIDATE_FONTS    |
497                                                                            GET_LINE_BREAKS   |
498                                                                            GET_WORD_BREAKS   |
499                                                                            BIDI_INFO         |
500                                                                            SHAPE_TEXT        |
501                                                                            GET_GLYPH_METRICS );
502     // Make sure the model is up-to-date before layouting
503     mImpl->UpdateModel( onlyOnceOperations );
504
505     // Operations that need to be done if the size changes.
506     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
507                                                                         ALIGN  |
508                                                                         REORDER );
509
510     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
511                 static_cast<OperationsMask>( onlyOnceOperations |
512                                              sizeOperations ),
513                 naturalSize.GetVectorXY() );
514
515     // Do not do again the only once operations.
516     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
517
518     // Do the size related operations again.
519     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
520
521     // Stores the natural size to avoid recalculate it again
522     // unless the text/style changes.
523     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
524
525     mImpl->mRecalculateNaturalSize = false;
526
527     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
528   }
529   else
530   {
531     naturalSize = mImpl->mVisualModel->GetNaturalSize();
532
533     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
534   }
535
536   return naturalSize;
537 }
538
539 float Controller::GetHeightForWidth( float width )
540 {
541   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
542   // Make sure the model is up-to-date before layouting
543   ProcessModifyEvents();
544
545   Size layoutSize;
546   if( width != mImpl->mControlSize.width )
547   {
548     // Operations that can be done only once until the text changes.
549     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
550                                                                            GET_SCRIPTS       |
551                                                                            VALIDATE_FONTS    |
552                                                                            GET_LINE_BREAKS   |
553                                                                            GET_WORD_BREAKS   |
554                                                                            BIDI_INFO         |
555                                                                            SHAPE_TEXT        |
556                                                                            GET_GLYPH_METRICS );
557     // Make sure the model is up-to-date before layouting
558     mImpl->UpdateModel( onlyOnceOperations );
559
560     // Operations that need to be done if the size changes.
561     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
562                                                                         ALIGN  |
563                                                                         REORDER );
564
565     DoRelayout( Size( width, MAX_FLOAT ),
566                 static_cast<OperationsMask>( onlyOnceOperations |
567                                              sizeOperations ),
568                 layoutSize );
569
570     // Do not do again the only once operations.
571     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
572
573     // Do the size related operations again.
574     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
575     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
576   }
577   else
578   {
579     layoutSize = mImpl->mVisualModel->GetActualSize();
580     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
581   }
582
583   return layoutSize.height;
584 }
585
586 bool Controller::Relayout( const Size& size )
587 {
588   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
589
590   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
591   {
592     bool glyphsRemoved( false );
593     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
594     {
595       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
596       glyphsRemoved = true;
597     }
598     // Not worth to relayout if width or height is equal to zero.
599     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
600     return glyphsRemoved;
601   }
602
603   if( size != mImpl->mControlSize )
604   {
605     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
606
607     // Operations that need to be done if the size changes.
608     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
609                                                              LAYOUT                    |
610                                                              ALIGN                     |
611                                                              UPDATE_ACTUAL_SIZE        |
612                                                              REORDER );
613
614     mImpl->mControlSize = size;
615   }
616
617   // Make sure the model is up-to-date before layouting
618   ProcessModifyEvents();
619   mImpl->UpdateModel( mImpl->mOperationsPending );
620
621   Size layoutSize;
622   bool updated = DoRelayout( mImpl->mControlSize,
623                              mImpl->mOperationsPending,
624                              layoutSize );
625
626   // Do not re-do any operation until something changes.
627   mImpl->mOperationsPending = NO_OPERATION;
628
629   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
630   CalculateTextAlignment( size );
631
632   if( mImpl->mEventData )
633   {
634     // Move the cursor, grab handle etc.
635     updated = mImpl->ProcessInputEvents() || updated;
636   }
637
638   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
639   return updated;
640 }
641
642 void Controller::ProcessModifyEvents()
643 {
644   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
645
646   for( unsigned int i=0; i<events.size(); ++i )
647   {
648     if( ModifyEvent::TEXT_REPLACED == events[0].type )
649     {
650       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
651       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
652
653       TextReplacedEvent();
654     }
655     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
656     {
657       TextInsertedEvent();
658     }
659     else if( ModifyEvent::TEXT_DELETED == events[0].type )
660     {
661       // Placeholder-text cannot be deleted
662       if( !mImpl->IsShowingPlaceholderText() )
663       {
664         TextDeletedEvent();
665       }
666     }
667   }
668
669   // Discard temporary text
670   events.clear();
671 }
672
673 void Controller::ResetText()
674 {
675   // Reset buffers.
676   mImpl->mLogicalModel->mText.Clear();
677   ClearModelData();
678
679   // We have cleared everything including the placeholder-text
680   mImpl->PlaceholderCleared();
681
682   // The natural size needs to be re-calculated.
683   mImpl->mRecalculateNaturalSize = true;
684
685   // Apply modifications to the model
686   mImpl->mOperationsPending = ALL_OPERATIONS;
687 }
688
689 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
690 {
691   // Reset the cursor position
692   if( NULL != mImpl->mEventData )
693   {
694     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
695
696     // Update the cursor if it's in editing mode.
697     if( ( EventData::EDITING == mImpl->mEventData->mState ) ||
698         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) )
699     {
700       mImpl->mEventData->mUpdateCursorPosition = true;
701     }
702   }
703 }
704
705 void Controller::ResetScrollPosition()
706 {
707   if( NULL != mImpl->mEventData )
708   {
709     // Reset the scroll position.
710     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
711     mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
712   }
713 }
714
715 void Controller::TextReplacedEvent()
716 {
717   // Reset buffers.
718   ClearModelData();
719
720   // The natural size needs to be re-calculated.
721   mImpl->mRecalculateNaturalSize = true;
722
723   // Apply modifications to the model
724   mImpl->mOperationsPending = ALL_OPERATIONS;
725   mImpl->UpdateModel( ALL_OPERATIONS );
726   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
727                                                            ALIGN              |
728                                                            UPDATE_ACTUAL_SIZE |
729                                                            REORDER );
730 }
731
732 void Controller::TextInsertedEvent()
733 {
734   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
735
736   // TODO - Optimize this
737   ClearModelData();
738
739   // The natural size needs to be re-calculated.
740   mImpl->mRecalculateNaturalSize = true;
741
742   // Apply modifications to the model; TODO - Optimize this
743   mImpl->mOperationsPending = ALL_OPERATIONS;
744   mImpl->UpdateModel( ALL_OPERATIONS );
745   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
746                                                            ALIGN              |
747                                                            UPDATE_ACTUAL_SIZE |
748                                                            REORDER );
749
750   // Queue a cursor reposition event; this must wait until after DoRelayout()
751   mImpl->mEventData->mUpdateCursorPosition = true;
752   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
753 }
754
755 void Controller::TextDeletedEvent()
756 {
757   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
758
759   // TODO - Optimize this
760   ClearModelData();
761
762   // The natural size needs to be re-calculated.
763   mImpl->mRecalculateNaturalSize = true;
764
765   // Apply modifications to the model; TODO - Optimize this
766   mImpl->mOperationsPending = ALL_OPERATIONS;
767   mImpl->UpdateModel( ALL_OPERATIONS );
768   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
769                                                            ALIGN              |
770                                                            UPDATE_ACTUAL_SIZE |
771                                                            REORDER );
772
773   // Queue a cursor reposition event; this must wait until after DoRelayout()
774   mImpl->mEventData->mScrollAfterDelete = true;
775 }
776
777 bool Controller::DoRelayout( const Size& size,
778                              OperationsMask operationsRequired,
779                              Size& layoutSize )
780 {
781   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
782   bool viewUpdated( false );
783
784   // Calculate the operations to be done.
785   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
786
787   if( LAYOUT & operations )
788   {
789     // Some vectors with data needed to layout and reorder may be void
790     // after the first time the text has been laid out.
791     // Fill the vectors again.
792
793     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
794
795     if( 0u == numberOfGlyphs )
796     {
797       // Nothing else to do if there is no glyphs.
798       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
799       return true;
800     }
801
802     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
803     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
804     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
805     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
806     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
807     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
808
809     // Set the layout parameters.
810     LayoutParameters layoutParameters( size,
811                                        mImpl->mLogicalModel->mText.Begin(),
812                                        lineBreakInfo.Begin(),
813                                        wordBreakInfo.Begin(),
814                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
815                                        numberOfGlyphs,
816                                        glyphs.Begin(),
817                                        glyphsToCharactersMap.Begin(),
818                                        charactersPerGlyph.Begin() );
819
820     // The laid-out lines.
821     // It's not possible to know in how many lines the text is going to be laid-out,
822     // but it can be resized at least with the number of 'paragraphs' to avoid
823     // some re-allocations.
824     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
825
826     // Delete any previous laid out lines before setting the new ones.
827     lines.Clear();
828
829     // The capacity of the bidirectional paragraph info is the number of paragraphs.
830     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
831
832     // Resize the vector of positions to have the same size than the vector of glyphs.
833     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
834     glyphPositions.Resize( numberOfGlyphs );
835
836     // Update the visual model.
837     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
838                                                    glyphPositions,
839                                                    lines,
840                                                    layoutSize );
841
842     if( viewUpdated )
843     {
844       // Reorder the lines
845       if( REORDER & operations )
846       {
847         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
848
849         // Check first if there are paragraphs with bidirectional info.
850         if( 0u != bidirectionalInfo.Count() )
851         {
852           // Get the lines
853           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
854
855           // Reorder the lines.
856           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
857           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
858           ReorderLines( bidirectionalInfo,
859                         lines,
860                         lineBidirectionalInfoRuns );
861
862           // Set the bidirectional info into the model.
863           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
864           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
865                                                        numberOfBidirectionalInfoRuns );
866
867           // Set the bidirectional info per line into the layout parameters.
868           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
869           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
870
871           // Get the character to glyph conversion table and set into the layout.
872           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
873
874           // Get the glyphs per character table and set into the layout.
875           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
876
877           // Re-layout the text. Reorder those lines with right to left characters.
878           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
879                                                          glyphPositions );
880
881           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
882           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
883                  endIt = lineBidirectionalInfoRuns.End();
884                it != endIt;
885                ++it )
886           {
887             BidirectionalLineInfoRun& bidiLineInfo = *it;
888
889             free( bidiLineInfo.visualToLogicalMap );
890           }
891         }
892       } // REORDER
893
894       // Sets the actual size.
895       if( UPDATE_ACTUAL_SIZE & operations )
896       {
897         mImpl->mVisualModel->SetActualSize( layoutSize );
898       }
899     } // view updated
900   }
901   else
902   {
903     layoutSize = mImpl->mVisualModel->GetActualSize();
904   }
905
906   if( ALIGN & operations )
907   {
908     // The laid-out lines.
909     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
910
911     mImpl->mLayoutEngine.Align( layoutSize,
912                                 lines );
913
914     viewUpdated = true;
915   }
916
917   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
918   return viewUpdated;
919 }
920
921 void Controller::SetMultiLineEnabled( bool enable )
922 {
923   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
924
925   if( layout != mImpl->mLayoutEngine.GetLayout() )
926   {
927     // Set the layout type.
928     mImpl->mLayoutEngine.SetLayout( layout );
929
930     // Set the flags to redo the layout operations
931     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
932                                                                           UPDATE_ACTUAL_SIZE |
933                                                                           ALIGN              |
934                                                                           REORDER );
935
936     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
937
938     mImpl->RequestRelayout();
939   }
940 }
941
942 bool Controller::IsMultiLineEnabled() const
943 {
944   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
945 }
946
947 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
948 {
949   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
950   {
951     // Set the alignment.
952     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
953
954     // Set the flag to redo the alignment operation.
955     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
956
957     mImpl->RequestRelayout();
958   }
959 }
960
961 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
962 {
963   return mImpl->mLayoutEngine.GetHorizontalAlignment();
964 }
965
966 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
967 {
968   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
969   {
970     // Set the alignment.
971     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
972
973     // Set the flag to redo the alignment operation.
974     // TODO : Is not needed re-layout and reorder again but with the current implementation it is.
975     //        Im working on a different patch to fix an issue with the alignment. When that patch
976     //        is in, this issue can be fixed.
977     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
978                                                                           UPDATE_ACTUAL_SIZE |
979                                                                           ALIGN              |
980                                                                           REORDER );
981
982     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
983
984     mImpl->RequestRelayout();
985   }
986 }
987
988 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
989 {
990   return mImpl->mLayoutEngine.GetVerticalAlignment();
991 }
992
993 void Controller::CalculateTextAlignment( const Size& size )
994 {
995   // Get the direction of the first character.
996   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
997
998   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
999
1000   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1001   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1002   if( firstParagraphDirection &&
1003       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1004   {
1005     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1006     {
1007       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1008     }
1009     else
1010     {
1011       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1012     }
1013   }
1014
1015   switch( horizontalAlignment )
1016   {
1017     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1018     {
1019       mImpl->mAlignmentOffset.x = 0.f;
1020       break;
1021     }
1022     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1023     {
1024       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1025       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1026       break;
1027     }
1028     case LayoutEngine::HORIZONTAL_ALIGN_END:
1029     {
1030       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1031       break;
1032     }
1033   }
1034
1035   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1036   switch( verticalAlignment )
1037   {
1038     case LayoutEngine::VERTICAL_ALIGN_TOP:
1039     {
1040       mImpl->mAlignmentOffset.y = 0.f;
1041       break;
1042     }
1043     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1044     {
1045       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1046       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1047       break;
1048     }
1049     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1050     {
1051       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1052       break;
1053     }
1054   }
1055 }
1056
1057 LayoutEngine& Controller::GetLayoutEngine()
1058 {
1059   return mImpl->mLayoutEngine;
1060 }
1061
1062 View& Controller::GetView()
1063 {
1064   return mImpl->mView;
1065 }
1066
1067 void Controller::KeyboardFocusGainEvent()
1068 {
1069   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1070
1071   if( mImpl->mEventData )
1072   {
1073     mImpl->ChangeState( EventData::EDITING );
1074
1075     if( mImpl->IsShowingPlaceholderText() )
1076     {
1077       // Show alternative placeholder-text when editing
1078       ShowPlaceholderText();
1079     }
1080
1081     mImpl->RequestRelayout();
1082   }
1083 }
1084
1085 void Controller::KeyboardFocusLostEvent()
1086 {
1087   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1088
1089   if( mImpl->mEventData )
1090   {
1091     mImpl->ChangeState( EventData::INACTIVE );
1092
1093     if( mImpl->IsShowingPlaceholderText() )
1094     {
1095       // Revert to regular placeholder-text when not editing
1096       ShowPlaceholderText();
1097     }
1098
1099     mImpl->RequestRelayout();
1100   }
1101 }
1102
1103 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1104 {
1105   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1106
1107   bool textChanged( false );
1108
1109   if( mImpl->mEventData &&
1110       keyEvent.state == KeyEvent::Down )
1111   {
1112     int keyCode = keyEvent.keyCode;
1113     const std::string& keyString = keyEvent.keyPressed;
1114
1115     // Hide the grab handle.
1116     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1117
1118     // Pre-process to separate modifying events from non-modifying input events.
1119     if( Dali::DALI_KEY_ESCAPE == keyCode )
1120     {
1121       // Escape key is a special case which causes focus loss
1122       KeyboardFocusLostEvent();
1123     }
1124     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1125              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1126              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1127              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1128     {
1129       Event event( Event::CURSOR_KEY_EVENT );
1130       event.p1.mInt = keyCode;
1131       mImpl->mEventData->mEventQueue.push_back( event );
1132     }
1133     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1134     {
1135       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1136
1137       // IMF manager is no longer handling key-events
1138       mImpl->ClearPreEditFlag();
1139
1140       // Remove the character before the current cursor position
1141       bool removed = RemoveText( -1, 1 );
1142
1143       if( removed )
1144       {
1145         if( 0u != mImpl->mLogicalModel->mText.Count() ||
1146             !mImpl->IsPlaceholderAvailable() )
1147         {
1148           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1149         }
1150         else
1151         {
1152           ShowPlaceholderText();
1153           mImpl->mEventData->mUpdateCursorPosition = true;
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