Here comes another bunch of methods for TextLayout. Again, this is based on the specs:
http://java.sun.com/j2se/1.4.2/docs/guide/2d/spec/j2d-fonts.html and is backed up by the tests in the Harmony testsuite. /Roman
Index: java/awt/font/TextLayout.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/awt/font/TextLayout.java,v retrieving revision 1.20 diff -u -1 -5 -r1.20 TextLayout.java --- java/awt/font/TextLayout.java 27 Nov 2006 11:17:21 -0000 1.20 +++ java/awt/font/TextLayout.java 27 Nov 2006 13:10:29 -0000 @@ -588,50 +588,97 @@ info[0] = (float) glyphRect.getMaxY(); } else { if (leading) info[0] = (float) glyphRect.getMinX(); else info[0] = (float) glyphRect.getMaxX(); } info[0] += run.location; info[1] = run.font.getItalicAngle(); } return info; } - public Shape getCaretShape (TextHitInfo hit) + public Shape getCaretShape(TextHitInfo hit) { - return getCaretShape( hit, getBounds() ); + return getCaretShape(hit, getBounds()); } - public Shape getCaretShape (TextHitInfo hit, Rectangle2D bounds) - throws NotImplementedException + public Shape getCaretShape(TextHitInfo hit, Rectangle2D bounds) { - throw new Error ("not implemented"); + // TODO: Handle vertical shapes somehow. + float[] info = getCaretInfo(hit); + float x1 = info[0]; + float y1 = (float) bounds.getMinY(); + float x2 = info[0]; + float y2 = (float) bounds.getMaxY(); + if (info[1] != 0) + { + // Shift x1 and x2 according to the slope. + x1 -= y1 * info[1]; + x2 -= y2 * info[1]; + } + GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 2); + path.moveTo(x1, y1); + path.lineTo(x2, y2); + return path; } - public Shape[] getCaretShapes (int offset) + public Shape[] getCaretShapes(int offset) { - return getCaretShapes( offset, getBounds() ); + return getCaretShapes(offset, getNaturalBounds()); } - public Shape[] getCaretShapes (int offset, Rectangle2D bounds) - throws NotImplementedException + public Shape[] getCaretShapes(int offset, Rectangle2D bounds) { - throw new Error ("not implemented"); + return getCaretShapes(offset, bounds, DEFAULT_CARET_POLICY); + } + + public Shape[] getCaretShapes(int offset, Rectangle2D bounds, + CaretPolicy policy) + { + // The RI returns a 2-size array even when there's only one + // shape in it. + Shape[] carets = new Shape[2]; + TextHitInfo hit1 = TextHitInfo.afterOffset(offset); + int caretHit1 = hitToCaret(hit1); + TextHitInfo hit2 = hit1.getOtherHit(); + int caretHit2 = hitToCaret(hit2); + if (caretHit1 == caretHit2) + { + carets[0] = getCaretShape(hit1); + carets[1] = null; // The RI returns null in this seldom case. + } + else + { + Shape caret1 = getCaretShape(hit1); + Shape caret2 = getCaretShape(hit2); + TextHitInfo strong = policy.getStrongCaret(hit1, hit2, this); + if (strong == hit1) + { + carets[0] = caret1; + carets[1] = caret2; + } + else + { + carets[0] = caret2; + carets[1] = caret1; + } + } + return carets; } public int getCharacterCount () { return length; } public byte getCharacterLevel (int index) { byte level; if( bidi == null ) level = 0; else level = (byte) bidi.getLevelAt(index); return level;