TextModel - Create paragraph info for a given range of characters inside a text.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index ccd64db..78bd7e9 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 #include <dali-toolkit/internal/text/character-set-conversion.h>
+#include <dali-toolkit/internal/text/color-segmentation.h>
 #include <dali-toolkit/internal/text/multi-language-support.h>
 #include <dali-toolkit/internal/text/segmentation.h>
 #include <dali-toolkit/internal/text/shaper.h>
@@ -132,7 +133,8 @@ EventData::EventData( DecoratorPtr decorator )
   mUpdateRightSelectionPosition( false ),
   mScrollAfterUpdatePosition( false ),
   mScrollAfterDelete( false ),
-  mAllTextSelected( false )
+  mAllTextSelected( false ),
+  mUpdateInputStyle( false )
 {
   mImfManager = ImfManager::Get();
 }
@@ -287,6 +289,20 @@ bool Controller::Impl::ProcessInputEvents()
     }
   }
 
+  if( mEventData->mUpdateInputStyle )
+  {
+    // Set the default style first.
+    RetrieveDefaultInputStyle( mEventData->mInputStyle );
+
+    // Get the character index from the cursor index.
+    const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
+
+    // Retrieve the style from the style runs stored in the logical model.
+    mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
+
+    mEventData->mUpdateInputStyle = false;
+  }
+
   mEventData->mEventQueue.clear();
 
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
@@ -309,6 +325,8 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
   const Length numberOfCharacters = utf32Characters.Count();
 
   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
+  CharacterIndex startIndex = 0u;
+  Length requestedNumberOfCharacters = numberOfCharacters;
   if( GET_LINE_BREAKS & operations )
   {
     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
@@ -318,7 +336,13 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
 
     SetLineBreakInfo( utf32Characters,
+                      startIndex,
+                      requestedNumberOfCharacters,
                       lineBreakInfo );
+
+    // Create the paragraph info.
+    mLogicalModel->CreateParagraphInfo( startIndex,
+                                        requestedNumberOfCharacters );
   }
 
   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
@@ -328,6 +352,8 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
 
     SetWordBreakInfo( utf32Characters,
+                      startIndex,
+                      requestedNumberOfCharacters,
                       wordBreakInfo );
   }
 
@@ -347,22 +373,27 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
     {
       // Retrieves the scripts used in the text.
       multilanguageSupport.SetScripts( utf32Characters,
+                                       startIndex,
+                                       requestedNumberOfCharacters,
                                        scripts );
     }
 
     if( validateFonts )
     {
-      if( 0u == validFonts.Count() )
-      {
-        // Copy the requested font defaults received via the property system.
-        // These may not be valid i.e. may not contain glyphs for the necessary scripts.
-        GetDefaultFonts( validFonts, numberOfCharacters );
-      }
+      // Validate the fonts set through the mark-up string.
+      Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
+
+      // Get the default font id.
+      const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
 
       // Validates the fonts. If there is a character with no assigned font it sets a default one.
       // After this call, fonts are validated.
       multilanguageSupport.ValidateFonts( utf32Characters,
                                           scripts,
+                                          fontDescriptionRuns,
+                                          defaultFontId,
+                                          startIndex,
+                                          requestedNumberOfCharacters,
                                           validFonts );
     }
   }
@@ -391,23 +422,29 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
     SetBidirectionalInfo( utf32Characters,
                           scripts,
                           lineBreakInfo,
+                          startIndex,
+                          requestedNumberOfCharacters,
                           bidirectionalInfo );
 
     if( 0u != bidirectionalInfo.Count() )
     {
-      // This paragraph has right to left text. Some characters may need to be mirrored.
-      // TODO: consider if the mirrored string can be stored as well.
-
-      textMirrored = GetMirroredText( utf32Characters,
-                                      mirroredUtf32Characters,
-                                      bidirectionalInfo );
-
       // Only set the character directions if there is right to left characters.
       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
-      directions.Resize( numberOfCharacters );
-
       GetCharactersDirection( bidirectionalInfo,
+                              numberOfCharacters,
+                              startIndex,
+                              requestedNumberOfCharacters,
                               directions );
+
+      // This paragraph has right to left text. Some characters may need to be mirrored.
+      // TODO: consider if the mirrored string can be stored as well.
+
+      textMirrored = GetMirroredText( utf32Characters,
+                                      directions,
+                                      bidirectionalInfo,
+                                      startIndex,
+                                      requestedNumberOfCharacters,
+                                      mirroredUtf32Characters );
     }
     else
     {
@@ -422,6 +459,7 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
   Vector<GlyphIndex> newParagraphGlyphs;
   newParagraphGlyphs.Reserve( numberOfParagraphs );
 
+  GlyphIndex startGlyphIndex = 0u;
   if( SHAPE_TEXT & operations )
   {
     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
@@ -430,21 +468,24 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
                lineBreakInfo,
                scripts,
                validFonts,
+               startIndex,
+               startGlyphIndex,
+               requestedNumberOfCharacters,
                glyphs,
                glyphsToCharactersMap,
                charactersPerGlyph,
                newParagraphGlyphs );
 
     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
-    mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
-    mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
+    mVisualModel->CreateGlyphsPerCharacterTable( startIndex, numberOfCharacters );
+    mVisualModel->CreateCharacterToGlyphTable( startIndex, numberOfCharacters );
   }
 
   const Length numberOfGlyphs = glyphs.Count();
 
   if( GET_GLYPH_METRICS & operations )
   {
-    GlyphInfo* glyphsBuffer = glyphs.Begin();
+    GlyphInfo* glyphsBuffer = glyphs.Begin() + startGlyphIndex;
     mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
 
     // Update the width and advance of all new paragraph characters.
@@ -459,7 +500,7 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
     }
   }
 
