+ /**
+ * @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
+ {
+ // TODO: Check for multiline with \n, etc...
+
+ // Check if the logical position is the first or the last one of the text.
+ const bool isFirstPosition = 0u == logical;
+ const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
+
+ if( isFirstPosition && isLastPosition )
+ {
+ // There is zero characters. Get the default font.
+
+ FontId defaultFontId = 0u;
+ if( NULL == mFontDefaults )
+ {
+ defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
+ EMPTY_STRING );
+ }
+ else
+ {
+ defaultFontId = mFontDefaults->GetFontId( mFontClient );
+ }
+
+ Text::FontMetrics fontMetrics;
+ mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+
+ cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
+ cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
+
+ cursorInfo.primaryPosition.x = 0.f;
+ cursorInfo.primaryPosition.y = 0.f;
+
+ // Nothing else to do.
+ return;
+ }
+
+ // Get the previous logical index.
+ const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
+
+ // Decrease the logical index if it's the last one.
+ if( isLastPosition )
+ {
+ --logical;
+ }
+
+ // Get the direction of the character and the previous one.
+ const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
+
+ CharacterDirection isCurrentRightToLeft = false;
+ CharacterDirection isPreviousRightToLeft = false;
+ if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+ {
+ isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
+ isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
+ }
+
+ // Get the line where the character is laid-out.
+ const LineRun* modelLines = mVisualModel->mLines.Begin();
+
+ const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
+ const LineRun& line = *( modelLines + lineIndex );
+
+ // Get the paragraph's direction.
+ const CharacterDirection isRightToLeftParagraph = line.direction;
+
+ // Check whether there is an alternative position:
+
+ cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
+ ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
+
+ // Set the line height.
+ cursorInfo.lineHeight = line.ascender + -line.descender;
+
+ // Convert the cursor position into the glyph position.
+ CharacterIndex characterIndex = logical;
+ if( cursorInfo.isSecondaryCursor &&
+ ( isRightToLeftParagraph != isCurrentRightToLeft ) )
+ {
+ characterIndex = previousLogical;
+ }
+
+ const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
+ const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
+ const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
+
+ // Get the metrics for the group of glyphs.
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( currentGlyphIndex,
+ numberOfGlyphs,
+ glyphMetrics );
+
+ float interGlyphAdvance = 0.f;
+ if( !isLastPosition &&
+ ( numberOfCharacters > 1u ) )
+ {
+ const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
+ interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
+ }
+
+ // Get the glyph position and x bearing.
+ const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
+
+ // Set the cursor's height.
+ cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
+
+ // Set the position.
+ cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
+ cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
+
+ if( isLastPosition )
+ {
+ // The position of the cursor after the last character needs special
+ // care depending on its direction and the direction of the paragraph.
+
+ if( cursorInfo.isSecondaryCursor )
+ {
+ // Need to find the first character after the last character with the paragraph's direction.
+ // i.e l0 l1 l2 r0 r1 should find r0.
+
+ // TODO: check for more than one line!
+ characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
+ characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
+
+ const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
+ const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
+
+ const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
+
+ // Get the metrics for the group of glyphs.
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( glyphIndex,
+ numberOfGlyphs,
+ glyphMetrics );
+
+ cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );