+ void RepositionSelectionHandles( float visualX, float visualY )
+ {
+ // TODO - Find which word was selected
+
+ const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
+ const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
+
+ const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
+ const Vector<Vector2>::SizeType positionCount = positions.Count();
+
+ // Guard against glyphs which did not fit inside the layout
+ const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
+
+ if( count )
+ {
+ float primaryX = positions[0].x;
+ float secondaryX = positions[count-1].x + glyphs[count-1].width;
+
+ // TODO - multi-line selection
+ const Vector<LineRun>& lines = mVisualModel->mLines;
+ float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
+
+ mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
+ mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
+
+ mDecorator->ClearHighlights();
+ mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
+ }
+ }
+
+ void ChangeState( State newState )
+ {
+ if( mState != newState )
+ {
+ mState = newState;
+
+ if( INACTIVE == mState )
+ {
+ mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
+ mDecorator->StopCursorBlink();
+ mDecorator->SetGrabHandleActive( false );
+ mDecorator->SetSelectionActive( false );
+ mDecorator->SetPopupActive( false );
+ mDecoratorUpdated = true;
+ }
+ else if ( SELECTING == mState )
+ {
+ mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
+ mDecorator->StopCursorBlink();
+ mDecorator->SetGrabHandleActive( false );
+ mDecorator->SetSelectionActive( true );
+ mDecoratorUpdated = true;
+ }
+ else if( EDITING == mState )
+ {
+ mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+ if( mCursorBlinkEnabled )
+ {
+ mDecorator->StartCursorBlink();
+ }
+ if( mGrabHandleEnabled )
+ {
+ mDecorator->SetGrabHandleActive( true );
+ }
+ if( mGrabHandlePopupEnabled )
+ {
+ mDecorator->SetPopupActive( false );
+ }
+ mDecorator->SetSelectionActive( false );
+ mDecoratorUpdated = true;
+ }
+ else if( EDITING_WITH_POPUP == mState )
+ {
+ mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+ if( mCursorBlinkEnabled )
+ {
+ mDecorator->StartCursorBlink();
+ }
+ if( mGrabHandleEnabled )
+ {
+ mDecorator->SetGrabHandleActive( true );
+ }
+ if( mGrabHandlePopupEnabled )
+ {
+ mDecorator->SetPopupActive( true );
+ }
+ mDecorator->SetSelectionActive( false );
+ mDecoratorUpdated = true;
+ }
+ }
+ }
+
+ LineIndex GetClosestLine( float y ) const
+ {
+ float totalHeight = 0.f;
+ LineIndex lineIndex = 0u;
+
+ const Vector<LineRun>& lines = mVisualModel->mLines;
+ for( LineIndex endLine = lines.Count();
+ lineIndex < endLine;
+ ++lineIndex )
+ {
+ const LineRun& lineRun = lines[lineIndex];
+ totalHeight += lineRun.ascender + -lineRun.descender;
+ if( y < totalHeight )
+ {
+ return lineIndex;
+ }
+ }
+
+ return lineIndex-1;
+ }
+
+ /**
+ * @brief Retrieves the cursor's logical position for a given touch point x,y
+ *
+ * @param[in] visualX The touch point x.
+ * @param[in] visualY The touch point y.
+ *
+ * @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.
+ */
+ CharacterIndex GetClosestCursorIndex( float visualX,
+ float visualY ) const
+ {
+ CharacterIndex logicalIndex = 0u;
+
+ const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
+ const Length numberOfLines = mVisualModel->mLines.Count();
+ if( 0 == numberOfGlyphs ||
+ 0 == numberOfLines )
+ {
+ return logicalIndex;
+ }
+
+ // Transform to visual model coords
+ visualX -= mScrollPosition.x;
+ visualY -= mScrollPosition.y;
+
+ // Find which line is closest
+ const LineIndex lineIndex = GetClosestLine( visualY );
+ const LineRun& line = mVisualModel->mLines[lineIndex];
+
+ // Get the positions of the glyphs.
+ const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
+ const Vector2* const positionsBuffer = positions.Begin();
+
+ // Get the visual to logical conversion tables.
+ const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
+ const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
+
+ // Get the character to glyph conversion table.
+ const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+
+ // Get the glyphs per character table.
+ const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+
+ // If the vector is void, there is no right to left characters.
+ const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
+
+ const CharacterIndex startCharacter = line.characterRun.characterIndex;
+ const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
+ DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
+
+ // Whether there is a hit on a glyph.
+ bool matched = false;
+
+ // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
+ CharacterIndex visualIndex = startCharacter;
+ for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
+ {
+ // The character in logical order.
+ const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
+
+ // The first glyph for that character in logical order.
+ const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
+
+ // The number of glyphs for that character
+ const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
+
+ // Get the metrics for the group of glyphs.
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( glyphLogicalOrderIndex,
+ numberOfGlyphs,
+ glyphMetrics );
+
+ const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
+
+ const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
+
+ if( visualX < glyphX )
+ {
+ matched = true;
+ break;
+ }
+ }
+
+ // Return the logical position of the cursor in characters.
+
+ if( !matched )
+ {
+ visualIndex = endCharacter;
+ }
+
+ return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+ }
+
+ /**
+ * @brief Calculates the cursor's position for a given character index in the logical order.
+ *
+ * It retrieves as well the line's height and the cursor's height and
+ * if there is a valid alternative cursor, its position and height.
+ *
+ * @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.
+ * @param[out] cursorInfo The line's height, the cursor's height, the cursor's position and whether there is an alternative cursor.
+ */
+ void GetCursorPosition( CharacterIndex logical,
+ CursorInfo& cursorInfo ) const