Distinguish the cursor height and line height.
[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 <vector>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/logical-model-impl.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/script-run.h>
35 #include <dali-toolkit/internal/text/segmentation.h>
36 #include <dali-toolkit/internal/text/shaper.h>
37 #include <dali-toolkit/internal/text/text-io.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39 #include <dali-toolkit/internal/text/visual-model-impl.h>
40
41 using std::vector;
42
43 namespace
44 {
45
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 const std::string EMPTY_STRING;
48
49 enum ModifyType
50 {
51   REPLACE_TEXT, ///< Replace the entire text
52   INSERT_TEXT,  ///< Insert characters at the current cursor position
53   DELETE_TEXT   ///< Delete a character at the current cursor position
54 };
55
56 struct ModifyEvent
57 {
58   ModifyType type;
59   std::string text;
60 };
61
62 } // namespace
63
64 namespace Dali
65 {
66
67 namespace Toolkit
68 {
69
70 namespace Text
71 {
72
73 struct Controller::TextInput
74 {
75   // Used to queue input events until DoRelayout()
76   enum EventType
77   {
78     KEYBOARD_FOCUS_GAIN_EVENT,
79     KEYBOARD_FOCUS_LOST_EVENT,
80     CURSOR_KEY_EVENT,
81     TAP_EVENT,
82     PAN_EVENT,
83     GRAB_HANDLE_EVENT
84   };
85
86   union Param
87   {
88     int mInt;
89     unsigned int mUint;
90     float mFloat;
91   };
92
93   struct Event
94   {
95     Event( EventType eventType )
96     : type( eventType )
97     {
98       p1.mInt = 0;
99       p2.mInt = 0;
100     }
101
102     EventType type;
103     Param p1;
104     Param p2;
105     Param p3;
106   };
107
108   enum State
109   {
110     INACTIVE,
111     SELECTING,
112     EDITING,
113     EDITING_WITH_POPUP
114   };
115
116   TextInput( LogicalModelPtr logicalModel,
117              VisualModelPtr visualModel,
118              DecoratorPtr decorator )
119   : mLogicalModel( logicalModel ),
120     mVisualModel( visualModel ),
121     mDecorator( decorator ),
122     mState( INACTIVE ),
123     mPrimaryCursorPosition( 0u ),
124     mSecondaryCursorPosition( 0u ),
125     mDecoratorUpdated( false ),
126     mCursorBlinkEnabled( true ),
127     mGrabHandleEnabled( true ),
128     mGrabHandlePopupEnabled( true ),
129     mSelectionEnabled( true ),
130     mHorizontalScrollingEnabled( true ),
131     mVerticalScrollingEnabled( false ),
132     mUpdateCursorPosition( false )
133   {
134   }
135
136   /**
137    * @brief Helper to move the cursor, grab handle etc.
138    */
139   bool ProcessInputEvents( const Vector2& controlSize,
140                            const Vector2& alignmentOffset )
141   {
142     mDecoratorUpdated = false;
143
144     if( mDecorator )
145     {
146       for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
147       {
148         switch( iter->type )
149         {
150           case KEYBOARD_FOCUS_GAIN_EVENT:
151           {
152             OnKeyboardFocus( true );
153             break;
154           }
155           case KEYBOARD_FOCUS_LOST_EVENT:
156           {
157             OnKeyboardFocus( false );
158             break;
159           }
160           case CURSOR_KEY_EVENT:
161           {
162             OnCursorKeyEvent( *iter );
163             break;
164           }
165           case TAP_EVENT:
166           {
167             OnTapEvent( *iter, alignmentOffset );
168             break;
169           }
170           case PAN_EVENT:
171           {
172             OnPanEvent( *iter, controlSize, alignmentOffset );
173             break;
174           }
175           case GRAB_HANDLE_EVENT:
176           {
177             OnGrabHandleEvent( *iter );
178             break;
179           }
180         }
181       }
182     }
183
184     // The cursor must also be repositioned after inserts into the model
185     if( mUpdateCursorPosition )
186     {
187       UpdateCursorPosition();
188       mUpdateCursorPosition = false;
189     }
190
191     mEventQueue.clear();
192
193     return mDecoratorUpdated;
194   }
195
196   void OnKeyboardFocus( bool hasFocus )
197   {
198     if( !hasFocus )
199     {
200       ChangeState( INACTIVE );
201     }
202     else
203     {
204       ChangeState( EDITING );
205     }
206   }
207
208   void OnCursorKeyEvent( const Event& event )
209   {
210     int keyCode = event.p1.mInt;
211
212     if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
213     {
214       // TODO
215     }
216     else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
217     {
218       // TODO
219     }
220     else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
221     {
222       // TODO
223     }
224     else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
225     {
226       // TODO
227     }
228   }
229
230   void HandleCursorKey( int keyCode )
231   {
232     // TODO
233   }
234
235   void OnTapEvent( const Event& event,
236                    const Vector2& alignmentOffset  )
237   {
238     unsigned int tapCount = event.p1.mUint;
239
240     if( 1u == tapCount )
241     {
242       ChangeState( EDITING );
243
244       float xPosition = event.p2.mFloat - alignmentOffset.x;
245       float yPosition = event.p3.mFloat - alignmentOffset.y;
246       float height(0.0f);
247       GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
248       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
249       mUpdateCursorPosition = false;
250
251       mDecoratorUpdated = true;
252     }
253     else if( mSelectionEnabled &&
254              2u == tapCount )
255     {
256       ChangeState( SELECTING );
257
258       RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
259     }
260   }
261
262   void OnPanEvent( const Event& event,
263                    const Vector2& controlSize,
264                    const Vector2& alignmentOffset )
265   {
266     int state = event.p1.mInt;
267
268     if( Gesture::Started    == state ||
269         Gesture::Continuing == state )
270     {
271       const Vector2& actualSize = mVisualModel->GetActualSize();
272
273       if( mHorizontalScrollingEnabled )
274       {
275         const float displacementX = event.p2.mFloat;
276         mScrollPosition.x += displacementX;
277
278         // Clamp between -space & 0 (and the text alignment).
279         const float contentWidth = actualSize.width;
280         if( contentWidth > controlSize.width )
281         {
282           const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
283           mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
284           mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
285
286           mDecoratorUpdated = true;
287         }
288         else
289         {
290           mScrollPosition.x = 0.f;
291         }
292       }
293
294       if( mVerticalScrollingEnabled )
295       {
296         const float displacementY = event.p3.mFloat;
297         mScrollPosition.y += displacementY;
298
299         // Clamp between -space & 0 (and the text alignment).
300         if( actualSize.height > controlSize.height )
301         {
302           const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
303           mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
304           mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
305
306           mDecoratorUpdated = true;
307         }
308         else
309         {
310           mScrollPosition.y = 0.f;
311         }
312       }
313     }
314   }
315
316   void OnGrabHandleEvent( const Event& event )
317   {
318     unsigned int state = event.p1.mUint;
319
320     if( GRAB_HANDLE_PRESSED == state )
321     {
322       float xPosition = event.p2.mFloat;
323       float yPosition = event.p3.mFloat;
324       float height(0.0f);
325
326       GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
327
328       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
329
330       //mDecorator->HidePopup();
331       ChangeState ( EDITING );
332       mDecoratorUpdated = true;
333     }
334     else if ( mGrabHandlePopupEnabled &&
335               GRAB_HANDLE_RELEASED == state )
336     {
337       //mDecorator->ShowPopup();
338       ChangeState ( EDITING_WITH_POPUP );
339       mDecoratorUpdated = true;
340     }
341   }
342
343   void RepositionSelectionHandles( float visualX, float visualY )
344   {
345     // TODO - Find which word was selected
346
347     const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
348     const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
349
350     const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
351     const Vector<Vector2>::SizeType positionCount = positions.Count();
352
353     // Guard against glyphs which did not fit inside the layout
354     const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
355
356     if( count )
357     {
358       float primaryX   = positions[0].x;
359       float secondaryX = positions[count-1].x + glyphs[count-1].width;
360
361       // TODO - multi-line selection
362       const Vector<LineRun>& lines = mVisualModel->mLines;
363       float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
364
365       mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE,   primaryX,   0.0f, height );
366       mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
367
368       mDecorator->ClearHighlights();
369       mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
370     }
371   }
372
373   void ChangeState( State newState )
374   {
375     if( mState != newState )
376     {
377       mState = newState;
378
379       if( INACTIVE == mState )
380       {
381         mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
382         mDecorator->StopCursorBlink();
383         mDecorator->SetGrabHandleActive( false );
384         mDecorator->SetSelectionActive( false );
385         mDecorator->SetPopupActive( false );
386         mDecoratorUpdated = true;
387       }
388       else if ( SELECTING == mState )
389       {
390         mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
391         mDecorator->StopCursorBlink();
392         mDecorator->SetGrabHandleActive( false );
393         mDecorator->SetSelectionActive( true );
394         mDecoratorUpdated = true;
395       }
396       else if( EDITING == mState )
397       {
398         mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
399         if( mCursorBlinkEnabled )
400         {
401           mDecorator->StartCursorBlink();
402         }
403         if( mGrabHandleEnabled )
404         {
405           mDecorator->SetGrabHandleActive( true );
406         }
407         if( mGrabHandlePopupEnabled )
408         {
409           mDecorator->SetPopupActive( false );
410         }
411         mDecorator->SetSelectionActive( false );
412         mDecoratorUpdated = true;
413       }
414       else if( EDITING_WITH_POPUP == mState )
415       {
416         mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
417         if( mCursorBlinkEnabled )
418         {
419           mDecorator->StartCursorBlink();
420         }
421         if( mGrabHandleEnabled )
422         {
423           mDecorator->SetGrabHandleActive( true );
424         }
425         if( mGrabHandlePopupEnabled )
426         {
427           mDecorator->SetPopupActive( true );
428         }
429         mDecorator->SetSelectionActive( false );
430         mDecoratorUpdated = true;
431       }
432     }
433   }
434
435   LineIndex GetClosestLine( float y )
436   {
437     LineIndex lineIndex( 0u );
438
439     const Vector<LineRun>& lines = mVisualModel->mLines;
440     for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
441     {
442       const LineRun& lineRun = lines[lineIndex];
443       totalHeight += lineRun.ascender + -lineRun.descender;
444       if( y < totalHeight )
445       {
446         return lineIndex;
447       }
448     }
449
450     return lineIndex-1;
451   }
452
453   void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
454   {
455     Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
456     Length numberOfLines  = mVisualModel->mLines.Count();
457     if( 0 == numberOfGlyphs ||
458         0 == numberOfLines )
459     {
460       return;
461     }
462
463     // Transform to visual model coords
464     visualX -= mScrollPosition.x;
465     visualY -= mScrollPosition.y;
466
467     // Find which line is closest
468     LineIndex lineIndex( GetClosestLine( visualY ) );
469
470     const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
471     const GlyphInfo* const glyphsBuffer = glyphs.Begin();
472
473     const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
474     const Vector2* const positionsBuffer = positions.Begin();
475
476     unsigned int closestGlyph = 0;
477     bool leftOfGlyph( false ); // which side of the glyph?
478     float closestDistance = MAX_FLOAT;
479
480     const LineRun& line = mVisualModel->mLines[lineIndex];
481     GlyphIndex startGlyph = line.glyphIndex;
482     GlyphIndex endGlyph   = line.glyphIndex + line.numberOfGlyphs;
483     DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
484
485     for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
486     {
487       const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
488       const Vector2& position = *( positionsBuffer + i );
489       float glyphX = position.x + glyphInfo.width*0.5f;
490       float glyphY = position.y + glyphInfo.height*0.5f;
491
492       float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
493
494       if( distanceToGlyph < closestDistance )
495       {
496         closestDistance = distanceToGlyph;
497         closestGlyph = i;
498         leftOfGlyph = ( visualX < glyphX );
499       }
500     }
501
502     // Calculate the logical position
503     logical = mVisualModel->GetCharacterIndex( closestGlyph );
504
505     // Returns the visual position of the glyph
506     visualX = positions[closestGlyph].x;
507     if( !leftOfGlyph )
508     {
509       visualX += glyphs[closestGlyph].width;
510
511       //if( LTR ) TODO
512         ++logical;
513     }
514     else// if ( RTL ) TODO
515     {
516       //++logical;
517     }
518     visualY = 0.0f;
519
520     height = line.ascender + -line.descender;
521   }
522
523   void UpdateCursorPosition()
524   {
525     if( 0 == mVisualModel->mGlyphs.Count() )
526     {
527       return;
528     }
529
530     // FIXME GetGlyphIndex() is behaving strangely
531 #if 0
532     GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
533 #else
534     GlyphIndex cursorGlyph( 0u );
535     for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
536     {
537       if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
538       {
539         break;
540       }
541     }
542 #endif
543
544     float visualX( 0.0f );
545     float visualY( 0.0f );
546     float height( 0.0f );
547     const Vector<LineRun>& lineRuns = mVisualModel->mLines;
548
549     if( cursorGlyph > 0 )
550     {
551       --cursorGlyph;
552
553       visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
554       //if( LTR ) TODO
555         visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
556
557       // Find the line height
558       GlyphIndex lastGlyph( 0 );
559       for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
560       {
561         lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
562         if( cursorGlyph < lastGlyph )
563         {
564           const LineRun& lineRun = lineRuns[lineIndex];
565           height = lineRun.ascender + -lineRun.descender;
566           break;
567         }
568       }
569     }
570
571     mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height, height ); // TODO: To be fixed in the next patch.
572     mDecoratorUpdated = true;
573   }
574
575   LogicalModelPtr mLogicalModel;
576   VisualModelPtr  mVisualModel;
577   DecoratorPtr    mDecorator;
578
579   std::string mPlaceholderText;
580
581   /**
582    * This is used to delay handling events until after the model has been updated.
583    * The number of updates to the model is minimized to improve performance.
584    */
585   vector<Event> mEventQueue; ///< The queue of touch events etc.
586
587   State mState; ///< Selection mode, edit mode etc.
588
589   CharacterIndex mPrimaryCursorPosition;   ///< Index into logical model for primary cursor
590   CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
591
592   /**
593    * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
594    * Typically this will have a negative value with scrolling occurs.
595    */
596   Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
597
598   bool mDecoratorUpdated           : 1; ///< True if the decorator was updated during event processing
599   bool mCursorBlinkEnabled         : 1; ///< True if cursor should blink when active
600   bool mGrabHandleEnabled          : 1; ///< True if grab handle is enabled
601   bool mGrabHandlePopupEnabled     : 1; ///< True if the grab handle popu-up should be shown
602   bool mSelectionEnabled           : 1; ///< True if selection handles, highlight etc. are enabled
603   bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
604   bool mVerticalScrollingEnabled   : 1; ///< True if vertical scrolling is enabled
605   bool mUpdateCursorPosition       : 1; ///< True if the visual position of the cursor must be recalculated
606 };
607
608 struct Controller::FontDefaults
609 {
610   FontDefaults()
611   : mDefaultPointSize(0.0f),
612     mFontId(0u)
613   {
614   }
615
616   FontId GetFontId( TextAbstraction::FontClient& fontClient )
617   {
618     if( !mFontId )
619     {
620       Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
621       mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
622     }
623
624     return mFontId;
625   }
626
627   std::string mDefaultFontFamily;
628   std::string mDefaultFontStyle;
629   float mDefaultPointSize;
630   FontId mFontId;
631 };
632
633 struct Controller::Impl
634 {
635   Impl( ControlInterface& controlInterface )
636   : mControlInterface( controlInterface ),
637     mLogicalModel(),
638     mVisualModel(),
639     mFontDefaults( NULL ),
640     mTextInput( NULL ),
641     mFontClient(),
642     mView(),
643     mLayoutEngine(),
644     mModifyEvents(),
645     mControlSize(),
646     mAlignmentOffset(),
647     mOperationsPending( NO_OPERATION ),
648     mRecalculateNaturalSize( true )
649   {
650     mLogicalModel = LogicalModel::New();
651     mVisualModel  = VisualModel::New();
652
653     mFontClient = TextAbstraction::FontClient::Get();
654
655     mView.SetVisualModel( mVisualModel );
656
657     // Set the text properties to default
658     mVisualModel->SetTextColor( Color::WHITE );
659     mVisualModel->SetShadowOffset( Vector2::ZERO );
660     mVisualModel->SetShadowColor( Vector4::ZERO );
661     mVisualModel->SetUnderlineEnabled( false );
662   }
663
664   ~Impl()
665   {
666     delete mTextInput;
667   }
668
669   ControlInterface& mControlInterface;     ///< Reference to the text controller.
670   LogicalModelPtr mLogicalModel;           ///< Pointer to the logical model.
671   VisualModelPtr  mVisualModel;            ///< Pointer to the visual model.
672   FontDefaults* mFontDefaults;             ///< Avoid allocating this when the user does not specify a font.
673   Controller::TextInput* mTextInput;       ///< Avoid allocating everything for text input until EnableTextInput().
674   TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
675   View mView;                              ///< The view interface to the rendering back-end.
676   LayoutEngine mLayoutEngine;              ///< The layout engine.
677   std::vector<ModifyEvent> mModifyEvents;  ///< Temporary stores the text set until the next relayout.
678   Size mControlSize;                       ///< The size of the control.
679   Vector2 mAlignmentOffset;                ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
680   OperationsMask mOperationsPending;       ///< Operations pending to be done to layout the text.
681   bool mRecalculateNaturalSize:1;          ///< Whether the natural size needs to be recalculated.
682 };
683
684 ControllerPtr Controller::New( ControlInterface& controlInterface )
685 {
686   return ControllerPtr( new Controller( controlInterface ) );
687 }
688
689 void Controller::SetText( const std::string& text )
690 {
691   // Cancel previously queued inserts etc.
692   mImpl->mModifyEvents.clear();
693
694   // Keep until size negotiation
695   ModifyEvent event;
696   event.type = REPLACE_TEXT;
697   event.text = text;
698   mImpl->mModifyEvents.push_back( event );
699
700   if( mImpl->mTextInput )
701   {
702     // Cancel previously queued events
703     mImpl->mTextInput->mEventQueue.clear();
704
705     // TODO - Hide selection decorations
706   }
707 }
708
709 void Controller::GetText( std::string& text ) const
710 {
711   if( !mImpl->mModifyEvents.empty() &&
712        REPLACE_TEXT == mImpl->mModifyEvents[0].type )
713   {
714     text = mImpl->mModifyEvents[0].text;
715   }
716   else
717   {
718     // TODO - Convert from UTF-32
719   }
720 }
721
722 void Controller::SetPlaceholderText( const std::string& text )
723 {
724   if( !mImpl->mTextInput )
725   {
726     mImpl->mTextInput->mPlaceholderText = text;
727   }
728 }
729
730 void Controller::GetPlaceholderText( std::string& text ) const
731 {
732   if( !mImpl->mTextInput )
733   {
734     text = mImpl->mTextInput->mPlaceholderText;
735   }
736 }
737
738 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
739 {
740   if( !mImpl->mFontDefaults )
741   {
742     mImpl->mFontDefaults = new Controller::FontDefaults();
743   }
744
745   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
746   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
747   mImpl->mOperationsPending = ALL_OPERATIONS;
748   mImpl->mRecalculateNaturalSize = true;
749
750   // Clear the font-specific data
751   mImpl->mLogicalModel->mFontRuns.Clear();
752   mImpl->mVisualModel->mGlyphs.Clear();
753   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
754   mImpl->mVisualModel->mCharactersToGlyph.Clear();
755   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
756   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
757   mImpl->mVisualModel->mGlyphPositions.Clear();
758   mImpl->mVisualModel->mLines.Clear();
759
760   RequestRelayout();
761 }
762
763 const std::string& Controller::GetDefaultFontFamily() const
764 {
765   if( mImpl->mFontDefaults )
766   {
767     return mImpl->mFontDefaults->mDefaultFontFamily;
768   }
769
770   return EMPTY_STRING;
771 }
772
773 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
774 {
775   if( !mImpl->mFontDefaults )
776   {
777     mImpl->mFontDefaults = new Controller::FontDefaults();
778   }
779
780   mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
781   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
782   mImpl->mOperationsPending = ALL_OPERATIONS;
783   mImpl->mRecalculateNaturalSize = true;
784
785   // Clear the font-specific data
786   mImpl->mLogicalModel->mFontRuns.Clear();
787   mImpl->mVisualModel->mGlyphs.Clear();
788   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
789   mImpl->mVisualModel->mCharactersToGlyph.Clear();
790   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
791   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
792   mImpl->mVisualModel->mGlyphPositions.Clear();
793   mImpl->mVisualModel->mLines.Clear();
794
795   RequestRelayout();
796 }
797
798 const std::string& Controller::GetDefaultFontStyle() const
799 {
800   if( mImpl->mFontDefaults )
801   {
802     return mImpl->mFontDefaults->mDefaultFontStyle;
803   }
804
805   return EMPTY_STRING;
806 }
807
808 void Controller::SetDefaultPointSize( float pointSize )
809 {
810   if( !mImpl->mFontDefaults )
811   {
812     mImpl->mFontDefaults = new Controller::FontDefaults();
813   }
814
815   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
816   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
817   mImpl->mOperationsPending = ALL_OPERATIONS;
818   mImpl->mRecalculateNaturalSize = true;
819
820   // Clear the font-specific data
821   mImpl->mLogicalModel->mFontRuns.Clear();
822   mImpl->mVisualModel->mGlyphs.Clear();
823   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
824   mImpl->mVisualModel->mCharactersToGlyph.Clear();
825   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
826   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
827   mImpl->mVisualModel->mGlyphPositions.Clear();
828   mImpl->mVisualModel->mLines.Clear();
829
830   RequestRelayout();
831 }
832
833 float Controller::GetDefaultPointSize() const
834 {
835   if( mImpl->mFontDefaults )
836   {
837     return mImpl->mFontDefaults->mDefaultPointSize;
838   }
839
840   return 0.0f;
841 }
842
843 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
844 {
845   if( mImpl->mFontDefaults )
846   {
847     FontRun fontRun;
848     fontRun.characterRun.characterIndex = 0;
849     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
850     fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
851     fontRun.isDefault = true;
852
853     fonts.PushBack( fontRun );
854   }
855 }
856
857 const Vector4& Controller::GetTextColor() const
858 {
859   return mImpl->mVisualModel->GetTextColor();
860 }
861
862 const Vector2& Controller::GetShadowOffset() const
863 {
864   return mImpl->mVisualModel->GetShadowOffset();
865 }
866
867 const Vector4& Controller::GetShadowColor() const
868 {
869   return mImpl->mVisualModel->GetShadowColor();
870 }
871
872 const Vector4& Controller::GetUnderlineColor() const
873 {
874   return mImpl->mVisualModel->GetUnderlineColor();
875 }
876
877 bool Controller::IsUnderlineEnabled() const
878 {
879   return mImpl->mVisualModel->IsUnderlineEnabled();
880 }
881
882 void Controller::SetTextColor( const Vector4& textColor )
883 {
884   mImpl->mVisualModel->SetTextColor( textColor );
885 }
886
887 void Controller::SetShadowOffset( const Vector2& shadowOffset )
888 {
889   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
890 }
891
892 void Controller::SetShadowColor( const Vector4& shadowColor )
893 {
894   mImpl->mVisualModel->SetShadowColor( shadowColor );
895 }
896
897 void Controller::SetUnderlineColor( const Vector4& color )
898 {
899   mImpl->mVisualModel->SetUnderlineColor( color );
900 }
901
902 void Controller::SetUnderlineEnabled( bool enabled )
903 {
904   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
905 }
906
907 void Controller::EnableTextInput( DecoratorPtr decorator )
908 {
909   if( !mImpl->mTextInput )
910   {
911     mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
912   }
913 }
914
915 void Controller::SetEnableCursorBlink( bool enable )
916 {
917   DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
918
919   if( mImpl->mTextInput )
920   {
921     mImpl->mTextInput->mCursorBlinkEnabled = enable;
922
923     if( !enable &&
924         mImpl->mTextInput->mDecorator )
925     {
926       mImpl->mTextInput->mDecorator->StopCursorBlink();
927     }
928   }
929 }
930
931 bool Controller::GetEnableCursorBlink() const
932 {
933   if( mImpl->mTextInput )
934   {
935     return mImpl->mTextInput->mCursorBlinkEnabled;
936   }
937
938   return false;
939 }
940
941 const Vector2& Controller::GetScrollPosition() const
942 {
943   if( mImpl->mTextInput )
944   {
945     return mImpl->mTextInput->mScrollPosition;
946   }
947
948   return Vector2::ZERO;
949 }
950
951 const Vector2& Controller::GetAlignmentOffset() const
952 {
953   return mImpl->mAlignmentOffset;
954 }
955
956 Vector3 Controller::GetNaturalSize()
957 {
958   Vector3 naturalSize;
959
960   // Make sure the model is up-to-date before layouting
961   ProcessModifyEvents();
962
963   if( mImpl->mRecalculateNaturalSize )
964   {
965     // Operations that can be done only once until the text changes.
966     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
967                                                                            GET_SCRIPTS       |
968                                                                            VALIDATE_FONTS    |
969                                                                            GET_LINE_BREAKS   |
970                                                                            GET_WORD_BREAKS   |
971                                                                            BIDI_INFO         |
972                                                                            SHAPE_TEXT        |
973                                                                            GET_GLYPH_METRICS );
974     // Make sure the model is up-to-date before layouting
975     UpdateModel( onlyOnceOperations );
976
977     // Operations that need to be done if the size changes.
978     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
979                                                                         ALIGN  |
980                                                                         REORDER );
981
982     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
983                 static_cast<OperationsMask>( onlyOnceOperations |
984                                              sizeOperations ),
985                 naturalSize.GetVectorXY() );
986
987     // Do not do again the only once operations.
988     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
989
990     // Do the size related operations again.
991     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
992
993     // Stores the natural size to avoid recalculate it again
994     // unless the text/style changes.
995     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
996
997     mImpl->mRecalculateNaturalSize = false;
998   }
999   else
1000   {
1001     naturalSize = mImpl->mVisualModel->GetNaturalSize();
1002   }
1003
1004   return naturalSize;
1005 }
1006
1007 float Controller::GetHeightForWidth( float width )
1008 {
1009   // Make sure the model is up-to-date before layouting
1010   ProcessModifyEvents();
1011
1012   Size layoutSize;
1013   if( width != mImpl->mControlSize.width )
1014   {
1015     // Operations that can be done only once until the text changes.
1016     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
1017                                                                            GET_SCRIPTS       |
1018                                                                            VALIDATE_FONTS    |
1019                                                                            GET_LINE_BREAKS   |
1020                                                                            GET_WORD_BREAKS   |
1021                                                                            BIDI_INFO         |
1022                                                                            SHAPE_TEXT        |
1023                                                                            GET_GLYPH_METRICS );
1024     // Make sure the model is up-to-date before layouting
1025     UpdateModel( onlyOnceOperations );
1026
1027     // Operations that need to be done if the size changes.
1028     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
1029                                                                         ALIGN  |
1030                                                                         REORDER );
1031
1032     DoRelayout( Size( width, MAX_FLOAT ),
1033                 static_cast<OperationsMask>( onlyOnceOperations |
1034                                              sizeOperations ),
1035                 layoutSize );
1036
1037     // Do not do again the only once operations.
1038     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1039
1040     // Do the size related operations again.
1041     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1042   }
1043   else
1044   {
1045     layoutSize = mImpl->mVisualModel->GetActualSize();
1046   }
1047
1048   return layoutSize.height;
1049 }
1050
1051 bool Controller::Relayout( const Size& size )
1052 {
1053   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1054   {
1055     bool glyphsRemoved( false );
1056     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1057     {
1058       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1059       glyphsRemoved = true;
1060     }
1061
1062     // Not worth to relayout if width or height is equal to zero.
1063     return glyphsRemoved;
1064   }
1065
1066   if( size != mImpl->mControlSize )
1067   {
1068     // Operations that need to be done if the size changes.
1069     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1070                                                              LAYOUT                    |
1071                                                              ALIGN                     |
1072                                                              UPDATE_ACTUAL_SIZE        |
1073                                                              REORDER );
1074
1075     mImpl->mControlSize = size;
1076   }
1077
1078   // Make sure the model is up-to-date before layouting
1079   ProcessModifyEvents();
1080   UpdateModel( mImpl->mOperationsPending );
1081
1082   Size layoutSize;
1083   bool updated = DoRelayout( mImpl->mControlSize,
1084                              mImpl->mOperationsPending,
1085                              layoutSize );
1086
1087   // Do not re-do any operation until something changes.
1088   mImpl->mOperationsPending = NO_OPERATION;
1089
1090   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1091   CalculateTextAlignment( size );
1092
1093   if( mImpl->mTextInput )
1094   {
1095     // Move the cursor, grab handle etc.
1096     updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1097   }
1098
1099   return updated;
1100 }
1101
1102 void Controller::ProcessModifyEvents()
1103 {
1104   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1105
1106   for( unsigned int i=0; i<events.size(); ++i )
1107   {
1108     if( REPLACE_TEXT == events[0].type )
1109     {
1110       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1111       DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1112
1113       ReplaceTextEvent( events[0].text );
1114     }
1115     else if( INSERT_TEXT == events[0].type )
1116     {
1117       InsertTextEvent( events[0].text );
1118     }
1119     else if( DELETE_TEXT == events[0].type )
1120     {
1121       DeleteTextEvent();
1122     }
1123   }
1124
1125   // Discard temporary text
1126   events.clear();
1127 }
1128
1129 void Controller::ReplaceTextEvent( const std::string& text )
1130 {
1131   // Reset buffers.
1132   mImpl->mLogicalModel->mText.Clear();
1133   mImpl->mLogicalModel->mScriptRuns.Clear();
1134   mImpl->mLogicalModel->mFontRuns.Clear();
1135   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1136   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1137   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1138   mImpl->mLogicalModel->mCharacterDirections.Clear();
1139   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1140   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1141   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1142   mImpl->mVisualModel->mGlyphs.Clear();
1143   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1144   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1145   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1146   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1147   mImpl->mVisualModel->mGlyphPositions.Clear();
1148   mImpl->mVisualModel->mLines.Clear();
1149
1150   //  Convert text into UTF-32
1151   Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1152   utf32Characters.Resize( text.size() );
1153
1154   // This is a bit horrible but std::string returns a (signed) char*
1155   const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1156
1157   // Transform a text array encoded in utf8 into an array encoded in utf32.
1158   // It returns the actual number of characters.
1159   Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1160   utf32Characters.Resize( characterCount );
1161
1162   // Reset the cursor position
1163   if( mImpl->mTextInput )
1164   {
1165     mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1166     // TODO - handle secondary cursor
1167   }
1168
1169   // The natural size needs to be re-calculated.
1170   mImpl->mRecalculateNaturalSize = true;
1171
1172   // Apply modifications to the model
1173   mImpl->mOperationsPending = ALL_OPERATIONS;
1174   UpdateModel( ALL_OPERATIONS );
1175   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
1176                                                            ALIGN              |
1177                                                            UPDATE_ACTUAL_SIZE |
1178                                                            REORDER );
1179 }
1180
1181 void Controller::InsertTextEvent( const std::string& text )
1182 {
1183   DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1184
1185   // TODO - Optimize this
1186   mImpl->mLogicalModel->mScriptRuns.Clear();
1187   mImpl->mLogicalModel->mFontRuns.Clear();
1188   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1189   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1190   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1191   mImpl->mLogicalModel->mCharacterDirections.Clear();
1192   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1193   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1194   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1195   mImpl->mVisualModel->mGlyphs.Clear();
1196   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1197   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1198   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1199   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1200   mImpl->mVisualModel->mGlyphPositions.Clear();
1201   mImpl->mVisualModel->mLines.Clear();
1202
1203   //  Convert text into UTF-32
1204   Vector<Character> utf32Characters;
1205   utf32Characters.Resize( text.size() );
1206
1207   // This is a bit horrible but std::string returns a (signed) char*
1208   const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1209
1210   // Transform a text array encoded in utf8 into an array encoded in utf32.
1211   // It returns the actual number of characters.
1212   Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1213   utf32Characters.Resize( characterCount );
1214
1215   // Insert at current cursor position
1216   Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1217   CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1218
1219   if( cursorIndex < modifyText.Count() )
1220   {
1221     modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1222   }
1223   else
1224   {
1225     modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1226   }
1227
1228   // Advance the cursor position
1229   ++cursorIndex;
1230
1231   // The natural size needs to be re-calculated.
1232   mImpl->mRecalculateNaturalSize = true;
1233
1234   // Apply modifications to the model; TODO - Optimize this
1235   mImpl->mOperationsPending = ALL_OPERATIONS;
1236   UpdateModel( ALL_OPERATIONS );
1237   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
1238                                                            ALIGN              |
1239                                                            UPDATE_ACTUAL_SIZE |
1240                                                            REORDER );
1241
1242   // Queue a cursor reposition event; this must wait until after DoRelayout()
1243   mImpl->mTextInput->mUpdateCursorPosition = true;
1244 }
1245
1246 void Controller::DeleteTextEvent()
1247 {
1248   DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1249
1250   // TODO - Optimize this
1251   mImpl->mLogicalModel->mScriptRuns.Clear();
1252   mImpl->mLogicalModel->mFontRuns.Clear();
1253   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1254   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1255   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1256   mImpl->mLogicalModel->mCharacterDirections.Clear();
1257   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1258   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1259   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1260   mImpl->mVisualModel->mGlyphs.Clear();
1261   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1262   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1263   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1264   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1265   mImpl->mVisualModel->mGlyphPositions.Clear();
1266   mImpl->mVisualModel->mLines.Clear();
1267
1268   // Delte at current cursor position
1269   Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1270   CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1271
1272   if( cursorIndex > 0 &&
1273       cursorIndex-1 < modifyText.Count() )
1274   {
1275     modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1276
1277     // Cursor position retreat
1278     --cursorIndex;
1279   }
1280
1281   // The natural size needs to be re-calculated.
1282   mImpl->mRecalculateNaturalSize = true;
1283
1284   // Apply modifications to the model; TODO - Optimize this
1285   mImpl->mOperationsPending = ALL_OPERATIONS;
1286   UpdateModel( ALL_OPERATIONS );
1287   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
1288                                                            ALIGN              |
1289                                                            UPDATE_ACTUAL_SIZE |
1290                                                            REORDER );
1291
1292   // Queue a cursor reposition event; this must wait until after DoRelayout()
1293   mImpl->mTextInput->mUpdateCursorPosition = true;
1294 }
1295
1296 void Controller::UpdateModel( OperationsMask operationsRequired )
1297 {
1298   // Calculate the operations to be done.
1299   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1300
1301   Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1302
1303   const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1304
1305   Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1306   if( GET_LINE_BREAKS & operations )
1307   {
1308     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1309     // calculate the bidirectional info for each 'paragraph'.
1310     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1311     // is not shaped together).
1312     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1313
1314     SetLineBreakInfo( utf32Characters,
1315                       lineBreakInfo );
1316   }
1317
1318   Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1319   if( GET_WORD_BREAKS & operations )
1320   {
1321     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1322     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1323
1324     SetWordBreakInfo( utf32Characters,
1325                       wordBreakInfo );
1326   }
1327
1328   const bool getScripts = GET_SCRIPTS & operations;
1329   const bool validateFonts = VALIDATE_FONTS & operations;
1330
1331   Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1332   Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1333
1334   if( getScripts || validateFonts )
1335   {
1336     // Validates the fonts assigned by the application or assigns default ones.
1337     // It makes sure all the characters are going to be rendered by the correct font.
1338     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1339
1340     if( getScripts )
1341     {
1342       // Retrieves the scripts used in the text.
1343       multilanguageSupport.SetScripts( utf32Characters,
1344                                        lineBreakInfo,
1345                                        scripts );
1346     }
1347
1348     if( validateFonts )
1349     {
1350       if( 0u == validFonts.Count() )
1351       {
1352         // Copy the requested font defaults received via the property system.
1353         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1354         GetDefaultFonts( validFonts, numberOfCharacters );
1355       }
1356
1357       // Validates the fonts. If there is a character with no assigned font it sets a default one.
1358       // After this call, fonts are validated.
1359       multilanguageSupport.ValidateFonts( utf32Characters,
1360                                           scripts,
1361                                           validFonts );
1362     }
1363   }
1364
1365   Vector<Character> mirroredUtf32Characters;
1366   bool textMirrored = false;
1367   if( BIDI_INFO & operations )
1368   {
1369     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1370     // bidirectional info.
1371
1372     Length numberOfParagraphs = 0u;
1373
1374     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1375     for( Length index = 0u; index < numberOfCharacters; ++index )
1376     {
1377       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1378       {
1379         ++numberOfParagraphs;
1380       }
1381     }
1382
1383     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1384     bidirectionalInfo.Reserve( numberOfParagraphs );
1385
1386     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1387     SetBidirectionalInfo( utf32Characters,
1388                           scripts,
1389                           lineBreakInfo,
1390                           bidirectionalInfo );
1391
1392     if( 0u != bidirectionalInfo.Count() )
1393     {
1394       // This paragraph has right to left text. Some characters may need to be mirrored.
1395       // TODO: consider if the mirrored string can be stored as well.
1396
1397       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1398
1399       // Only set the character directions if there is right to left characters.
1400       Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1401       directions.Resize( numberOfCharacters );
1402
1403       GetCharactersDirection( bidirectionalInfo,
1404                               directions );
1405     }
1406     else
1407     {
1408       // There is no right to left characters. Clear the directions vector.
1409       mImpl->mLogicalModel->mCharacterDirections.Clear();
1410     }
1411
1412    }
1413
1414   Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1415   Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1416   Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1417   if( SHAPE_TEXT & operations )
1418   {
1419     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1420     // Shapes the text.
1421     ShapeText( textToShape,
1422                lineBreakInfo,
1423                scripts,
1424                validFonts,
1425                glyphs,
1426                glyphsToCharactersMap,
1427                charactersPerGlyph );
1428
1429     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1430     mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1431     mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1432   }
1433
1434   const Length numberOfGlyphs = glyphs.Count();
1435
1436   if( GET_GLYPH_METRICS & operations )
1437   {
1438     mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1439   }
1440 }
1441
1442 bool Controller::DoRelayout( const Size& size,
1443                              OperationsMask operationsRequired,
1444                              Size& layoutSize )
1445 {
1446   bool viewUpdated( false );
1447
1448   // Calculate the operations to be done.
1449   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1450
1451   if( LAYOUT & operations )
1452   {
1453     // Some vectors with data needed to layout and reorder may be void
1454     // after the first time the text has been laid out.
1455     // Fill the vectors again.
1456
1457     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1458
1459     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1460     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1461     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1462     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1463     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1464
1465     // Set the layout parameters.
1466     LayoutParameters layoutParameters( size,
1467                                        mImpl->mLogicalModel->mText.Begin(),
1468                                        lineBreakInfo.Begin(),
1469                                        wordBreakInfo.Begin(),
1470                                        numberOfGlyphs,
1471                                        glyphs.Begin(),
1472                                        glyphsToCharactersMap.Begin(),
1473                                        charactersPerGlyph.Begin() );
1474
1475     // The laid-out lines.
1476     // It's not possible to know in how many lines the text is going to be laid-out,
1477     // but it can be resized at least with the number of 'paragraphs' to avoid
1478     // some re-allocations.
1479     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1480
1481     // Delete any previous laid out lines before setting the new ones.
1482     lines.Clear();
1483
1484     // The capacity of the bidirectional paragraph info is the number of paragraphs.
1485     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1486
1487     // Resize the vector of positions to have the same size than the vector of glyphs.
1488     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1489     glyphPositions.Resize( numberOfGlyphs );
1490
1491     // Update the visual model.
1492     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1493                                                    glyphPositions,
1494                                                    lines,
1495                                                    layoutSize );
1496
1497     if( viewUpdated )
1498     {
1499       // Reorder the lines
1500       if( REORDER & operations )
1501       {
1502         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1503
1504         // Check first if there are paragraphs with bidirectional info.
1505         if( 0u != bidirectionalInfo.Count() )
1506         {
1507           // Get the lines
1508           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1509
1510           // Reorder the lines.
1511           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1512           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1513           ReorderLines( bidirectionalInfo,
1514                         lines,
1515                         lineBidirectionalInfoRuns );
1516
1517           // Set the bidirectional info into the model.
1518           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1519           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1520                                                        numberOfBidirectionalInfoRuns );
1521
1522           // Set the bidirectional info per line into the layout parameters.
1523           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1524           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1525
1526           // Get the character to glyph conversion table and set into the layout.
1527           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1528
1529           // Get the glyphs per character table and set into the layout.
1530           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1531
1532           // Re-layout the text. Reorder those lines with right to left characters.
1533           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1534                                                          glyphPositions );
1535
1536           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1537           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1538                  endIt = lineBidirectionalInfoRuns.End();
1539                it != endIt;
1540                ++it )
1541           {
1542             BidirectionalLineInfoRun& bidiLineInfo = *it;
1543
1544             free( bidiLineInfo.visualToLogicalMap );
1545           }
1546         }
1547       } // REORDER
1548
1549       if( ALIGN & operations )
1550       {
1551         mImpl->mLayoutEngine.Align( layoutParameters,
1552                                     layoutSize,
1553                                     lines,
1554                                     glyphPositions );
1555       }
1556
1557       // Sets the actual size.
1558       if( UPDATE_ACTUAL_SIZE & operations )
1559       {
1560         mImpl->mVisualModel->SetActualSize( layoutSize );
1561       }
1562     } // view updated
1563   }
1564   else
1565   {
1566     layoutSize = mImpl->mVisualModel->GetActualSize();
1567   }
1568
1569   return viewUpdated;
1570 }
1571
1572 void Controller::CalculateTextAlignment( const Size& size )
1573 {
1574   // Get the direction of the first character.
1575   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1576
1577   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1578
1579   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1580   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1581   if( firstParagraphDirection &&
1582       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1583   {
1584     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1585     {
1586       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1587     }
1588     else
1589     {
1590       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1591     }
1592   }
1593
1594   switch( horizontalAlignment )
1595   {
1596     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1597     {
1598       mImpl->mAlignmentOffset.x = 0.f;
1599       break;
1600     }
1601     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1602     {
1603       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1604       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1605       break;
1606     }
1607     case LayoutEngine::HORIZONTAL_ALIGN_END:
1608     {
1609       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1610       break;
1611     }
1612   }
1613
1614   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1615   switch( verticalAlignment )
1616   {
1617     case LayoutEngine::VERTICAL_ALIGN_TOP:
1618     {
1619       mImpl->mAlignmentOffset.y = 0.f;
1620       break;
1621     }
1622     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1623     {
1624       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1625       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1626       break;
1627     }
1628     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1629     {
1630       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1631       break;
1632     }
1633   }
1634 }
1635
1636 View& Controller::GetView()
1637 {
1638   return mImpl->mView;
1639 }
1640
1641 LayoutEngine& Controller::GetLayoutEngine()
1642 {
1643   return mImpl->mLayoutEngine;
1644 }
1645
1646 void Controller::RequestRelayout()
1647 {
1648   mImpl->mControlInterface.RequestTextRelayout();
1649 }
1650
1651 void Controller::KeyboardFocusGainEvent()
1652 {
1653   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1654
1655   if( mImpl->mTextInput )
1656   {
1657     TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1658     mImpl->mTextInput->mEventQueue.push_back( event );
1659
1660     RequestRelayout();
1661   }
1662 }
1663
1664 void Controller::KeyboardFocusLostEvent()
1665 {
1666   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1667
1668   if( mImpl->mTextInput )
1669   {
1670     TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1671     mImpl->mTextInput->mEventQueue.push_back( event );
1672
1673     RequestRelayout();
1674   }
1675 }
1676
1677 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1678 {
1679   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1680
1681   if( mImpl->mTextInput &&
1682       keyEvent.state == KeyEvent::Down )
1683   {
1684     int keyCode = keyEvent.keyCode;
1685     const std::string& keyString = keyEvent.keyPressed;
1686
1687     // Pre-process to separate modifying events from non-modifying input events.
1688     if( Dali::DALI_KEY_ESCAPE == keyCode )
1689     {
1690       // Escape key is a special case which causes focus loss
1691       KeyboardFocusLostEvent();
1692     }
1693     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1694              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1695              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1696              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1697     {
1698       TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1699       event.p1.mInt = keyCode;
1700       mImpl->mTextInput->mEventQueue.push_back( event );
1701     }
1702     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1703     {
1704       // Queue a delete event
1705       ModifyEvent event;
1706       event.type = DELETE_TEXT;
1707       mImpl->mModifyEvents.push_back( event );
1708     }
1709     else if( !keyString.empty() )
1710     {
1711       // Queue an insert event
1712       ModifyEvent event;
1713       event.type = INSERT_TEXT;
1714       event.text = keyString;
1715       mImpl->mModifyEvents.push_back( event );
1716     }
1717
1718     mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1719
1720     RequestRelayout();
1721   }
1722
1723   return false;
1724 }
1725
1726 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1727 {
1728   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1729
1730   if( mImpl->mTextInput )
1731   {
1732     TextInput::Event event( TextInput::TAP_EVENT );
1733     event.p1.mUint = tapCount;
1734     event.p2.mFloat = x;
1735     event.p3.mFloat = y;
1736     mImpl->mTextInput->mEventQueue.push_back( event );
1737
1738     RequestRelayout();
1739   }
1740 }
1741
1742 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1743 {
1744   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1745
1746   if( mImpl->mTextInput )
1747   {
1748     TextInput::Event event( TextInput::PAN_EVENT );
1749     event.p1.mInt = state;
1750     event.p2.mFloat = displacement.x;
1751     event.p3.mFloat = displacement.y;
1752     mImpl->mTextInput->mEventQueue.push_back( event );
1753
1754     RequestRelayout();
1755   }
1756 }
1757
1758 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1759 {
1760   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1761
1762   if( mImpl->mTextInput )
1763   {
1764     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1765     event.p1.mUint  = state;
1766     event.p2.mFloat = x;
1767     event.p3.mFloat = y;
1768     mImpl->mTextInput->mEventQueue.push_back( event );
1769
1770     RequestRelayout();
1771   }
1772 }
1773
1774 Controller::~Controller()
1775 {
1776   delete mImpl;
1777 }
1778
1779 Controller::Controller( ControlInterface& controlInterface )
1780 : mImpl( NULL )
1781 {
1782   mImpl = new Controller::Impl( controlInterface );
1783 }
1784
1785 } // namespace Text
1786
1787 } // namespace Toolkit
1788
1789 } // namespace Dali