+void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
+{
+ if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
+ {
+ // Nothing to select if handles are in the same place.
+ selectedText="";
+ 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;
+
+ // Validate the start and end selection points
+ 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
+ {
+ // Delete text between handles
+ Vector<Character>& currentText = mLogicalModel->mText;
+
+ Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
+ Vector<Character>::Iterator last = first + lengthOfSelectedText;
+ currentText.Erase( first, last );
+
+ // Scroll after delete.
+ mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
+ mEventData->mScrollAfterDelete = true;
+ }
+ // Udpade the cursor position and the decorator.
+ // Scroll after the position is updated if is not scrolling after delete.
+ mEventData->mUpdateCursorPosition = true;
+ mEventData->mScrollAfterUpdatePosition = !mEventData->mScrollAfterDelete;
+ mEventData->mDecoratorUpdated = true;
+ }
+}
+
+void Controller::Impl::ShowClipboard()
+{
+ if ( mClipboard )
+ {
+ mClipboard.ShowClipboard();
+ }
+}
+
+void Controller::Impl::HideClipboard()
+{
+ if ( mClipboard )
+ {
+ mClipboard.HideClipboard();
+ }
+}
+
+bool Controller::Impl::CopyStringToClipboard( std::string& source )
+{
+ //Send string to clipboard
+ return ( mClipboard && mClipboard.SetItem( source ) );
+}
+
+void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
+{
+ std::string selectedText;
+ RetrieveSelection( selectedText, deleteAfterSending );
+ CopyStringToClipboard( selectedText );
+ ChangeState( EventData::EDITING );
+}
+
+void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
+{
+ if ( mClipboard )
+ {
+ retreivedString = mClipboard.GetItem( itemIndex );
+ }
+}
+
+void Controller::Impl::RepositionSelectionHandles()
+{
+ CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
+ CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
+
+ if( selectionStart == selectionEnd )
+ {
+ // Nothing to select if handles are in the same place.
+ return;
+ }
+
+ mEventData->mDecorator->ClearHighlights();
+
+ const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+ const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+ const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
+ const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+ const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+ const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
+ const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
+
+ // TODO: Better algorithm to create the highlight box.
+ // TODO: Multi-line.
+
+ // Get the height of the line.
+ const Vector<LineRun>& lines = mVisualModel->mLines;
+ const LineRun& firstLine = *lines.Begin();
+ const float height = firstLine.ascender + -firstLine.descender;
+
+ const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
+ const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
+ const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
+
+ // Swap the indices if the start is greater than the end.
+ const bool indicesSwapped = selectionStart > selectionEnd;
+
+ // Tell the decorator to flip the selection handles if needed.
+ mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
+
+ if( indicesSwapped )
+ {
+ std::swap( selectionStart, selectionEnd );
+ }
+
+ // Get the indices to the first and last selected glyphs.
+ const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
+ const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+ const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
+ const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
+
+ // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+ const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
+ bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
+
+ // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+ const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
+ bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
+
+ const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+
+ // Traverse the glyphs.
+ for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+ {
+ const GlyphInfo& glyph = *( glyphsBuffer + index );
+ const Vector2& position = *( positionsBuffer + index );
+
+ if( splitStartGlyph )
+ {
+ // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+ const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
+ const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
+ // Get the direction of the character.
+ CharacterDirection isCurrentRightToLeft = false;
+ if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+ {
+ isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
+ }
+
+ // The end point could be in the middle of the ligature.
+ // Calculate the number of characters selected.
+ const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
+
+ const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
+
+ mEventData->mDecorator->AddHighlight( xPosition,
+ offset.y,
+ xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
+ offset.y + height );
+
+ splitStartGlyph = false;
+ continue;
+ }
+
+ if( splitEndGlyph && ( index == glyphEnd ) )
+ {
+ // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+ const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
+ const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
+ // Get the direction of the character.
+ CharacterDirection isCurrentRightToLeft = false;
+ if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+ {
+ isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
+ }
+
+ const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
+
+ const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
+ mEventData->mDecorator->AddHighlight( xPosition,
+ offset.y,
+ xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
+ offset.y + height );
+
+ splitEndGlyph = false;
+ continue;
+ }
+
+ const float xPosition = position.x - glyph.xBearing + offset.x;
+ mEventData->mDecorator->AddHighlight( xPosition,
+ offset.y,
+ xPosition + glyph.advance,
+ offset.y + height );
+ }
+
+ CursorInfo primaryCursorInfo;
+ GetCursorPosition( mEventData->mLeftSelectionPosition,
+ primaryCursorInfo );
+
+ CursorInfo secondaryCursorInfo;
+ GetCursorPosition( mEventData->mRightSelectionPosition,
+ secondaryCursorInfo );
+
+ 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( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.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;
+
+ // Set the flag to update the decorator.
+ mEventData->mDecoratorUpdated = true;
+}
+