APIs for text editor.
[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 <memory.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/markup-processor.h>
33 #include <dali-toolkit/internal/text/text-controller-impl.h>
34
35 namespace
36 {
37
38 #if defined(DEBUG_ENABLED)
39   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
40 #endif
41
42 const float MAX_FLOAT = std::numeric_limits<float>::max();
43 const unsigned int POINTS_PER_INCH = 72;
44
45 const std::string EMPTY_STRING("");
46
47 float ConvertToEven( float value )
48 {
49   int intValue(static_cast<int>( value ));
50   return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
51 }
52
53 } // namespace
54
55 namespace Dali
56 {
57
58 namespace Toolkit
59 {
60
61 namespace Text
62 {
63
64 /**
65  * @brief Adds a new font description run for the selected text.
66  *
67  * The new font parameters are added after the call to this method.
68  *
69  * @param[in] eventData The event data pointer.
70  * @param[in] logicalModel The logical model where to add the new font description run.
71  * @param[out] startOfSelectedText Index to the first selected character.
72  * @param[out] lengthOfSelectedText Number of selected characters.
73  */
74 FontDescriptionRun& UpdateSelectionFontStyleRun( EventData* eventData,
75                                                  LogicalModelPtr logicalModel,
76                                                  CharacterIndex& startOfSelectedText,
77                                                  Length& lengthOfSelectedText )
78 {
79   const bool handlesCrossed = eventData->mLeftSelectionPosition > eventData->mRightSelectionPosition;
80
81   // Get start and end position of selection
82   startOfSelectedText = handlesCrossed ? eventData->mRightSelectionPosition : eventData->mLeftSelectionPosition;
83   lengthOfSelectedText = ( handlesCrossed ? eventData->mLeftSelectionPosition : eventData->mRightSelectionPosition ) - startOfSelectedText;
84
85   // Add the font run.
86   const VectorBase::SizeType numberOfRuns = logicalModel->mFontDescriptionRuns.Count();
87   logicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
88
89   FontDescriptionRun& fontDescriptionRun = *( logicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
90
91   fontDescriptionRun.characterRun.characterIndex = startOfSelectedText;
92   fontDescriptionRun.characterRun.numberOfCharacters = lengthOfSelectedText;
93
94   // Recalculate the selection highlight as the metrics may have changed.
95   eventData->mUpdateLeftSelectionPosition = true;
96   eventData->mUpdateRightSelectionPosition = true;
97
98   return fontDescriptionRun;
99 }
100
101 ControllerPtr Controller::New( ControlInterface& controlInterface )
102 {
103   return ControllerPtr( new Controller( controlInterface ) );
104 }
105
106 void Controller::EnableTextInput( DecoratorPtr decorator )
107 {
108   if( NULL == mImpl->mEventData )
109   {
110     mImpl->mEventData = new EventData( decorator );
111   }
112 }
113
114 void Controller::SetGlyphType( TextAbstraction::GlyphType glyphType )
115 {
116   // Metrics for bitmap & vector based glyphs are different
117   mImpl->mMetrics->SetGlyphType( glyphType );
118
119   // Clear the font-specific data
120   ClearFontData();
121
122   mImpl->RequestRelayout();
123 }
124
125 void Controller::SetMarkupProcessorEnabled( bool enable )
126 {
127   mImpl->mMarkupProcessorEnabled = enable;
128 }
129
130 bool Controller::IsMarkupProcessorEnabled() const
131 {
132   return mImpl->mMarkupProcessorEnabled;
133 }
134
135 void Controller::SetAutoScrollEnabled( bool enable )
136 {
137   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled[%s] SingleBox[%s]-> [%p]\n", (enable)?"true":"false", ( mImpl->mLayoutEngine.GetLayout() == LayoutEngine::SINGLE_LINE_BOX)?"true":"false", this );
138
139   if ( mImpl->mLayoutEngine.GetLayout() == LayoutEngine::SINGLE_LINE_BOX )
140   {
141     if ( enable )
142     {
143       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n" );
144       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
145                                                                LAYOUT                    |
146                                                                ALIGN                     |
147                                                                UPDATE_ACTUAL_SIZE        |
148                                                                UPDATE_DIRECTION          |
149                                                                REORDER );
150
151     }
152     else
153     {
154       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
155       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
156                                                                LAYOUT                    |
157                                                                ALIGN                     |
158                                                                UPDATE_ACTUAL_SIZE        |
159                                                                REORDER );
160     }
161
162     mImpl->mAutoScrollEnabled = enable;
163     mImpl->RequestRelayout();
164   }
165   else
166   {
167     DALI_LOG_WARNING( "Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored" );
168     mImpl->mAutoScrollEnabled = false;
169   }
170 }
171
172 bool Controller::IsAutoScrollEnabled() const
173 {
174   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::IsAutoScrollEnabled[%s]\n", (mImpl->mAutoScrollEnabled)?"true":"false" );
175
176   return mImpl->mAutoScrollEnabled;
177 }
178
179 CharacterDirection Controller::GetAutoScrollDirection() const
180 {
181   return mImpl->mAutoScrollDirectionRTL;
182 }
183
184 void Controller::SetText( const std::string& text )
185 {
186   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
187
188   // Reset keyboard as text changed
189   mImpl->ResetImfManager();
190
191   // Remove the previously set text and style.
192   ResetText();
193
194   // Remove the style.
195   ClearStyleData();
196
197   CharacterIndex lastCursorIndex = 0u;
198
199   if( NULL != mImpl->mEventData )
200   {
201     // If popup shown then hide it by switching to Editing state
202     if( ( EventData::SELECTING == mImpl->mEventData->mState )          ||
203         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
204         ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) ||
205         ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
206     {
207       mImpl->ChangeState( EventData::EDITING );
208     }
209   }
210
211   if( !text.empty() )
212   {
213     mImpl->mVisualModel->SetTextColor( mImpl->mTextColor );
214
215     MarkupProcessData markupProcessData( mImpl->mLogicalModel->mColorRuns,
216                                          mImpl->mLogicalModel->mFontDescriptionRuns );
217
218     Length textSize = 0u;
219     const uint8_t* utf8 = NULL;
220     if( mImpl->mMarkupProcessorEnabled )
221     {
222       ProcessMarkupString( text, markupProcessData );
223       textSize = markupProcessData.markupProcessedText.size();
224
225       // This is a bit horrible but std::string returns a (signed) char*
226       utf8 = reinterpret_cast<const uint8_t*>( markupProcessData.markupProcessedText.c_str() );
227     }
228     else
229     {
230       textSize = text.size();
231
232       // This is a bit horrible but std::string returns a (signed) char*
233       utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
234     }
235
236     //  Convert text into UTF-32
237     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
238     utf32Characters.Resize( textSize );
239
240     // Transform a text array encoded in utf8 into an array encoded in utf32.
241     // It returns the actual number of characters.
242     Length characterCount = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
243     utf32Characters.Resize( characterCount );
244
245     DALI_ASSERT_DEBUG( textSize >= characterCount && "Invalid UTF32 conversion length" );
246     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, textSize, mImpl->mLogicalModel->mText.Count() );
247
248     // The characters to be added.
249     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mLogicalModel->mText.Count();
250
251     // To reset the cursor position
252     lastCursorIndex = characterCount;
253
254     // Update the rest of the model during size negotiation
255     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
256
257     // The natural size needs to be re-calculated.
258     mImpl->mRecalculateNaturalSize = true;
259
260     // Apply modifications to the model
261     mImpl->mOperationsPending = ALL_OPERATIONS;
262   }
263   else
264   {
265     ShowPlaceholderText();
266   }
267
268   // Resets the cursor position.
269   ResetCursorPosition( lastCursorIndex );
270
271   // Scrolls the text to make the cursor visible.
272   ResetScrollPosition();
273
274   mImpl->RequestRelayout();
275
276   if( NULL != mImpl->mEventData )
277   {
278     // Cancel previously queued events
279     mImpl->mEventData->mEventQueue.clear();
280   }
281
282   // Notify IMF as text changed
283   NotifyImfManager();
284
285   // Do this last since it provides callbacks into application code
286   mImpl->mControlInterface.TextChanged();
287 }
288
289 void Controller::GetText( std::string& text ) const
290 {
291   if( !mImpl->IsShowingPlaceholderText() )
292   {
293     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
294
295     if( 0u != utf32Characters.Count() )
296     {
297       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
298     }
299   }
300   else
301   {
302     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
303   }
304 }
305
306 unsigned int Controller::GetLogicalCursorPosition() const
307 {
308   if( NULL != mImpl->mEventData )
309   {
310     return mImpl->mEventData->mPrimaryCursorPosition;
311   }
312
313   return 0u;
314 }
315
316 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
317 {
318   if( NULL != mImpl->mEventData )
319   {
320     if( PLACEHOLDER_TYPE_INACTIVE == type )
321     {
322       mImpl->mEventData->mPlaceholderTextInactive = text;
323     }
324     else
325     {
326       mImpl->mEventData->mPlaceholderTextActive = text;
327     }
328
329     // Update placeholder if there is no text
330     if( mImpl->IsShowingPlaceholderText() ||
331         ( 0u == mImpl->mLogicalModel->mText.Count() ) )
332     {
333       ShowPlaceholderText();
334     }
335   }
336 }
337
338 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
339 {
340   if( NULL != mImpl->mEventData )
341   {
342     if( PLACEHOLDER_TYPE_INACTIVE == type )
343     {
344       text = mImpl->mEventData->mPlaceholderTextInactive;
345     }
346     else
347     {
348       text = mImpl->mEventData->mPlaceholderTextActive;
349     }
350   }
351 }
352
353 void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
354 {
355   mImpl->mMaximumNumberOfCharacters = maxCharacters;
356 }
357
358 int Controller::GetMaximumNumberOfCharacters()
359 {
360   return mImpl->mMaximumNumberOfCharacters;
361 }
362
363 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
364 {
365   if( NULL == mImpl->mFontDefaults )
366   {
367     mImpl->mFontDefaults = new FontDefaults();
368   }
369
370   mImpl->mFontDefaults->mFontDescription.family = defaultFontFamily;
371   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str());
372   mImpl->mFontDefaults->familyDefined = true;
373
374   // Clear the font-specific data
375   ClearFontData();
376
377   mImpl->RequestRelayout();
378 }
379
380 const std::string& Controller::GetDefaultFontFamily() const
381 {
382   if( NULL != mImpl->mFontDefaults )
383   {
384     return mImpl->mFontDefaults->mFontDescription.family;
385   }
386
387   return EMPTY_STRING;
388 }
389
390 void Controller::SetDefaultFontStyle( const std::string& style )
391 {
392   if( NULL == mImpl->mFontDefaults )
393   {
394     mImpl->mFontDefaults = new FontDefaults();
395   }
396
397   mImpl->mFontDefaults->mFontStyle = style;
398 }
399
400 const std::string& Controller::GetDefaultFontStyle() const
401 {
402   if( NULL != mImpl->mFontDefaults )
403   {
404     return mImpl->mFontDefaults->mFontStyle;
405   }
406
407   return EMPTY_STRING;
408 }
409
410 void Controller::SetDefaultFontWeight( FontWeight weight )
411 {
412   if( NULL == mImpl->mFontDefaults )
413   {
414     mImpl->mFontDefaults = new FontDefaults();
415   }
416
417   mImpl->mFontDefaults->mFontDescription.weight = weight;
418   mImpl->mFontDefaults->weightDefined = true;
419
420   // Clear the font-specific data
421   ClearFontData();
422
423   mImpl->RequestRelayout();
424 }
425
426 FontWeight Controller::GetDefaultFontWeight() const
427 {
428   if( NULL != mImpl->mFontDefaults )
429   {
430     return mImpl->mFontDefaults->mFontDescription.weight;
431   }
432
433   return TextAbstraction::FontWeight::NORMAL;
434 }
435
436 void Controller::SetDefaultFontWidth( FontWidth width )
437 {
438   if( NULL == mImpl->mFontDefaults )
439   {
440     mImpl->mFontDefaults = new FontDefaults();
441   }
442
443   mImpl->mFontDefaults->mFontDescription.width = width;
444   mImpl->mFontDefaults->widthDefined = true;
445
446   // Clear the font-specific data
447   ClearFontData();
448
449   mImpl->RequestRelayout();
450 }
451
452 FontWidth Controller::GetDefaultFontWidth() const
453 {
454   if( NULL != mImpl->mFontDefaults )
455   {
456     return mImpl->mFontDefaults->mFontDescription.width;
457   }
458
459   return TextAbstraction::FontWidth::NORMAL;
460 }
461
462 void Controller::SetDefaultFontSlant( FontSlant slant )
463 {
464   if( NULL == mImpl->mFontDefaults )
465   {
466     mImpl->mFontDefaults = new FontDefaults();
467   }
468
469   mImpl->mFontDefaults->mFontDescription.slant = slant;
470   mImpl->mFontDefaults->slantDefined = true;
471
472   // Clear the font-specific data
473   ClearFontData();
474
475   mImpl->RequestRelayout();
476 }
477
478 FontSlant Controller::GetDefaultFontSlant() const
479 {
480   if( NULL != mImpl->mFontDefaults )
481   {
482     return mImpl->mFontDefaults->mFontDescription.slant;
483   }
484
485   return TextAbstraction::FontSlant::NORMAL;
486 }
487
488 void Controller::SetDefaultPointSize( float pointSize )
489 {
490   if( NULL == mImpl->mFontDefaults )
491   {
492     mImpl->mFontDefaults = new FontDefaults();
493   }
494
495   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
496   mImpl->mFontDefaults->sizeDefined = true;
497
498   unsigned int horizontalDpi( 0u );
499   unsigned int verticalDpi( 0u );
500   mImpl->mFontClient.GetDpi( horizontalDpi, verticalDpi );
501
502   // Adjust the metrics if the fixed-size font should be down-scaled
503   int maxEmojiSize( pointSize/POINTS_PER_INCH * verticalDpi );
504   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultPointSize %p setting MaxEmojiSize %d\n", this, maxEmojiSize );
505   mImpl->mMetrics->SetMaxEmojiSize( maxEmojiSize );
506
507   // Clear the font-specific data
508   ClearFontData();
509
510   mImpl->RequestRelayout();
511 }
512
513 float Controller::GetDefaultPointSize() const
514 {
515   if( NULL != mImpl->mFontDefaults )
516   {
517     return mImpl->mFontDefaults->mDefaultPointSize;
518   }
519
520   return 0.0f;
521 }
522
523 void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
524 {
525   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange");
526
527   if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
528   {
529     DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
530     mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
531
532     ClearFontData();
533
534     mImpl->RequestRelayout();
535   }
536 }
537
538 void Controller::SetTextColor( const Vector4& textColor )
539 {
540   mImpl->mTextColor = textColor;
541
542   if( !mImpl->IsShowingPlaceholderText() )
543   {
544     mImpl->mVisualModel->SetTextColor( textColor );
545
546     mImpl->RequestRelayout();
547   }
548 }
549
550 const Vector4& Controller::GetTextColor() const
551 {
552   return mImpl->mTextColor;
553 }
554
555 bool Controller::RemoveText( int cursorOffset,
556                              int numberOfCharacters,
557                              UpdateInputStyleType type )
558 {
559   bool removed = false;
560
561   if( NULL == mImpl->mEventData )
562   {
563     return removed;
564   }
565
566   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
567                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
568
569   if( !mImpl->IsShowingPlaceholderText() )
570   {
571     // Delete at current cursor position
572     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
573     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
574
575     CharacterIndex cursorIndex = oldCursorIndex;
576
577     // Validate the cursor position & number of characters
578     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
579     {
580       cursorIndex = oldCursorIndex + cursorOffset;
581     }
582
583     if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
584     {
585       numberOfCharacters = currentText.Count() - cursorIndex;
586     }
587
588     if( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
589     {
590       // Mark the paragraphs to be updated.
591       mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
592       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
593
594       // Update the input style and remove the text's style before removing the text.
595
596       if( UPDATE_INPUT_STYLE == type )
597       {
598         // Set first the default input style.
599         mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
600
601         // Update the input style.
602         mImpl->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
603       }
604
605       // Updates the text style runs by removing characters. Runs with no characters are removed.
606       mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
607
608       // Remove the characters.
609       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
610       Vector<Character>::Iterator last  = first + numberOfCharacters;
611
612       currentText.Erase( first, last );
613
614       // Cursor position retreat
615       oldCursorIndex = cursorIndex;
616
617       mImpl->mEventData->mScrollAfterDelete = true;
618
619       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
620       removed = true;
621     }
622   }
623
624   return removed;
625 }
626
627 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
628 {
629   if( NULL != mImpl->mEventData )
630   {
631     mImpl->mEventData->mPlaceholderTextColor = textColor;
632   }
633
634   if( mImpl->IsShowingPlaceholderText() )
635   {
636     mImpl->mVisualModel->SetTextColor( textColor );
637     mImpl->RequestRelayout();
638   }
639 }
640
641 const Vector4& Controller::GetPlaceholderTextColor() const
642 {
643   if( NULL != mImpl->mEventData )
644   {
645     return mImpl->mEventData->mPlaceholderTextColor;
646   }
647
648   return Color::BLACK;
649 }
650
651 void Controller::SetShadowOffset( const Vector2& shadowOffset )
652 {
653   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
654
655   mImpl->RequestRelayout();
656 }
657
658 const Vector2& Controller::GetShadowOffset() const
659 {
660   return mImpl->mVisualModel->GetShadowOffset();
661 }
662
663 void Controller::SetShadowColor( const Vector4& shadowColor )
664 {
665   mImpl->mVisualModel->SetShadowColor( shadowColor );
666
667   mImpl->RequestRelayout();
668 }
669
670 const Vector4& Controller::GetShadowColor() const
671 {
672   return mImpl->mVisualModel->GetShadowColor();
673 }
674
675 void Controller::SetDefaultShadowProperties( const std::string& shadowProperties )
676 {
677   if( NULL == mImpl->mShadowDefaults )
678   {
679     mImpl->mShadowDefaults = new ShadowDefaults();
680   }
681
682   mImpl->mShadowDefaults->properties = shadowProperties;
683 }
684
685 const std::string& Controller::GetDefaultShadowProperties() const
686 {
687   if( NULL != mImpl->mShadowDefaults )
688   {
689     return mImpl->mShadowDefaults->properties;
690   }
691
692   return EMPTY_STRING;
693 }
694
695 void Controller::SetUnderlineColor( const Vector4& color )
696 {
697   mImpl->mVisualModel->SetUnderlineColor( color );
698
699   mImpl->RequestRelayout();
700 }
701
702 const Vector4& Controller::GetUnderlineColor() const
703 {
704   return mImpl->mVisualModel->GetUnderlineColor();
705 }
706
707 void Controller::SetUnderlineEnabled( bool enabled )
708 {
709   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
710
711   mImpl->RequestRelayout();
712 }
713
714 bool Controller::IsUnderlineEnabled() const
715 {
716   return mImpl->mVisualModel->IsUnderlineEnabled();
717 }
718
719 void Controller::SetUnderlineHeight( float height )
720 {
721   mImpl->mVisualModel->SetUnderlineHeight( height );
722
723   mImpl->RequestRelayout();
724 }
725
726 float Controller::GetUnderlineHeight() const
727 {
728   return mImpl->mVisualModel->GetUnderlineHeight();
729 }
730
731 void Controller::SetDefaultUnderlineProperties( const std::string& underlineProperties )
732 {
733   if( NULL == mImpl->mUnderlineDefaults )
734   {
735     mImpl->mUnderlineDefaults = new UnderlineDefaults();
736   }
737
738   mImpl->mUnderlineDefaults->properties = underlineProperties;
739 }
740
741 const std::string& Controller::GetDefaultUnderlineProperties() const
742 {
743   if( NULL != mImpl->mUnderlineDefaults )
744   {
745     return mImpl->mUnderlineDefaults->properties;
746   }
747
748   return EMPTY_STRING;
749 }
750
751 void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
752 {
753   if( NULL == mImpl->mEmbossDefaults )
754   {
755     mImpl->mEmbossDefaults = new EmbossDefaults();
756   }
757
758   mImpl->mEmbossDefaults->properties = embossProperties;
759 }
760
761 const std::string& Controller::GetDefaultEmbossProperties() const
762 {
763   if( NULL != mImpl->mEmbossDefaults )
764   {
765     return mImpl->mEmbossDefaults->properties;
766   }
767
768   return EMPTY_STRING;
769 }
770
771 void Controller::SetDefaultOutlineProperties( const std::string& outlineProperties )
772 {
773   if( NULL == mImpl->mOutlineDefaults )
774   {
775     mImpl->mOutlineDefaults = new OutlineDefaults();
776   }
777
778   mImpl->mOutlineDefaults->properties = outlineProperties;
779 }
780
781 const std::string& Controller::GetDefaultOutlineProperties() const
782 {
783   if( NULL != mImpl->mOutlineDefaults )
784   {
785     return mImpl->mOutlineDefaults->properties;
786   }
787
788   return EMPTY_STRING;
789 }
790
791 void Controller::SetDefaultLineSpacing( float lineSpacing )
792 {
793   //TODO finish implementation
794   mImpl->mLayoutEngine.SetDefaultLineSpacing( lineSpacing );
795 }
796
797 float Controller::GetDefaultLineSpacing() const
798 {
799   return mImpl->mLayoutEngine.GetDefaultLineSpacing();
800 }
801
802 void Controller::SetInputColor( const Vector4& color )
803 {
804   if( NULL != mImpl->mEventData )
805   {
806     mImpl->mEventData->mInputStyle.textColor = color;
807     mImpl->mEventData->mInputStyle.isDefaultColor = false;
808
809     if( EventData::SELECTING == mImpl->mEventData->mState )
810     {
811       const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition;
812
813       // Get start and end position of selection
814       const CharacterIndex startOfSelectedText = handlesCrossed ? mImpl->mEventData->mRightSelectionPosition : mImpl->mEventData->mLeftSelectionPosition;
815       const Length lengthOfSelectedText = ( handlesCrossed ? mImpl->mEventData->mLeftSelectionPosition : mImpl->mEventData->mRightSelectionPosition ) - startOfSelectedText;
816
817       // Add the color run.
818       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
819       mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
820
821       ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
822       colorRun.color = color;
823       colorRun.characterRun.characterIndex = startOfSelectedText;
824       colorRun.characterRun.numberOfCharacters = lengthOfSelectedText;
825
826       // Request to relayout.
827       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | COLOR );
828       mImpl->RequestRelayout();
829
830       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
831       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
832       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
833     }
834   }
835 }
836
837 const Vector4& Controller::GetInputColor() const
838 {
839   if( NULL != mImpl->mEventData )
840   {
841     return mImpl->mEventData->mInputStyle.textColor;
842   }
843
844   // Return the default text's color if there is no EventData.
845   return mImpl->mTextColor;
846
847 }
848
849 void Controller::SetInputFontFamily( const std::string& fontFamily )
850 {
851   if( NULL != mImpl->mEventData )
852   {
853     mImpl->mEventData->mInputStyle.familyName = fontFamily;
854     mImpl->mEventData->mInputStyle.familyDefined = true;
855
856     if( EventData::SELECTING == mImpl->mEventData->mState )
857     {
858       CharacterIndex startOfSelectedText = 0u;
859       Length lengthOfSelectedText = 0u;
860       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
861                                                                             mImpl->mLogicalModel,
862                                                                             startOfSelectedText,
863                                                                             lengthOfSelectedText );
864
865       fontDescriptionRun.familyLength = fontFamily.size();
866       fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
867       memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
868       fontDescriptionRun.familyDefined = true;
869
870       // The memory allocated for the font family name is freed when the font description is removed from the logical model.
871
872       // Request to relayout.
873       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
874                                                                VALIDATE_FONTS            |
875                                                                SHAPE_TEXT                |
876                                                                GET_GLYPH_METRICS         |
877                                                                LAYOUT                    |
878                                                                UPDATE_ACTUAL_SIZE        |
879                                                                REORDER                   |
880                                                                ALIGN );
881       mImpl->mRecalculateNaturalSize = true;
882       mImpl->RequestRelayout();
883
884       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
885       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
886       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
887
888       // As the font changes, recalculate the handle positions is needed.
889       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
890       mImpl->mEventData->mUpdateRightSelectionPosition = true;
891       mImpl->mEventData->mScrollAfterUpdatePosition = true;
892     }
893   }
894 }
895
896 const std::string& Controller::GetInputFontFamily() const
897 {
898   if( NULL != mImpl->mEventData )
899   {
900     return mImpl->mEventData->mInputStyle.familyName;
901   }
902
903   // Return the default font's family if there is no EventData.
904   return GetDefaultFontFamily();
905 }
906
907 void Controller::SetInputFontStyle( const std::string& fontStyle )
908 {
909   if( NULL != mImpl->mEventData )
910   {
911     mImpl->mEventData->mInputStyle.fontStyle = fontStyle;
912   }
913 }
914
915 const std::string& Controller::GetInputFontStyle() const
916 {
917   if( NULL != mImpl->mEventData )
918   {
919     return mImpl->mEventData->mInputStyle.fontStyle;
920   }
921
922   // Return the default font's style if there is no EventData.
923   return GetDefaultFontStyle();
924 }
925
926 void Controller::SetInputFontWeight( FontWeight weight )
927 {
928   if( NULL != mImpl->mEventData )
929   {
930     mImpl->mEventData->mInputStyle.weight = weight;
931     mImpl->mEventData->mInputStyle.weightDefined = true;
932
933     if( EventData::SELECTING == mImpl->mEventData->mState )
934     {
935       CharacterIndex startOfSelectedText = 0u;
936       Length lengthOfSelectedText = 0u;
937       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
938                                                                             mImpl->mLogicalModel,
939                                                                             startOfSelectedText,
940                                                                             lengthOfSelectedText );
941
942       fontDescriptionRun.weight = weight;
943       fontDescriptionRun.weightDefined = true;
944
945       // Request to relayout.
946       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
947                                                                VALIDATE_FONTS            |
948                                                                SHAPE_TEXT                |
949                                                                GET_GLYPH_METRICS         |
950                                                                LAYOUT                    |
951                                                                UPDATE_ACTUAL_SIZE        |
952                                                                REORDER                   |
953                                                                ALIGN );
954       mImpl->mRecalculateNaturalSize = true;
955       mImpl->RequestRelayout();
956
957       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
958       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
959       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
960
961       // As the font might change, recalculate the handle positions is needed.
962       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
963       mImpl->mEventData->mUpdateRightSelectionPosition = true;
964       mImpl->mEventData->mScrollAfterUpdatePosition = true;
965     }
966   }
967 }
968
969 FontWeight Controller::GetInputFontWeight() const
970 {
971   if( NULL != mImpl->mEventData )
972   {
973     return mImpl->mEventData->mInputStyle.weight;
974   }
975
976   return GetDefaultFontWeight();
977 }
978
979 void Controller::SetInputFontWidth( FontWidth width )
980 {
981   if( NULL != mImpl->mEventData )
982   {
983     mImpl->mEventData->mInputStyle.width = width;
984     mImpl->mEventData->mInputStyle.widthDefined = true;
985
986     if( EventData::SELECTING == mImpl->mEventData->mState )
987     {
988       CharacterIndex startOfSelectedText = 0u;
989       Length lengthOfSelectedText = 0u;
990       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
991                                                                             mImpl->mLogicalModel,
992                                                                             startOfSelectedText,
993                                                                             lengthOfSelectedText );
994
995       fontDescriptionRun.width = width;
996       fontDescriptionRun.widthDefined = true;
997
998       // Request to relayout.
999       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1000                                                                VALIDATE_FONTS            |
1001                                                                SHAPE_TEXT                |
1002                                                                GET_GLYPH_METRICS         |
1003                                                                LAYOUT                    |
1004                                                                UPDATE_ACTUAL_SIZE        |
1005                                                                REORDER                   |
1006                                                                ALIGN );
1007       mImpl->mRecalculateNaturalSize = true;
1008       mImpl->RequestRelayout();
1009
1010       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1011       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1012       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1013
1014       // As the font might change, recalculate the handle positions is needed.
1015       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1016       mImpl->mEventData->mUpdateRightSelectionPosition = true;
1017       mImpl->mEventData->mScrollAfterUpdatePosition = true;
1018     }
1019   }
1020 }
1021
1022 FontWidth Controller::GetInputFontWidth() const
1023 {
1024   if( NULL != mImpl->mEventData )
1025   {
1026     return mImpl->mEventData->mInputStyle.width;
1027   }
1028
1029   return GetDefaultFontWidth();
1030 }
1031
1032 void Controller::SetInputFontSlant( FontSlant slant )
1033 {
1034   if( NULL != mImpl->mEventData )
1035   {
1036     mImpl->mEventData->mInputStyle.slant = slant;
1037     mImpl->mEventData->mInputStyle.slantDefined = true;
1038
1039     if( EventData::SELECTING == mImpl->mEventData->mState )
1040     {
1041       CharacterIndex startOfSelectedText = 0u;
1042       Length lengthOfSelectedText = 0u;
1043       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1044                                                                             mImpl->mLogicalModel,
1045                                                                             startOfSelectedText,
1046                                                                             lengthOfSelectedText );
1047
1048       fontDescriptionRun.slant = slant;
1049       fontDescriptionRun.slantDefined = true;
1050
1051       // Request to relayout.
1052       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1053                                                                VALIDATE_FONTS            |
1054                                                                SHAPE_TEXT                |
1055                                                                GET_GLYPH_METRICS         |
1056                                                                LAYOUT                    |
1057                                                                UPDATE_ACTUAL_SIZE        |
1058                                                                REORDER                   |
1059                                                                ALIGN );
1060       mImpl->mRecalculateNaturalSize = true;
1061       mImpl->RequestRelayout();
1062
1063       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1064       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1065       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1066
1067       // As the font might change, recalculate the handle positions is needed.
1068       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1069       mImpl->mEventData->mUpdateRightSelectionPosition = true;
1070       mImpl->mEventData->mScrollAfterUpdatePosition = true;
1071     }
1072   }
1073 }
1074
1075 FontSlant Controller::GetInputFontSlant() const
1076 {
1077   if( NULL != mImpl->mEventData )
1078   {
1079     return mImpl->mEventData->mInputStyle.slant;
1080   }
1081
1082   return GetDefaultFontSlant();
1083 }
1084
1085 void Controller::SetInputFontPointSize( float size )
1086 {
1087   if( NULL != mImpl->mEventData )
1088   {
1089     mImpl->mEventData->mInputStyle.size = size;
1090
1091     if( EventData::SELECTING == mImpl->mEventData->mState )
1092     {
1093       CharacterIndex startOfSelectedText = 0u;
1094       Length lengthOfSelectedText = 0u;
1095       FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1096                                                                             mImpl->mLogicalModel,
1097                                                                             startOfSelectedText,
1098                                                                             lengthOfSelectedText );
1099
1100       fontDescriptionRun.size = static_cast<PointSize26Dot6>( size * 64.f );
1101       fontDescriptionRun.sizeDefined = true;
1102
1103       // Request to relayout.
1104       mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1105                                                                VALIDATE_FONTS            |
1106                                                                SHAPE_TEXT                |
1107                                                                GET_GLYPH_METRICS         |
1108                                                                LAYOUT                    |
1109                                                                UPDATE_ACTUAL_SIZE        |
1110                                                                REORDER                   |
1111                                                                ALIGN );
1112       mImpl->mRecalculateNaturalSize = true;
1113       mImpl->RequestRelayout();
1114
1115       mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1116       mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1117       mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1118
1119       // As the font might change, recalculate the handle positions is needed.
1120       mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1121       mImpl->mEventData->mUpdateRightSelectionPosition = true;
1122       mImpl->mEventData->mScrollAfterUpdatePosition = true;
1123     }
1124   }
1125 }
1126
1127 float Controller::GetInputFontPointSize() const
1128 {
1129   if( NULL != mImpl->mEventData )
1130   {
1131     return mImpl->mEventData->mInputStyle.size;
1132   }
1133
1134   // Return the default font's point size if there is no EventData.
1135   return GetDefaultPointSize();
1136 }
1137
1138 void Controller::SetInputLineSpacing( float lineSpacing )
1139 {
1140   if( NULL != mImpl->mEventData )
1141   {
1142     mImpl->mEventData->mInputStyle.lineSpacing = lineSpacing;
1143   }
1144 }
1145
1146 float Controller::GetInputLineSpacing() const
1147 {
1148   if( NULL != mImpl->mEventData )
1149   {
1150     return mImpl->mEventData->mInputStyle.lineSpacing;
1151   }
1152
1153   return 0.f;
1154 }
1155
1156 void Controller::SetInputShadowProperties( const std::string& shadowProperties )
1157 {
1158   if( NULL != mImpl->mEventData )
1159   {
1160     mImpl->mEventData->mInputStyle.shadowProperties = shadowProperties;
1161   }
1162 }
1163
1164 const std::string& Controller::GetInputShadowProperties() const
1165 {
1166   if( NULL != mImpl->mEventData )
1167   {
1168     return mImpl->mEventData->mInputStyle.shadowProperties;
1169   }
1170
1171   return GetDefaultShadowProperties();
1172 }
1173
1174 void Controller::SetInputUnderlineProperties( const std::string& underlineProperties )
1175 {
1176   if( NULL != mImpl->mEventData )
1177   {
1178     mImpl->mEventData->mInputStyle.underlineProperties = underlineProperties;
1179   }
1180 }
1181
1182 const std::string& Controller::GetInputUnderlineProperties() const
1183 {
1184   if( NULL != mImpl->mEventData )
1185   {
1186     return mImpl->mEventData->mInputStyle.underlineProperties;
1187   }
1188
1189   return GetDefaultUnderlineProperties();
1190 }
1191
1192 void Controller::SetInputEmbossProperties( const std::string& embossProperties )
1193 {
1194   if( NULL != mImpl->mEventData )
1195   {
1196     mImpl->mEventData->mInputStyle.embossProperties = embossProperties;
1197   }
1198 }
1199
1200 const std::string& Controller::GetInputEmbossProperties() const
1201 {
1202   if( NULL != mImpl->mEventData )
1203   {
1204     return mImpl->mEventData->mInputStyle.embossProperties;
1205   }
1206
1207   return GetDefaultEmbossProperties();
1208 }
1209
1210 void Controller::SetInputOutlineProperties( const std::string& outlineProperties )
1211 {
1212   if( NULL != mImpl->mEventData )
1213   {
1214     mImpl->mEventData->mInputStyle.outlineProperties = outlineProperties;
1215   }
1216 }
1217
1218 const std::string& Controller::GetInputOutlineProperties() const
1219 {
1220   if( NULL != mImpl->mEventData )
1221   {
1222     return mImpl->mEventData->mInputStyle.outlineProperties;
1223   }
1224
1225   return GetDefaultOutlineProperties();
1226 }
1227
1228 void Controller::SetEnableCursorBlink( bool enable )
1229 {
1230   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
1231
1232   if( NULL != mImpl->mEventData )
1233   {
1234     mImpl->mEventData->mCursorBlinkEnabled = enable;
1235
1236     if( !enable &&
1237         mImpl->mEventData->mDecorator )
1238     {
1239       mImpl->mEventData->mDecorator->StopCursorBlink();
1240     }
1241   }
1242 }
1243
1244 bool Controller::GetEnableCursorBlink() const
1245 {
1246   if( NULL != mImpl->mEventData )
1247   {
1248     return mImpl->mEventData->mCursorBlinkEnabled;
1249   }
1250
1251   return false;
1252 }
1253
1254 const Vector2& Controller::GetScrollPosition() const
1255 {
1256   if( NULL != mImpl->mEventData )
1257   {
1258     return mImpl->mEventData->mScrollPosition;
1259   }
1260
1261   return Vector2::ZERO;
1262 }
1263
1264 const Vector2& Controller::GetAlignmentOffset() const
1265 {
1266   return mImpl->mAlignmentOffset;
1267 }
1268
1269 Vector3 Controller::GetNaturalSize()
1270 {
1271   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
1272   Vector3 naturalSize;
1273
1274   // Make sure the model is up-to-date before layouting
1275   ProcessModifyEvents();
1276
1277   if( mImpl->mRecalculateNaturalSize )
1278   {
1279     // Operations that can be done only once until the text changes.
1280     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
1281                                                                            GET_SCRIPTS       |
1282                                                                            VALIDATE_FONTS    |
1283                                                                            GET_LINE_BREAKS   |
1284                                                                            GET_WORD_BREAKS   |
1285                                                                            BIDI_INFO         |
1286                                                                            SHAPE_TEXT        |
1287                                                                            GET_GLYPH_METRICS );
1288     // Make sure the model is up-to-date before layouting
1289     mImpl->UpdateModel( onlyOnceOperations );
1290
1291     // Layout the text for the new width.
1292     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
1293
1294     // Set the update info to relayout the whole text.
1295     mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1296     mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
1297
1298     // Store the actual control's size to restore later.
1299     const Size actualControlSize = mImpl->mVisualModel->mControlSize;
1300
1301     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1302                 static_cast<OperationsMask>( onlyOnceOperations |
1303                                              LAYOUT ),
1304                 naturalSize.GetVectorXY() );
1305
1306     // Do not do again the only once operations.
1307     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1308
1309     // Do the size related operations again.
1310     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
1311                                                                         ALIGN  |
1312                                                                         REORDER );
1313     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1314
1315     // Stores the natural size to avoid recalculate it again
1316     // unless the text/style changes.
1317     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1318
1319     mImpl->mRecalculateNaturalSize = false;
1320
1321     // Clear the update info. This info will be set the next time the text is updated.
1322     mImpl->mTextUpdateInfo.Clear();
1323
1324     // Restore the actual control's size.
1325     mImpl->mVisualModel->mControlSize = actualControlSize;
1326
1327     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1328   }
1329   else
1330   {
1331     naturalSize = mImpl->mVisualModel->GetNaturalSize();
1332
1333     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1334   }
1335
1336   naturalSize.x = ConvertToEven( naturalSize.x );
1337   naturalSize.y = ConvertToEven( naturalSize.y );
1338
1339   return naturalSize;
1340 }
1341
1342 float Controller::GetHeightForWidth( float width )
1343 {
1344   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
1345   // Make sure the model is up-to-date before layouting
1346   ProcessModifyEvents();
1347
1348   Size layoutSize;
1349   if( fabsf( width - mImpl->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 )
1350   {
1351     // Operations that can be done only once until the text changes.
1352     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
1353                                                                            GET_SCRIPTS       |
1354                                                                            VALIDATE_FONTS    |
1355                                                                            GET_LINE_BREAKS   |
1356                                                                            GET_WORD_BREAKS   |
1357                                                                            BIDI_INFO         |
1358                                                                            SHAPE_TEXT        |
1359                                                                            GET_GLYPH_METRICS );
1360     // Make sure the model is up-to-date before layouting
1361     mImpl->UpdateModel( onlyOnceOperations );
1362
1363
1364     // Layout the text for the new width.
1365     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
1366
1367     // Set the update info to relayout the whole text.
1368     mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1369     mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
1370
1371     // Store the actual control's width.
1372     const float actualControlWidth = mImpl->mVisualModel->mControlSize.width;
1373
1374     DoRelayout( Size( width, MAX_FLOAT ),
1375                 static_cast<OperationsMask>( onlyOnceOperations |
1376                                              LAYOUT ),
1377                 layoutSize );
1378
1379     // Do not do again the only once operations.
1380     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1381
1382     // Do the size related operations again.
1383     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
1384                                                                         ALIGN  |
1385                                                                         REORDER );
1386
1387     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1388
1389     // Clear the update info. This info will be set the next time the text is updated.
1390     mImpl->mTextUpdateInfo.Clear();
1391
1392     // Restore the actual control's width.
1393     mImpl->mVisualModel->mControlSize.width = actualControlWidth;
1394
1395     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
1396   }
1397   else
1398   {
1399     layoutSize = mImpl->mVisualModel->GetLayoutSize();
1400     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
1401   }
1402
1403   return layoutSize.height;
1404 }
1405
1406 bool Controller::Relayout( const Size& size )
1407 {
1408   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, (mImpl->mAutoScrollEnabled)?"true":"false"  );
1409
1410   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1411   {
1412     bool glyphsRemoved( false );
1413     if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
1414     {
1415       mImpl->mVisualModel->mGlyphPositions.Clear();
1416       glyphsRemoved = true;
1417     }
1418
1419     // Clear the update info. This info will be set the next time the text is updated.
1420     mImpl->mTextUpdateInfo.Clear();
1421
1422     // Not worth to relayout if width or height is equal to zero.
1423     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
1424
1425     return glyphsRemoved;
1426   }
1427
1428   // Whether a new size has been set.
1429   const bool newSize = ( size != mImpl->mVisualModel->mControlSize );
1430
1431   if( newSize )
1432   {
1433     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
1434
1435     // Layout operations that need to be done if the size changes.
1436     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1437                                                              LAYOUT                    |
1438                                                              ALIGN                     |
1439                                                              UPDATE_ACTUAL_SIZE        |
1440                                                              REORDER );
1441     // Set the update info to relayout the whole text.
1442     mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
1443     mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
1444   }
1445
1446   // Whether there are modify events.
1447   if( 0u != mImpl->mModifyEvents.Count() )
1448   {
1449     // Style operations that need to be done if the text is modified.
1450     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1451                                                              COLOR );
1452   }
1453
1454   // Make sure the model is up-to-date before layouting.
1455   ProcessModifyEvents();
1456   bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
1457
1458   // Layout the text.
1459   Size layoutSize;
1460   updated = DoRelayout( size,
1461                         mImpl->mOperationsPending,
1462                         layoutSize ) || updated;
1463
1464   // Do not re-do any operation until something changes.
1465   mImpl->mOperationsPending = NO_OPERATION;
1466
1467   // Whether the text control is editable
1468   const bool isEditable = NULL != mImpl->mEventData;
1469
1470   // Keep the current offset and alignment as it will be used to update the decorator's positions (if the size changes).
1471   Vector2 offset;
1472   if( newSize && isEditable )
1473   {
1474     offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
1475   }
1476
1477   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1478   CalculateTextAlignment( size );
1479
1480   if( isEditable )
1481   {
1482     if( newSize )
1483     {
1484       // If there is a new size, the scroll position needs to be clamped.
1485       mImpl->ClampHorizontalScroll( layoutSize );
1486
1487       // Update the decorator's positions is needed if there is a new size.
1488       mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
1489     }
1490
1491     // Move the cursor, grab handle etc.
1492     updated = mImpl->ProcessInputEvents() || updated;
1493   }
1494
1495   // Clear the update info. This info will be set the next time the text is updated.
1496   mImpl->mTextUpdateInfo.Clear();
1497   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
1498
1499   return updated;
1500 }
1501
1502 void Controller::ProcessModifyEvents()
1503 {
1504   Vector<ModifyEvent>& events = mImpl->mModifyEvents;
1505
1506   if( 0u == events.Count() )
1507   {
1508     // Nothing to do.
1509     return;
1510   }
1511
1512   for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
1513          endIt = events.End();
1514        it != endIt;
1515        ++it )
1516   {
1517     const ModifyEvent& event = *it;
1518
1519     if( ModifyEvent::TEXT_REPLACED == event.type )
1520     {
1521       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1522       DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
1523
1524       TextReplacedEvent();
1525     }
1526     else if( ModifyEvent::TEXT_INSERTED == event.type )
1527     {
1528       TextInsertedEvent();
1529     }
1530     else if( ModifyEvent::TEXT_DELETED == event.type )
1531     {
1532       // Placeholder-text cannot be deleted
1533       if( !mImpl->IsShowingPlaceholderText() )
1534       {
1535         TextDeletedEvent();
1536       }
1537     }
1538   }
1539
1540   if( NULL != mImpl->mEventData )
1541   {
1542     // When the text is being modified, delay cursor blinking
1543     mImpl->mEventData->mDecorator->DelayCursorBlink();
1544   }
1545
1546   // Discard temporary text
1547   events.Clear();
1548 }
1549
1550 void Controller::ResetText()
1551 {
1552   // Reset buffers.
1553   mImpl->mLogicalModel->mText.Clear();
1554
1555   // We have cleared everything including the placeholder-text
1556   mImpl->PlaceholderCleared();
1557
1558   mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
1559   mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
1560   mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
1561
1562   // Clear any previous text.
1563   mImpl->mTextUpdateInfo.mClearAll = true;
1564
1565   // The natural size needs to be re-calculated.
1566   mImpl->mRecalculateNaturalSize = true;
1567
1568   // Apply modifications to the model
1569   mImpl->mOperationsPending = ALL_OPERATIONS;
1570 }
1571
1572 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
1573 {
1574   // Reset the cursor position
1575   if( NULL != mImpl->mEventData )
1576   {
1577     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
1578
1579     // Update the cursor if it's in editing mode.
1580     if( EventData::IsEditingState( mImpl->mEventData->mState )  )
1581     {
1582       mImpl->mEventData->mUpdateCursorPosition = true;
1583     }
1584   }
1585 }
1586
1587 void Controller::ResetScrollPosition()
1588 {
1589   if( NULL != mImpl->mEventData )
1590   {
1591     // Reset the scroll position.
1592     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
1593     mImpl->mEventData->mScrollAfterUpdatePosition = true;
1594   }
1595 }
1596
1597 void Controller::TextReplacedEvent()
1598 {
1599   // The natural size needs to be re-calculated.
1600   mImpl->mRecalculateNaturalSize = true;
1601
1602   // Apply modifications to the model
1603   mImpl->mOperationsPending = ALL_OPERATIONS;
1604 }
1605
1606 void Controller::TextInsertedEvent()
1607 {
1608   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
1609
1610   if( NULL == mImpl->mEventData )
1611   {
1612     return;
1613   }
1614
1615   // The natural size needs to be re-calculated.
1616   mImpl->mRecalculateNaturalSize = true;
1617
1618   // Apply modifications to the model; TODO - Optimize this
1619   mImpl->mOperationsPending = ALL_OPERATIONS;
1620 }
1621
1622 void Controller::TextDeletedEvent()
1623 {
1624   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
1625
1626   if( NULL == mImpl->mEventData )
1627   {
1628     return;
1629   }
1630
1631   // The natural size needs to be re-calculated.
1632   mImpl->mRecalculateNaturalSize = true;
1633
1634   // Apply modifications to the model; TODO - Optimize this
1635   mImpl->mOperationsPending = ALL_OPERATIONS;
1636 }
1637
1638 bool Controller::DoRelayout( const Size& size,
1639                              OperationsMask operationsRequired,
1640                              Size& layoutSize )
1641 {
1642   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
1643   bool viewUpdated( false );
1644
1645   // Calculate the operations to be done.
1646   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1647
1648   const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
1649   const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
1650
1651   if( NO_OPERATION != ( LAYOUT & operations ) )
1652   {
1653     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
1654
1655     // Some vectors with data needed to layout and reorder may be void
1656     // after the first time the text has been laid out.
1657     // Fill the vectors again.
1658
1659     // Calculate the number of glyphs to layout.
1660     const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
1661     const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
1662     const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
1663     const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
1664
1665     const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
1666     const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
1667     const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
1668     const Length totalNumberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
1669
1670     if( 0u == totalNumberOfGlyphs )
1671     {
1672       if( NO_OPERATION != ( UPDATE_ACTUAL_SIZE & operations ) )
1673       {
1674         mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
1675       }
1676
1677       // Nothing else to do if there is no glyphs.
1678       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
1679       return true;
1680     }
1681
1682     const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1683     const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1684     const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
1685     const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1686     const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1687     const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1688     const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
1689
1690     // Set the layout parameters.
1691     LayoutParameters layoutParameters( size,
1692                                        textBuffer,
1693                                        lineBreakInfo.Begin(),
1694                                        wordBreakInfo.Begin(),
1695                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
1696                                        glyphs.Begin(),
1697                                        glyphsToCharactersMap.Begin(),
1698                                        charactersPerGlyph.Begin(),
1699                                        charactersToGlyphBuffer,
1700                                        glyphsPerCharacterBuffer,
1701                                        totalNumberOfGlyphs );
1702
1703     // Resize the vector of positions to have the same size than the vector of glyphs.
1704     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1705     glyphPositions.Resize( totalNumberOfGlyphs );
1706
1707     // Whether the last character is a new paragraph character.
1708     mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph =  TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
1709     layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
1710
1711     // The initial glyph and the number of glyphs to layout.
1712     layoutParameters.startGlyphIndex = startGlyphIndex;
1713     layoutParameters.numberOfGlyphs = numberOfGlyphs;
1714     layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
1715     layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
1716
1717     // Update the visual model.
1718     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1719                                                    glyphPositions,
1720                                                    mImpl->mVisualModel->mLines,
1721                                                    layoutSize );
1722
1723
1724     if( viewUpdated )
1725     {
1726       if ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
1727       {
1728         mImpl->mAutoScrollDirectionRTL = false;
1729       }
1730
1731       // Reorder the lines
1732       if( NO_OPERATION != ( REORDER & operations ) )
1733       {
1734         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1735         Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
1736
1737         // Check first if there are paragraphs with bidirectional info.
1738         if( 0u != bidirectionalInfo.Count() )
1739         {
1740           // Get the lines
1741           const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
1742
1743           // Reorder the lines.
1744           bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1745           ReorderLines( bidirectionalInfo,
1746                         startIndex,
1747                         requestedNumberOfCharacters,
1748                         mImpl->mVisualModel->mLines,
1749                         bidirectionalLineInfo );
1750
1751           // Set the bidirectional info per line into the layout parameters.
1752           layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
1753           layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
1754
1755           // Re-layout the text. Reorder those lines with right to left characters.
1756           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1757                                                          startIndex,
1758                                                          requestedNumberOfCharacters,
1759                                                          glyphPositions );
1760
1761           if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
1762           {
1763             const LineRun* const firstline = mImpl->mVisualModel->mLines.Begin();
1764             if ( firstline )
1765             {
1766               mImpl->mAutoScrollDirectionRTL = firstline->direction;
1767             }
1768           }
1769         }
1770       } // REORDER
1771
1772       // Sets the actual size.
1773       if( NO_OPERATION != ( UPDATE_ACTUAL_SIZE & operations ) )
1774       {
1775         mImpl->mVisualModel->SetLayoutSize( layoutSize );
1776       }
1777     } // view updated
1778
1779     // Store the size used to layout the text.
1780     mImpl->mVisualModel->mControlSize = size;
1781   }
1782   else
1783   {
1784     layoutSize = mImpl->mVisualModel->GetLayoutSize();
1785   }
1786
1787   if( NO_OPERATION != ( ALIGN & operations ) )
1788   {
1789     // The laid-out lines.
1790     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1791
1792     mImpl->mLayoutEngine.Align( size,
1793                                 startIndex,
1794                                 requestedNumberOfCharacters,
1795                                 lines );
1796
1797     viewUpdated = true;
1798   }
1799 #if defined(DEBUG_ENABLED)
1800   std::string currentText;
1801   GetText( currentText );
1802   DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false",  currentText.c_str() );
1803 #endif
1804   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
1805   return viewUpdated;
1806 }
1807
1808 void Controller::SetMultiLineEnabled( bool enable )
1809 {
1810   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
1811
1812   if( layout != mImpl->mLayoutEngine.GetLayout() )
1813   {
1814     // Set the layout type.
1815     mImpl->mLayoutEngine.SetLayout( layout );
1816
1817     // Set the flags to redo the layout operations
1818     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
1819                                                                           UPDATE_ACTUAL_SIZE |
1820                                                                           ALIGN              |
1821                                                                           REORDER );
1822
1823     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
1824
1825     mImpl->RequestRelayout();
1826   }
1827 }
1828
1829 bool Controller::IsMultiLineEnabled() const
1830 {
1831   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
1832 }
1833
1834 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
1835 {
1836   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1837   {
1838     // Set the alignment.
1839     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1840
1841     // Set the flag to redo the alignment operation.
1842     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1843
1844     mImpl->RequestRelayout();
1845   }
1846 }
1847
1848 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1849 {
1850   return mImpl->mLayoutEngine.GetHorizontalAlignment();
1851 }
1852
1853 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1854 {
1855   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1856   {
1857     // Set the alignment.
1858     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1859
1860     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1861
1862     mImpl->RequestRelayout();
1863   }
1864 }
1865
1866 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1867 {
1868   return mImpl->mLayoutEngine.GetVerticalAlignment();
1869 }
1870
1871 void Controller::CalculateTextAlignment( const Size& controlSize )
1872 {
1873   Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
1874
1875   if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
1876   {
1877     // Get the line height of the default font.
1878     layoutSize.height = mImpl->GetDefaultFontLineHeight();
1879   }
1880
1881   if( LayoutEngine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
1882   {
1883     // Get the direction of the first character.
1884     const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1885
1886     // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1887     LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1888     if( firstParagraphDirection )
1889     {
1890       switch( horizontalAlignment )
1891       {
1892         case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1893         {
1894           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1895           break;
1896         }
1897         case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1898         {
1899           // Nothing to do.
1900           break;
1901         }
1902         case LayoutEngine::HORIZONTAL_ALIGN_END:
1903         {
1904           horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1905           break;
1906         }
1907       }
1908     }
1909
1910     switch( horizontalAlignment )
1911     {
1912       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1913       {
1914         mImpl->mAlignmentOffset.x = 0.f;
1915         break;
1916       }
1917       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1918       {
1919         mImpl->mAlignmentOffset.x = floorf( 0.5f * ( controlSize.width - layoutSize.width ) ); // try to avoid pixel alignment.
1920         break;
1921       }
1922       case LayoutEngine::HORIZONTAL_ALIGN_END:
1923       {
1924         mImpl->mAlignmentOffset.x = controlSize.width - layoutSize.width;
1925         break;
1926       }
1927     }
1928   }
1929
1930   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1931   switch( verticalAlignment )
1932   {
1933     case LayoutEngine::VERTICAL_ALIGN_TOP:
1934     {
1935       mImpl->mAlignmentOffset.y = 0.f;
1936       break;
1937     }
1938     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1939     {
1940       mImpl->mAlignmentOffset.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
1941       break;
1942     }
1943     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1944     {
1945       mImpl->mAlignmentOffset.y = controlSize.height - layoutSize.height;
1946       break;
1947     }
1948   }
1949 }
1950
1951 LayoutEngine& Controller::GetLayoutEngine()
1952 {
1953   return mImpl->mLayoutEngine;
1954 }
1955
1956 View& Controller::GetView()
1957 {
1958   return mImpl->mView;
1959 }
1960
1961 void Controller::KeyboardFocusGainEvent()
1962 {
1963   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1964
1965   if( NULL != mImpl->mEventData )
1966   {
1967     if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1968         ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1969     {
1970       mImpl->ChangeState( EventData::EDITING );
1971       mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1972     }
1973
1974     if( mImpl->IsShowingPlaceholderText() )
1975     {
1976       // Show alternative placeholder-text when editing
1977       ShowPlaceholderText();
1978     }
1979
1980     mImpl->RequestRelayout();
1981   }
1982 }
1983
1984 void Controller::KeyboardFocusLostEvent()
1985 {
1986   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1987
1988   if( NULL != mImpl->mEventData )
1989   {
1990     if( EventData::INTERRUPTED != mImpl->mEventData->mState )
1991     {
1992       mImpl->ChangeState( EventData::INACTIVE );
1993
1994       if( !mImpl->IsShowingRealText() )
1995       {
1996         // Revert to regular placeholder-text when not editing
1997         ShowPlaceholderText();
1998       }
1999     }
2000   }
2001   mImpl->RequestRelayout();
2002 }
2003
2004 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2005 {
2006   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
2007
2008   bool textChanged( false );
2009
2010   if( ( NULL != mImpl->mEventData ) &&
2011       ( keyEvent.state == KeyEvent::Down ) )
2012   {
2013     int keyCode = keyEvent.keyCode;
2014     const std::string& keyString = keyEvent.keyPressed;
2015
2016     // Pre-process to separate modifying events from non-modifying input events.
2017     if( Dali::DALI_KEY_ESCAPE == keyCode )
2018     {
2019       // Escape key is a special case which causes focus loss
2020       KeyboardFocusLostEvent();
2021     }
2022     else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
2023              ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
2024              ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
2025              ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
2026     {
2027       Event event( Event::CURSOR_KEY_EVENT );
2028       event.p1.mInt = keyCode;
2029       mImpl->mEventData->mEventQueue.push_back( event );
2030     }
2031     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2032     {
2033       textChanged = BackspaceKeyEvent();
2034     }
2035     else if( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
2036     {
2037       mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
2038       // Avoids calling the InsertText() method which can delete selected text
2039     }
2040     else if( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
2041              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
2042     {
2043       mImpl->ChangeState( EventData::INACTIVE );
2044       // Menu/Home key behaviour does not allow edit mode to resume like Power key
2045       // Avoids calling the InsertText() method which can delete selected text
2046     }
2047     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
2048     {
2049       // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
2050       // and a character is typed after the type of a upper case latin character.
2051
2052       // Do nothing.
2053     }
2054     else
2055     {
2056       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
2057
2058       // IMF manager is no longer handling key-events
2059       mImpl->ClearPreEditFlag();
2060
2061       InsertText( keyString, COMMIT );
2062       textChanged = true;
2063     }
2064
2065     if( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
2066         ( mImpl->mEventData->mState != EventData::INACTIVE ) )
2067     {
2068       mImpl->ChangeState( EventData::EDITING );
2069     }
2070
2071     mImpl->RequestRelayout();
2072   }
2073
2074   if( textChanged )
2075   {
2076     // Do this last since it provides callbacks into application code
2077     mImpl->mControlInterface.TextChanged();
2078   }
2079
2080   return true;
2081 }
2082
2083 void Controller::InsertText( const std::string& text, Controller::InsertType type )
2084 {
2085   bool removedPrevious( false );
2086   bool maxLengthReached( false );
2087
2088   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
2089
2090   if( NULL == mImpl->mEventData )
2091   {
2092     return;
2093   }
2094
2095   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
2096                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
2097                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
2098
2099   // TODO: At the moment the underline runs are only for pre-edit.
2100   mImpl->mVisualModel->mUnderlineRuns.Clear();
2101
2102   // Keep the current number of characters.
2103   const Length currentNumberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
2104
2105   // Remove the previous IMF pre-edit.
2106   if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
2107   {
2108     removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
2109                                   mImpl->mEventData->mPreEditLength,
2110                                   DONT_UPDATE_INPUT_STYLE );
2111
2112     mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
2113     mImpl->mEventData->mPreEditLength = 0u;
2114   }
2115   else
2116   {
2117     // Remove the previous Selection.
2118     removedPrevious = RemoveSelectedText();
2119   }
2120
2121   Vector<Character> utf32Characters;
2122   Length characterCount = 0u;
2123
2124   if( !text.empty() )
2125   {
2126     //  Convert text into UTF-32
2127     utf32Characters.Resize( text.size() );
2128
2129     // This is a bit horrible but std::string returns a (signed) char*
2130     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
2131
2132     // Transform a text array encoded in utf8 into an array encoded in utf32.
2133     // It returns the actual number of characters.
2134     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
2135     utf32Characters.Resize( characterCount );
2136
2137     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
2138     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
2139   }
2140
2141   if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
2142   {
2143     // The placeholder text is no longer needed
2144     if( mImpl->IsShowingPlaceholderText() )
2145     {
2146       ResetText();
2147     }
2148
2149     mImpl->ChangeState( EventData::EDITING );
2150
2151     // Handle the IMF (predicitive text) state changes
2152     if( COMMIT == type )
2153     {
2154       // IMF manager is no longer handling key-events
2155       mImpl->ClearPreEditFlag();
2156     }
2157     else // PRE_EDIT
2158     {
2159       if( !mImpl->mEventData->mPreEditFlag )
2160       {
2161         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
2162
2163         // Record the start of the pre-edit text
2164         mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
2165       }
2166
2167       mImpl->mEventData->mPreEditLength = utf32Characters.Count();
2168       mImpl->mEventData->mPreEditFlag = true;
2169
2170       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
2171     }
2172
2173     const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
2174
2175     // Restrict new text to fit within Maximum characters setting.
2176     Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
2177     maxLengthReached = ( characterCount > maxSizeOfNewText );
2178
2179     // The cursor position.
2180     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
2181
2182     // Update the text's style.
2183
2184     // Updates the text style runs by adding characters.
2185     mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
2186
2187     // Get the character index from the cursor index.
2188     const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
2189
2190     // Retrieve the text's style for the given index.
2191     InputStyle style;
2192     mImpl->RetrieveDefaultInputStyle( style );
2193     mImpl->mLogicalModel->RetrieveStyle( styleIndex, style );
2194
2195     // Whether to add a new text color run.
2196     const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
2197
2198     // Whether to add a new font run.
2199     const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
2200     const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
2201     const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
2202     const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
2203     const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
2204
2205     // Add style runs.
2206     if( addColorRun )
2207     {
2208       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
2209       mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
2210
2211       ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
2212       colorRun.color = mImpl->mEventData->mInputStyle.textColor;
2213       colorRun.characterRun.characterIndex = cursorIndex;
2214       colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
2215     }
2216
2217     if( addFontNameRun   ||
2218         addFontWeightRun ||
2219         addFontWidthRun  ||
2220         addFontSlantRun  ||
2221         addFontSizeRun )
2222     {
2223       const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mFontDescriptionRuns.Count();
2224       mImpl->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
2225
2226       FontDescriptionRun& fontDescriptionRun = *( mImpl->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
2227
2228       if( addFontNameRun )
2229       {
2230         fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
2231         fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
2232         memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
2233         fontDescriptionRun.familyDefined = true;
2234
2235         // The memory allocated for the font family name is freed when the font description is removed from the logical model.
2236       }
2237
2238       if( addFontWeightRun )
2239       {
2240         fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
2241         fontDescriptionRun.weightDefined = true;
2242       }
2243
2244       if( addFontWidthRun )
2245       {
2246         fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
2247         fontDescriptionRun.widthDefined = true;
2248       }
2249
2250       if( addFontSlantRun )
2251       {
2252         fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
2253         fontDescriptionRun.slantDefined = true;
2254       }
2255
2256       if( addFontSizeRun )
2257       {
2258         fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
2259         fontDescriptionRun.sizeDefined = true;
2260       }
2261
2262       fontDescriptionRun.characterRun.characterIndex = cursorIndex;
2263       fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
2264     }
2265
2266     // Insert at current cursor position.
2267     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
2268
2269     if( cursorIndex < numberOfCharactersInModel )
2270     {
2271       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
2272     }
2273     else
2274     {
2275       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
2276     }
2277
2278     // Mark the first paragraph to be updated.
2279     mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
2280     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
2281
2282     // Update the cursor index.
2283     cursorIndex += maxSizeOfNewText;
2284
2285     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
2286   }
2287
2288   const Length numberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
2289
2290   if( ( 0u == mImpl->mLogicalModel->mText.Count() ) &&
2291       mImpl->IsPlaceholderAvailable() )
2292   {
2293     // Show place-holder if empty after removing the pre-edit text
2294     ShowPlaceholderText();
2295     mImpl->mEventData->mUpdateCursorPosition = true;
2296     mImpl->ClearPreEditFlag();
2297   }
2298   else if( removedPrevious ||
2299            ( 0 != utf32Characters.Count() ) )
2300   {
2301     // Queue an inserted event
2302     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
2303
2304     mImpl->mEventData->mUpdateCursorPosition = true;
2305     if( numberOfCharacters < currentNumberOfCharacters )
2306     {
2307       mImpl->mEventData->mScrollAfterDelete = true;
2308     }
2309     else
2310     {
2311       mImpl->mEventData->mScrollAfterUpdatePosition = true;
2312     }
2313   }
2314
2315   if( maxLengthReached )
2316   {
2317     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
2318
2319     mImpl->ResetImfManager();
2320
2321     // Do this last since it provides callbacks into application code
2322     mImpl->mControlInterface.MaxLengthReached();
2323   }
2324 }
2325
2326 bool Controller::RemoveSelectedText()
2327 {
2328   bool textRemoved( false );
2329
2330   if( EventData::SELECTING == mImpl->mEventData->mState )
2331   {
2332     std::string removedString;
2333     mImpl->RetrieveSelection( removedString, true );
2334
2335     if( !removedString.empty() )
2336     {
2337       textRemoved = true;
2338       mImpl->ChangeState( EventData::EDITING );
2339     }
2340   }
2341
2342   return textRemoved;
2343 }
2344
2345 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2346 {
2347   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
2348
2349   if( NULL != mImpl->mEventData )
2350   {
2351     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
2352
2353     if( 1u == tapCount )
2354     {
2355       // This is to avoid unnecessary relayouts when tapping an empty text-field
2356       bool relayoutNeeded( false );
2357
2358       if( ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
2359           ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
2360       {
2361         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );  // If Popup shown hide it here so can be shown again if required.
2362       }
2363
2364       if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != mImpl->mEventData->mState ) )
2365       {
2366         // Already in an active state so show a popup
2367         if( !mImpl->IsClipboardEmpty() )
2368         {
2369           // Shows Paste popup but could show full popup with Selection options. ( EDITING_WITH_POPUP )
2370           mImpl->ChangeState( EventData::EDITING_WITH_PASTE_POPUP );
2371         }
2372         else
2373         {
2374           mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2375         }
2376         relayoutNeeded = true;
2377       }
2378       else
2379       {
2380         if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
2381         {
2382           // Hide placeholder text
2383           ResetText();
2384         }
2385
2386         if( EventData::INACTIVE == mImpl->mEventData->mState )
2387         {
2388           mImpl->ChangeState( EventData::EDITING );
2389         }
2390         else if( !mImpl->IsClipboardEmpty() )
2391         {
2392           mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
2393         }
2394         relayoutNeeded = true;
2395       }
2396
2397       // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
2398       if( relayoutNeeded )
2399       {
2400         Event event( Event::TAP_EVENT );
2401         event.p1.mUint = tapCount;
2402         event.p2.mFloat = x;
2403         event.p3.mFloat = y;
2404         mImpl->mEventData->mEventQueue.push_back( event );
2405
2406         mImpl->RequestRelayout();
2407       }
2408     }
2409     else if( 2u == tapCount )
2410     {
2411       if( mImpl->mEventData->mSelectionEnabled &&
2412           mImpl->IsShowingRealText() )
2413       {
2414         SelectEvent( x, y, false );
2415       }
2416     }
2417   }
2418
2419   // Reset keyboard as tap event has occurred.
2420   mImpl->ResetImfManager();
2421 }
2422
2423 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2424         // Show cursor and grabhandle on first tap, this matches the behaviour of tapping when already editing
2425 {
2426   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
2427
2428   if( NULL != mImpl->mEventData )
2429   {
2430     Event event( Event::PAN_EVENT );
2431     event.p1.mInt = state;
2432     event.p2.mFloat = displacement.x;
2433     event.p3.mFloat = displacement.y;
2434     mImpl->mEventData->mEventQueue.push_back( event );
2435
2436     mImpl->RequestRelayout();
2437   }
2438 }
2439
2440 void Controller::LongPressEvent( Gesture::State state, float x, float y  )
2441 {
2442   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
2443
2444   if( ( state == Gesture::Started ) &&
2445       ( NULL != mImpl->mEventData ) )
2446   {
2447     if( !mImpl->IsShowingRealText() )
2448     {
2449       Event event( Event::LONG_PRESS_EVENT );
2450       event.p1.mInt = state;
2451       mImpl->mEventData->mEventQueue.push_back( event );
2452       mImpl->RequestRelayout();
2453     }
2454     else
2455     {
2456       // The 1st long-press on inactive text-field is treated as tap
2457       if( EventData::INACTIVE == mImpl->mEventData->mState )
2458       {
2459         mImpl->ChangeState( EventData::EDITING );
2460
2461         Event event( Event::TAP_EVENT );
2462         event.p1.mUint = 1;
2463         event.p2.mFloat = x;
2464         event.p3.mFloat = y;
2465         mImpl->mEventData->mEventQueue.push_back( event );
2466
2467         mImpl->RequestRelayout();
2468       }
2469       else
2470       {
2471         // Reset the imf manger to commit the pre-edit before selecting the text.
2472         mImpl->ResetImfManager();
2473
2474         SelectEvent( x, y, false );
2475       }
2476     }
2477   }
2478 }
2479
2480 void Controller::SelectEvent( float x, float y, bool selectAll )
2481 {
2482   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
2483
2484   if( NULL != mImpl->mEventData )
2485   {
2486     mImpl->ChangeState( EventData::SELECTING );
2487
2488     if( selectAll )
2489     {
2490       Event event( Event::SELECT_ALL );
2491       mImpl->mEventData->mEventQueue.push_back( event );
2492     }
2493     else
2494     {
2495       Event event( Event::SELECT );
2496       event.p2.mFloat = x;
2497       event.p3.mFloat = y;
2498       mImpl->mEventData->mEventQueue.push_back( event );
2499     }
2500
2501     mImpl->RequestRelayout();
2502   }
2503 }
2504
2505 void Controller::GetTargetSize( Vector2& targetSize )
2506 {
2507   targetSize = mImpl->mVisualModel->mControlSize;
2508 }
2509
2510 void Controller::AddDecoration( Actor& actor, bool needsClipping )
2511 {
2512   mImpl->mControlInterface.AddDecoration( actor, needsClipping );
2513 }
2514
2515 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
2516 {
2517   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
2518
2519   if( NULL != mImpl->mEventData )
2520   {
2521     switch( handleType )
2522     {
2523       case GRAB_HANDLE:
2524       {
2525         Event event( Event::GRAB_HANDLE_EVENT );
2526         event.p1.mUint  = state;
2527         event.p2.mFloat = x;
2528         event.p3.mFloat = y;
2529
2530         mImpl->mEventData->mEventQueue.push_back( event );
2531         break;
2532       }
2533       case LEFT_SELECTION_HANDLE:
2534       {
2535         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
2536         event.p1.mUint  = state;
2537         event.p2.mFloat = x;
2538         event.p3.mFloat = y;
2539
2540         mImpl->mEventData->mEventQueue.push_back( event );
2541         break;
2542       }
2543       case RIGHT_SELECTION_HANDLE:
2544       {
2545         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
2546         event.p1.mUint  = state;
2547         event.p2.mFloat = x;
2548         event.p3.mFloat = y;
2549
2550         mImpl->mEventData->mEventQueue.push_back( event );
2551         break;
2552       }
2553       case LEFT_SELECTION_HANDLE_MARKER:
2554       case RIGHT_SELECTION_HANDLE_MARKER:
2555       {
2556         // Markers do not move the handles.
2557         break;
2558       }
2559       case HANDLE_TYPE_COUNT:
2560       {
2561         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
2562       }
2563     }
2564
2565     mImpl->RequestRelayout();
2566   }
2567 }
2568
2569 void Controller::PasteText( const std::string& stringToPaste )
2570 {
2571   InsertText( stringToPaste, Text::Controller::COMMIT );
2572   mImpl->ChangeState( EventData::EDITING );
2573   mImpl->RequestRelayout();
2574
2575   // Do this last since it provides callbacks into application code
2576   mImpl->mControlInterface.TextChanged();
2577 }
2578
2579 void Controller::PasteClipboardItemEvent()
2580 {
2581   // Retrieve the clipboard contents first
2582   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
2583   std::string stringToPaste( notifier.GetContent() );
2584
2585   // Commit the current pre-edit text; the contents of the clipboard should be appended
2586   mImpl->ResetImfManager();
2587
2588   // Temporary disable hiding clipboard
2589   mImpl->SetClipboardHideEnable( false );
2590
2591   // Paste
2592   PasteText( stringToPaste );
2593
2594   mImpl->SetClipboardHideEnable( true );
2595 }
2596
2597 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
2598 {
2599   if( NULL == mImpl->mEventData )
2600   {
2601     return;
2602   }
2603
2604   switch( button )
2605   {
2606     case Toolkit::TextSelectionPopup::CUT:
2607     {
2608       mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
2609       mImpl->mOperationsPending = ALL_OPERATIONS;
2610
2611       // This is to reset the virtual keyboard to Upper-case
2612       if( 0u == mImpl->mLogicalModel->mText.Count() )
2613       {
2614         NotifyImfManager();
2615       }
2616
2617       if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2618           !mImpl->IsPlaceholderAvailable() )
2619       {
2620         mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2621       }
2622       else
2623       {
2624         ShowPlaceholderText();
2625       }
2626
2627       mImpl->mEventData->mUpdateCursorPosition = true;
2628       mImpl->mEventData->mScrollAfterDelete = true;
2629
2630       mImpl->RequestRelayout();
2631       mImpl->mControlInterface.TextChanged();
2632       break;
2633     }
2634     case Toolkit::TextSelectionPopup::COPY:
2635     {
2636       mImpl->SendSelectionToClipboard( false ); // Text not modified
2637       mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
2638       break;
2639     }
2640     case Toolkit::TextSelectionPopup::PASTE:
2641     {
2642       std::string stringToPaste("");
2643       mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
2644       PasteText( stringToPaste );
2645       break;
2646     }
2647     case Toolkit::TextSelectionPopup::SELECT:
2648     {
2649       const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2650
2651       if( mImpl->mEventData->mSelectionEnabled )
2652       {
2653         // Creates a SELECT event.
2654         SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
2655       }
2656       break;
2657     }
2658     case Toolkit::TextSelectionPopup::SELECT_ALL:
2659     {
2660       // Creates a SELECT_ALL event
2661       SelectEvent( 0.f, 0.f, true );
2662       break;
2663     }
2664     case Toolkit::TextSelectionPopup::CLIPBOARD:
2665     {
2666       mImpl->ShowClipboard();
2667       break;
2668     }
2669     case Toolkit::TextSelectionPopup::NONE:
2670     {
2671       // Nothing to do.
2672       break;
2673     }
2674   }
2675 }
2676
2677 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2678 {
2679   bool update = false;
2680   bool requestRelayout = false;
2681
2682   std::string text;
2683   unsigned int cursorPosition = 0u;
2684
2685   switch( imfEvent.eventName )
2686   {
2687     case ImfManager::COMMIT:
2688     {
2689       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
2690       update = true;
2691       requestRelayout = true;
2692       break;
2693     }
2694     case ImfManager::PREEDIT:
2695     {
2696       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
2697       update = true;
2698       requestRelayout = true;
2699       break;
2700     }
2701     case ImfManager::DELETESURROUNDING:
2702     {
2703       update = RemoveText( imfEvent.cursorOffset,
2704                            imfEvent.numberOfChars,
2705                            DONT_UPDATE_INPUT_STYLE );
2706
2707       if( update )
2708       {
2709         if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2710             !mImpl->IsPlaceholderAvailable() )
2711         {
2712           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2713         }
2714         else
2715         {
2716           ShowPlaceholderText();
2717         }
2718         mImpl->mEventData->mUpdateCursorPosition = true;
2719         mImpl->mEventData->mScrollAfterDelete = true;
2720       }
2721       requestRelayout = true;
2722       break;
2723     }
2724     case ImfManager::GETSURROUNDING:
2725     {
2726       GetText( text );
2727       cursorPosition = GetLogicalCursorPosition();
2728
2729       imfManager.SetSurroundingText( text );
2730       imfManager.SetCursorPosition( cursorPosition );
2731       break;
2732     }
2733     case ImfManager::VOID:
2734     {
2735       // do nothing
2736       break;
2737     }
2738   } // end switch
2739
2740   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
2741   {
2742     GetText( text );
2743     cursorPosition = GetLogicalCursorPosition();
2744   }
2745
2746   if( requestRelayout )
2747   {
2748     mImpl->mOperationsPending = ALL_OPERATIONS;
2749     mImpl->RequestRelayout();
2750
2751     // Do this last since it provides callbacks into application code
2752     mImpl->mControlInterface.TextChanged();
2753   }
2754
2755   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
2756
2757   return callbackData;
2758 }
2759
2760 Controller::~Controller()
2761 {
2762   delete mImpl;
2763 }
2764
2765 bool Controller::BackspaceKeyEvent()
2766 {
2767   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
2768
2769   bool removed = false;
2770
2771   if( NULL == mImpl->mEventData )
2772   {
2773     return removed;
2774   }
2775
2776   // IMF manager is no longer handling key-events
2777   mImpl->ClearPreEditFlag();
2778
2779   if( EventData::SELECTING == mImpl->mEventData->mState )
2780   {
2781     removed = RemoveSelectedText();
2782   }
2783   else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
2784   {
2785     // Remove the character before the current cursor position
2786     removed = RemoveText( -1,
2787                           1,
2788                           UPDATE_INPUT_STYLE );
2789   }
2790
2791   if( removed )
2792   {
2793     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE RemovedText\n", this );
2794     // Notifiy the IMF manager after text changed
2795     // Automatic  Upper-case and restarting prediction on an existing word require this.
2796     NotifyImfManager();
2797
2798     if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
2799         !mImpl->IsPlaceholderAvailable() )
2800     {
2801       mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2802     }
2803     else
2804     {
2805       ShowPlaceholderText();
2806     }
2807     mImpl->mEventData->mUpdateCursorPosition = true;
2808     mImpl->mEventData->mScrollAfterDelete = true;
2809   }
2810
2811   return removed;
2812 }
2813
2814 void Controller::NotifyImfManager()
2815 {
2816   if( NULL != mImpl->mEventData )
2817   {
2818     if( mImpl->mEventData->mImfManager )
2819     {
2820       // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2821       std::string text;
2822       GetText( text );
2823       mImpl->mEventData->mImfManager.SetSurroundingText( text );
2824
2825       mImpl->mEventData->mImfManager.SetCursorPosition( GetLogicalCursorPosition() );
2826       mImpl->mEventData->mImfManager.NotifyCursorPosition();
2827     }
2828   }
2829 }
2830
2831 void Controller::ShowPlaceholderText()
2832 {
2833   if( mImpl->IsPlaceholderAvailable() )
2834   {
2835     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
2836
2837     if( NULL == mImpl->mEventData )
2838     {
2839       return;
2840     }
2841
2842     mImpl->mEventData->mIsShowingPlaceholderText = true;
2843
2844     // Disable handles when showing place-holder text
2845     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2846     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2847     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2848
2849     const char* text( NULL );
2850     size_t size( 0 );
2851
2852     // TODO - Switch placeholder text styles when changing state
2853     if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
2854         ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
2855     {
2856       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
2857       size = mImpl->mEventData->mPlaceholderTextActive.size();
2858     }
2859     else
2860     {
2861       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
2862       size = mImpl->mEventData->mPlaceholderTextInactive.size();
2863     }
2864
2865     mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2866     mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2867
2868     // Reset model for showing placeholder.
2869     mImpl->mLogicalModel->mText.Clear();
2870     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
2871
2872     // Convert text into UTF-32
2873     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
2874     utf32Characters.Resize( size );
2875
2876     // This is a bit horrible but std::string returns a (signed) char*
2877     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
2878
2879     // Transform a text array encoded in utf8 into an array encoded in utf32.
2880     // It returns the actual number of characters.
2881     const Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
2882     utf32Characters.Resize( characterCount );
2883
2884     // The characters to be added.
2885     mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = characterCount;
2886
2887     // Reset the cursor position
2888     mImpl->mEventData->mPrimaryCursorPosition = 0;
2889
2890     // The natural size needs to be re-calculated.
2891     mImpl->mRecalculateNaturalSize = true;
2892
2893     // Apply modifications to the model
2894     mImpl->mOperationsPending = ALL_OPERATIONS;
2895
2896     // Update the rest of the model during size negotiation
2897     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
2898   }
2899 }
2900
2901 void Controller::ClearFontData()
2902 {
2903   if( mImpl->mFontDefaults )
2904   {
2905     mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
2906   }
2907
2908   // Set flags to update the model.
2909   mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2910   mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2911   mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mLogicalModel->mText.Count();
2912
2913   mImpl->mTextUpdateInfo.mClearAll = true;
2914   mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
2915   mImpl->mRecalculateNaturalSize = true;
2916
2917   mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2918                                                            VALIDATE_FONTS            |
2919                                                            SHAPE_TEXT                |
2920                                                            GET_GLYPH_METRICS         |
2921                                                            LAYOUT                    |
2922                                                            UPDATE_ACTUAL_SIZE        |
2923                                                            REORDER                   |
2924                                                            ALIGN );
2925 }
2926
2927 void Controller::ClearStyleData()
2928 {
2929   mImpl->mLogicalModel->mColorRuns.Clear();
2930   mImpl->mLogicalModel->ClearFontDescriptionRuns();
2931 }
2932
2933 Controller::Controller( ControlInterface& controlInterface )
2934 : mImpl( NULL )
2935 {
2936   mImpl = new Controller::Impl( controlInterface );
2937 }
2938
2939 } // namespace Text
2940
2941 } // namespace Toolkit
2942
2943 } // namespace Dali