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