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