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