-  if( mEventData &&
+  if( ( NULL != mEventData ) &&
       mEventData->mPreEditFlag &&
       ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
   {
@@ -481,18 +522,57 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
   }
 }
 
-void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
+bool Controller::Impl::UpdateModelStyle( OperationsMask operationsRequired )
+{
+  bool updated = false;
+
+  if( COLOR & operationsRequired )
+  {
+    // Set the color runs in glyphs.
+    SetColorSegmentationInfo( mLogicalModel->mColorRuns,
+                              mVisualModel->mCharactersToGlyph,
+                              mVisualModel->mGlyphsPerCharacter,
+                              mVisualModel->mColorRuns );
+
+    updated = true;
+  }
+
+  return updated;
+}
+
+void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
 {
+  // Sets the default text's color.
+  inputStyle.textColor = mTextColor;
+
+  // Sets the default font's family name, weight, width, slant and size.
   if( mFontDefaults )
   {
-    DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::GetDefaultFonts font family(%s)\n", mFontDefaults->mFontDescription.family.c_str() );
-    FontRun fontRun;
-    fontRun.characterRun.characterIndex = 0;
-    fontRun.characterRun.numberOfCharacters = numberOfCharacters;
-    fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
-    fontRun.isDefault = true;
+    inputStyle.familyName = mFontDefaults->mFontDescription.family;
+    inputStyle.weight = mFontDefaults->mFontDescription.weight;
+    inputStyle.width = mFontDefaults->mFontDescription.width;
+    inputStyle.slant = mFontDefaults->mFontDescription.slant;
+    inputStyle.size = mFontDefaults->mDefaultPointSize;
+
+    inputStyle.familyDefined = mFontDefaults->familyDefined;
+    inputStyle.weightDefined = mFontDefaults->weightDefined;
+    inputStyle.widthDefined = mFontDefaults->widthDefined;
+    inputStyle.slantDefined = mFontDefaults->slantDefined;
+    inputStyle.sizeDefined = mFontDefaults->sizeDefined;
+  }
+  else
+  {
+    inputStyle.familyName.clear();
+    inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
+    inputStyle.width = TextAbstraction::FontWidth::NORMAL;
+    inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
+    inputStyle.size = 0.f;
 
-    fonts.PushBack( fontRun );
+    inputStyle.familyDefined = false;
+    inputStyle.weightDefined = false;
+    inputStyle.widthDefined = false;
+    inputStyle.slantDefined = false;
+    inputStyle.sizeDefined = false;
   }
 }
 
@@ -549,6 +629,7 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
   }
 
   mEventData->mUpdateCursorPosition = true;
+  mEventData->mUpdateInputStyle = true;
   mEventData->mScrollAfterUpdatePosition = true;
 }
 
@@ -578,6 +659,7 @@ void Controller::Impl::OnTapEvent( const Event& event )
 
       mEventData->mUpdateCursorPosition = true;
       mEventData->mScrollAfterUpdatePosition = true;
+      mEventData->mUpdateInputStyle = true;
 
       // Notify the cursor position to the imf manager.
       if( mEventData->mImfManager )
