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