+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterDelete = true;
+
+ mImpl->RequestRelayout();
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ mImpl->mEditableControlInterface->TextChanged();
+ }
+ break;
+ }
+ case Toolkit::TextSelectionPopup::COPY:
+ {
+ mImpl->SendSelectionToClipboard( false ); // Text not modified
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+
+ mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
+ break;
+ }
+ case Toolkit::TextSelectionPopup::PASTE:
+ {
+ mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
+ break;
+ }
+ case Toolkit::TextSelectionPopup::SELECT:
+ {
+ const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+ if( mImpl->mEventData->mSelectionEnabled )
+ {
+ // Creates a SELECT event.
+ SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
+ }
+ break;
+ }
+ case Toolkit::TextSelectionPopup::SELECT_ALL:
+ {
+ // Creates a SELECT_ALL event
+ SelectEvent( 0.f, 0.f, true );
+ break;
+ }
+ case Toolkit::TextSelectionPopup::CLIPBOARD:
+ {
+ mImpl->ShowClipboard();
+ break;
+ }
+ case Toolkit::TextSelectionPopup::NONE:
+ {
+ // Nothing to do.
+ break;
+ }
+ }
+}
+
+// private : Update.
+
+void Controller::InsertText( const std::string& text, Controller::InsertType type )
+{
+ bool removedPrevious( false );
+ bool maxLengthReached( false );
+
+ DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
+
+ if( NULL == mImpl->mEventData )
+ {
+ return;
+ }
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
+ this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
+ mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
+
+ // TODO: At the moment the underline runs are only for pre-edit.
+ mImpl->mVisualModel->mUnderlineRuns.Clear();
+
+ // Keep the current number of characters.
+ const Length currentNumberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
+
+ // Remove the previous IMF pre-edit.
+ if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
+ {
+ removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
+ mImpl->mEventData->mPreEditLength,
+ DONT_UPDATE_INPUT_STYLE );
+
+ mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
+ mImpl->mEventData->mPreEditLength = 0u;
+ }
+ else
+ {
+ // Remove the previous Selection.
+ removedPrevious = RemoveSelectedText();
+ }
+
+ Vector<Character> utf32Characters;
+ Length characterCount = 0u;
+
+ if( !text.empty() )
+ {
+ // Convert text into UTF-32
+ utf32Characters.Resize( text.size() );
+
+ // This is a bit horrible but std::string returns a (signed) char*
+ const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
+
+ // Transform a text array encoded in utf8 into an array encoded in utf32.
+ // It returns the actual number of characters.
+ characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
+ utf32Characters.Resize( characterCount );
+
+ DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
+ }
+
+ if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
+ {
+ // The placeholder text is no longer needed
+ if( mImpl->IsShowingPlaceholderText() )
+ {
+ ResetText();
+ }
+
+ mImpl->ChangeState( EventData::EDITING );
+
+ // Handle the IMF (predicitive text) state changes
+ if( COMMIT == type )
+ {
+ // IMF manager is no longer handling key-events
+ mImpl->ClearPreEditFlag();
+ }
+ else // PRE_EDIT
+ {
+ if( !mImpl->mEventData->mPreEditFlag )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state\n" );
+
+ // Record the start of the pre-edit text
+ mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ }
+
+ mImpl->mEventData->mPreEditLength = utf32Characters.Count();
+ mImpl->mEventData->mPreEditFlag = true;
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
+ }
+
+ const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
+
+ // Restrict new text to fit within Maximum characters setting.
+ Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
+ maxLengthReached = ( characterCount > maxSizeOfNewText );
+
+ // The cursor position.
+ CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
+
+ // Update the text's style.
+
+ // Updates the text style runs by adding characters.
+ mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
+
+ // Get the character index from the cursor index.
+ const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
+
+ // Retrieve the text's style for the given index.
+ InputStyle style;
+ mImpl->RetrieveDefaultInputStyle( style );
+ mImpl->mLogicalModel->RetrieveStyle( styleIndex, style );
+
+ // Whether to add a new text color run.
+ const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
+
+ // Whether to add a new font run.
+ const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
+ const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
+ const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
+ const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
+ const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
+
+ // Add style runs.
+ if( addColorRun )
+ {
+ const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mColorRuns.Count();
+ mImpl->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
+
+ ColorRun& colorRun = *( mImpl->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
+ colorRun.color = mImpl->mEventData->mInputStyle.textColor;
+ colorRun.characterRun.characterIndex = cursorIndex;
+ colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
+ }
+
+ if( addFontNameRun ||
+ addFontWeightRun ||
+ addFontWidthRun ||
+ addFontSlantRun ||
+ addFontSizeRun )
+ {
+ const VectorBase::SizeType numberOfRuns = mImpl->mLogicalModel->mFontDescriptionRuns.Count();
+ mImpl->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
+
+ FontDescriptionRun& fontDescriptionRun = *( mImpl->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
+
+ if( addFontNameRun )
+ {
+ fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+
+ // The memory allocated for the font family name is freed when the font description is removed from the logical model.
+ }
+
+ if( addFontWeightRun )
+ {
+ fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
+ fontDescriptionRun.weightDefined = true;
+ }
+
+ if( addFontWidthRun )
+ {
+ fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
+ fontDescriptionRun.widthDefined = true;
+ }
+
+ if( addFontSlantRun )
+ {
+ fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
+ fontDescriptionRun.slantDefined = true;
+ }
+
+ if( addFontSizeRun )
+ {
+ fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
+ fontDescriptionRun.sizeDefined = true;
+ }
+
+ fontDescriptionRun.characterRun.characterIndex = cursorIndex;
+ fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
+ }
+
+ // Insert at current cursor position.
+ Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
+
+ if( cursorIndex < numberOfCharactersInModel )
+ {
+ modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
+ }
+ else
+ {
+ modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
+ }
+
+ // Mark the first paragraph to be updated.
+ mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
+ mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
+
+ // Update the cursor index.
+ cursorIndex += maxSizeOfNewText;
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
+ }
+
+ const Length numberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
+
+ if( ( 0u == mImpl->mLogicalModel->mText.Count() ) &&
+ mImpl->IsPlaceholderAvailable() )
+ {
+ // Show place-holder if empty after removing the pre-edit text
+ ShowPlaceholderText();
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->ClearPreEditFlag();
+ }
+ else if( removedPrevious ||
+ ( 0 != utf32Characters.Count() ) )
+ {
+ // Queue an inserted event
+ mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ if( numberOfCharacters < currentNumberOfCharacters )
+ {
+ mImpl->mEventData->mScrollAfterDelete = true;
+ }
+ else
+ {
+ mImpl->mEventData->mScrollAfterUpdatePosition = true;
+ }
+ }
+
+ if( maxLengthReached )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
+
+ mImpl->ResetImfManager();
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ // Do this last since it provides callbacks into application code
+ mImpl->mEditableControlInterface->MaxLengthReached();
+ }
+ }
+}
+
+void Controller::PasteText( const std::string& stringToPaste )
+{
+ InsertText( stringToPaste, Text::Controller::COMMIT );
+ mImpl->ChangeState( EventData::EDITING );
+ mImpl->RequestRelayout();
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ // Do this last since it provides callbacks into application code
+ mImpl->mEditableControlInterface->TextChanged();
+ }
+}
+
+bool Controller::RemoveText( int cursorOffset,
+ int numberOfCharacters,
+ UpdateInputStyleType type )
+{
+ bool removed = false;
+
+ if( NULL == mImpl->mEventData )
+ {
+ return removed;
+ }
+
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
+ this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
+
+ if( !mImpl->IsShowingPlaceholderText() )
+ {
+ // Delete at current cursor position
+ Vector<Character>& currentText = mImpl->mLogicalModel->mText;
+ CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
+
+ CharacterIndex cursorIndex = oldCursorIndex;
+
+ // Validate the cursor position & number of characters
+ if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
+ {
+ cursorIndex = oldCursorIndex + cursorOffset;
+ }
+
+ if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
+ {
+ numberOfCharacters = currentText.Count() - cursorIndex;
+ }
+
+ if( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
+ {
+ // Mark the paragraphs to be updated.
+ mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
+ mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
+
+ // Update the input style and remove the text's style before removing the text.
+
+ if( UPDATE_INPUT_STYLE == type )
+ {
+ // Keep a copy of the current input style.
+ InputStyle currentInputStyle;
+ currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
+
+ // Set first the default input style.
+ mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
+
+ // Update the input style.
+ mImpl->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
+
+ // Compare if the input style has changed.
+ const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );
+
+ if( hasInputStyleChanged )
+ {
+ const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mImpl->mEventData->mInputStyle );
+ // Queue the input style changed signal.
+ mImpl->mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+ }
+ }
+
+ // Updates the text style runs by removing characters. Runs with no characters are removed.
+ mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
+
+ // Remove the characters.
+ Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
+ Vector<Character>::Iterator last = first + numberOfCharacters;
+
+ currentText.Erase( first, last );
+
+ // Cursor position retreat
+ oldCursorIndex = cursorIndex;
+
+ mImpl->mEventData->mScrollAfterDelete = true;
+
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
+ removed = true;
+ }
+ }
+
+ return removed;
+}
+
+bool Controller::RemoveSelectedText()
+{
+ bool textRemoved( false );
+
+ if( EventData::SELECTING == mImpl->mEventData->mState )
+ {
+ std::string removedString;
+ mImpl->RetrieveSelection( removedString, true );
+
+ if( !removedString.empty() )
+ {
+ textRemoved = true;
+ mImpl->ChangeState( EventData::EDITING );
+ }
+ }
+
+ return textRemoved;
+}
+
+// private : Relayout.
+
+bool Controller::DoRelayout( const Size& size,
+ OperationsMask operationsRequired,
+ Size& layoutSize )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
+ bool viewUpdated( false );
+
+ // Calculate the operations to be done.
+ const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
+
+ const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
+ const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
+
+ // Get the current layout size.
+ layoutSize = mImpl->mVisualModel->GetLayoutSize();
+
+ if( NO_OPERATION != ( LAYOUT & operations ) )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
+
+ // Some vectors with data needed to layout and reorder may be void
+ // after the first time the text has been laid out.
+ // Fill the vectors again.
+
+ // Calculate the number of glyphs to layout.
+ const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
+ const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
+ const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
+ const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
+
+ const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
+ const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
+ const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
+ const Length totalNumberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
+
+ if( 0u == totalNumberOfGlyphs )
+ {
+ if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+ {
+ mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
+ }
+
+ // Nothing else to do if there is no glyphs.
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
+ return true;
+ }
+
+ const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
+ const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
+ const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
+ const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
+ const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
+ const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
+ const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
+
+ // Set the layout parameters.
+ LayoutParameters layoutParameters( size,
+ textBuffer,
+ lineBreakInfo.Begin(),
+ wordBreakInfo.Begin(),
+ ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
+ glyphs.Begin(),
+ glyphsToCharactersMap.Begin(),
+ charactersPerGlyph.Begin(),
+ charactersToGlyphBuffer,
+ glyphsPerCharacterBuffer,
+ totalNumberOfGlyphs );
+
+ // Resize the vector of positions to have the same size than the vector of glyphs.
+ Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
+ glyphPositions.Resize( totalNumberOfGlyphs );
+
+ // Whether the last character is a new paragraph character.
+ mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
+ layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
+
+ // The initial glyph and the number of glyphs to layout.
+ layoutParameters.startGlyphIndex = startGlyphIndex;
+ layoutParameters.numberOfGlyphs = numberOfGlyphs;
+ layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
+ layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
+
+ // Update the visual model.
+ Size newLayoutSize;
+ viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
+ glyphPositions,
+ mImpl->mVisualModel->mLines,
+ newLayoutSize );
+
+ viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
+
+ if( viewUpdated )
+ {
+ layoutSize = newLayoutSize;
+
+ if ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
+ {
+ mImpl->mAutoScrollDirectionRTL = false;
+ }
+
+ // Reorder the lines
+ if( NO_OPERATION != ( REORDER & operations ) )
+ {
+ Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
+ Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
+
+ // Check first if there are paragraphs with bidirectional info.
+ if( 0u != bidirectionalInfo.Count() )
+ {
+ // Get the lines
+ const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
+
+ // Reorder the lines.
+ bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
+ ReorderLines( bidirectionalInfo,
+ startIndex,
+ requestedNumberOfCharacters,
+ mImpl->mVisualModel->mLines,
+ bidirectionalLineInfo );
+
+ // Set the bidirectional info per line into the layout parameters.
+ layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
+ layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
+
+ // Re-layout the text. Reorder those lines with right to left characters.
+ mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
+ startIndex,
+ requestedNumberOfCharacters,
+ glyphPositions );
+
+ if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
+ {
+ const LineRun* const firstline = mImpl->mVisualModel->mLines.Begin();
+ if ( firstline )
+ {
+ mImpl->mAutoScrollDirectionRTL = firstline->direction;
+ }
+ }
+ }
+ } // REORDER
+
+ // Sets the layout size.
+ if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+ {
+ mImpl->mVisualModel->SetLayoutSize( layoutSize );
+ }
+ } // view updated
+
+ // Store the size used to layout the text.
+ mImpl->mVisualModel->mControlSize = size;
+ }
+
+ if( NO_OPERATION != ( ALIGN & operations ) )
+ {
+ // The laid-out lines.
+ Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
+
+ mImpl->mLayoutEngine.Align( size,
+ startIndex,
+ requestedNumberOfCharacters,
+ lines );