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