@@ -602,7 +684,7 @@ void Controller::Impl::OnPanEvent( const Event& event )
   if( Gesture::Started    == state ||
       Gesture::Continuing == state )
   {
-    const Vector2& actualSize = mVisualModel->GetActualSize();
+    const Vector2& actualSize = mVisualModel->GetLayoutSize();
     const Vector2 currentScroll = mEventData->mScrollPosition;
 
     if( mEventData->mHorizontalScrollingEnabled )
@@ -632,7 +714,7 @@ void Controller::Impl::OnLongPressEvent( const Event& event )
 {
   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
 
-  if  ( EventData::EDITING == mEventData->mState )
+  if( EventData::EDITING == mEventData->mState )
   {
     ChangeState ( EventData::EDITING_WITH_POPUP );
     mEventData->mDecoratorUpdated = true;
@@ -709,8 +791,9 @@ void Controller::Impl::OnHandleEvent( const Event& event )
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
       mEventData->mUpdateCursorPosition = true;
+      mEventData->mUpdateInputStyle = true;
 
-      if ( !IsClipboardEmpty() )
+      if( !IsClipboardEmpty() )
       {
         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
       }
@@ -756,7 +839,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
   else if( HANDLE_SCROLLING == state )
   {
     const float xSpeed = event.p2.mFloat;
-    const Vector2& actualSize = mVisualModel->GetActualSize();
+    const Vector2& actualSize = mVisualModel->GetLayoutSize();
     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
 
     mEventData->mScrollPosition.x += xSpeed;
@@ -795,6 +878,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
       mEventData->mPrimaryCursorPosition = handlePosition;
+      mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
     }
     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
     {
@@ -889,31 +973,36 @@ void Controller::Impl::OnSelectAllEvent()
   }
 }
 
-void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
+void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
 {
-  if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
+  if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
   {
     // Nothing to select if handles are in the same place.
-    selectedText="";
+    selectedText.clear();
     return;
   }
 
   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
 
   //Get start and end position of selection
-  uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
-  uint32_t lengthOfSelectedText =  ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
+  const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
+  const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
 
   // Validate the start and end selection points
-  if(  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() )
+  if( ( startOfSelectedText + lengthOfSelectedText ) <= mLogicalModel->mText.Count() )
   {
     //Get text as a UTF8 string
     Vector<Character>& utf32Characters = mLogicalModel->mText;
 
     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
 
-    if ( deleteAfterRetreival  ) // Only delete text if copied successfully
+    if( deleteAfterRetrieval ) // Only delete text if copied successfully
     {
+      // Set as input style the style of the first deleted character.
+      mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
+
+      mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
+
       // Delete text between handles
       Vector<Character>& currentText = mLogicalModel->mText;
 
@@ -935,7 +1024,7 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete
 
 void Controller::Impl::ShowClipboard()
 {
-  if ( mClipboard )
+  if( mClipboard )
   {
     mClipboard.ShowClipboard();
   }
@@ -943,7 +1032,7 @@ void Controller::Impl::ShowClipboard()
 
 void Controller::Impl::HideClipboard()
 {
-  if ( mClipboard )
+  if( mClipboard )
   {
     mClipboard.HideClipboard();
   }
@@ -963,11 +1052,11 @@ void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
   ChangeState( EventData::EDITING );
 }
 
-void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
+void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
 {
   if ( mClipboard )
   {
-    retreivedString =  mClipboard.GetItem( itemIndex );
+    retrievedString =  mClipboard.GetItem( itemIndex );
   }
 }
 
@@ -1108,9 +1197,15 @@ void Controller::Impl::RepositionSelectionHandles()
   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
 
-  mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
+  mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
+                                       primaryPosition.x,
+                                       primaryCursorInfo.lineOffset + offset.y,
+                                       primaryCursorInfo.lineHeight );
 
-  mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
+  mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
+                                       secondaryPosition.x,
+                                       secondaryCursorInfo.lineOffset + offset.y,
+                                       secondaryCursorInfo.lineHeight );
 
   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
@@ -1135,8 +1230,8 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
 
   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
   const Length numberOfLines  = mVisualModel->mLines.Count();
-  if( 0 == numberOfGlyphs ||
-      0 == numberOfLines )
+  if( ( 0 == numberOfGlyphs ) ||
+      ( 0 == numberOfLines ) )
   {
     // Nothing to do if there is no text.
     return;
@@ -1175,31 +1270,31 @@ void Controller::Impl::SetPopupButtons()
   {
     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
 
-    if ( !IsClipboardEmpty() )
+    if( !IsClipboardEmpty() )
     {
       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
     }
 
-    if ( !mEventData->mAllTextSelected )
+    if( !mEventData->mAllTextSelected )
     {
       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
     }
   }
-  else if ( EventData::EDITING_WITH_POPUP == mEventData->mState )
+  else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
   {
-    if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
+    if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
     {
       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
     }
 
-    if ( !IsClipboardEmpty() )
+    if( !IsClipboardEmpty() )
     {
       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
     }
   }
-  else if ( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
+  else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
   {
     if ( !IsClipboardEmpty() )
     {
@@ -1236,7 +1331,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecoratorUpdated = true;
       HideClipboard();
     }
-    else if ( EventData::INTERRUPTED  == mEventData->mState)
+    else if( EventData::INTERRUPTED  == mEventData->mState)
     {
       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
@@ -1245,7 +1340,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecoratorUpdated = true;
       HideClipboard();
     }
-    else if ( EventData::SELECTING == mEventData->mState )
+    else if( EventData::SELECTING == mEventData->mState )
     {
       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
       mEventData->mDecorator->StopCursorBlink();
@@ -1323,7 +1418,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecoratorUpdated = true;
       HideClipboard();
     }
-    else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
+    else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
     {
       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
       mEventData->mDecorator->StopCursorBlink();
@@ -1336,7 +1431,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       }
       mEventData->mDecoratorUpdated = true;
     }
-    else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
+    else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
     {
       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
 
@@ -1354,7 +1449,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       }
       mEventData->mDecoratorUpdated = true;
     }
-    else if ( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
+    else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
     {
       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
 
@@ -1410,7 +1505,7 @@ void Controller::Impl::FindSelectionIndices( float visualX, float visualY, Chara
   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
   DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
 
-  if ( mLogicalModel->mText.Count() == 0 )
+  if( mLogicalModel->mText.Count() == 0 )
   {
     return;  // if model empty
   }
@@ -1418,7 +1513,7 @@ void Controller::Impl::FindSelectionIndices( float visualX, float visualY, Chara
   if( hitCharacter >= mLogicalModel->mText.Count() )
   {
     // Closest hit character is the last character.
-    if ( hitCharacter ==  mLogicalModel->mText.Count() )
+    if( hitCharacter ==  mLogicalModel->mText.Count() )
     {
       hitCharacter--; //Hit character index set to last character in logical model
     }
@@ -1466,8 +1561,8 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
   const Length numberOfLines  = mVisualModel->mLines.Count();
-  if( 0 == numberOfGlyphs ||
-      0 == numberOfLines )
+  if( ( 0 == numberOfGlyphs ) ||
+      ( 0 == numberOfLines ) )
   {
     return logicalIndex;
   }
@@ -1534,7 +1629,7 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
       // Get the position of the first glyph.
       const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex );
 
-      // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ﻻ. 
+      // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ﻻ.
       const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script );
       const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u;
       const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfBlocks );
@@ -1593,6 +1688,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
     // If there is no font's family set, use the default font.
     // Use the current alignment to place the cursor at the beginning, center or end of the box.
 
+    cursorInfo.lineOffset = 0.f;
     cursorInfo.lineHeight = GetDefaultFontLineHeight();
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
 
@@ -1672,7 +1768,8 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
 
-  // Set the line height.
+  // Set the line offset and height.
+  cursorInfo.lineOffset = 0.f;
   cursorInfo.lineHeight = line.ascender + -line.descender;
 
   // Calculate the primary cursor.
@@ -1813,6 +1910,25 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
   }
+
+  if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
+  {
+    // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
+
+    // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
+    // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
+
+    if( 0.f > cursorInfo.primaryPosition.x )
+    {
+      cursorInfo.primaryPosition.x = 0.f;
+    }
+
+    const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
+    if( cursorInfo.primaryPosition.x > edgeWidth )
+    {
+      cursorInfo.primaryPosition.x = edgeWidth;
+    }
+  }
 }
 
 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
@@ -1885,7 +2001,7 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
   // Sets the grab handle position.
   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
                                        cursorPosition.x,
-                                       cursorPosition.y,
+                                       cursorInfo.lineOffset + offset.y,
                                        cursorInfo.lineHeight );
 
   if( cursorInfo.isSecondaryCursor )
@@ -1927,12 +2043,13 @@ void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
     return;
   }
 
-  const Vector2 cursorPosition = cursorInfo.primaryPosition + mEventData->mScrollPosition + mAlignmentOffset;
+  const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+  const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
 
   // Sets the handle's position.
   mEventData->mDecorator->SetPosition( handleType,
                                        cursorPosition.x,
-                                       cursorPosition.y,
+                                       cursorInfo.lineOffset + offset.y,
                                        cursorInfo.lineHeight );
 
   // If selection handle at start of the text and other at end of the text then all text is selected.
@@ -1982,17 +2099,18 @@ void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
   const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
 
   // Transform the position to decorator coords.
-  const float offset = mEventData->mScrollPosition.x + mAlignmentOffset.x;
+  const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
+  const float offset = mEventData->mScrollPosition.x + alignment;
   const float decoratorPositionBegin = position.x + offset;
   const float decoratorPositionEnd = positionEnd + offset;
 
   if( decoratorPositionBegin < 0.f )
   {
-    mEventData->mScrollPosition.x = -position.x - mAlignmentOffset.x;
+    mEventData->mScrollPosition.x = -position.x - alignment;
   }
   else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
   {
-    mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - mAlignmentOffset.x;
+    mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
   }
 }
 
@@ -2004,7 +2122,7 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
   // Calculate the offset to match the cursor position before the character was deleted.
   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
 
-  ClampHorizontalScroll( mVisualModel->GetActualSize() );
+  ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
 }
 
 void Controller::Impl::RequestRelayout()