8156217: Selected text is shifted on HiDPI display

Reviewed-by: prr, serb
This commit is contained in:
Alexander Scherbatiy 2016-10-20 12:18:15 +03:00
parent 4a4be07df3
commit e8c27e9a2f
16 changed files with 1936 additions and 212 deletions

View File

@ -46,7 +46,11 @@ public abstract class TextUI extends ComponentUI
* @return the coordinates as a {@code Rectangle}
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
@Deprecated(since = "9")
public abstract Rectangle modelToView(JTextComponent t, int pos) throws BadLocationException;
/**
@ -59,7 +63,11 @@ public abstract class TextUI extends ComponentUI
* @return the coordinates as a {@code Rectangle}
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
@Deprecated(since = "9")
public abstract Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException;
/**
@ -92,7 +100,11 @@ public abstract class TextUI extends ComponentUI
* should be in the same coordinate system as the mouse
* events.
* @return the offset from the start of the document >= 0
*
* @deprecated replaced by
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
*/
@Deprecated(since = "9")
public abstract int viewToModel(JTextComponent t, Point pt);
/**
@ -110,7 +122,11 @@ public abstract class TextUI extends ComponentUI
*
* @return the location within the model that best represents the
* given point in the view >= 0
*
* @deprecated replaced by
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
*/
@Deprecated(since = "9")
public abstract int viewToModel(JTextComponent t, Point pt,
Position.Bias[] biasReturn);
@ -222,7 +238,11 @@ public abstract class TextUI extends ComponentUI
* @return a {@code String} containing the tooltip
* @see javax.swing.text.JTextComponent#getToolTipText
* @since 1.4
*
* @deprecated replaced by
* {@link #getToolTipText2D(JTextComponent, Point2D)}
*/
@Deprecated(since = "9")
public String getToolTipText(JTextComponent t, Point pt) {
return null;
}

View File

@ -28,6 +28,8 @@ import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.im.InputContext;
import java.beans.*;
import java.io.*;
@ -1047,7 +1049,12 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see TextUI#modelToView
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
@Deprecated(since = "9")
@Override
public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
return modelToView(tc, pos, Position.Bias.Forward);
}
@ -1064,8 +1071,30 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see TextUI#modelToView
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
@Deprecated(since = "9")
@Override
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias)
throws BadLocationException
{
return (Rectangle) modelToView(tc, pos, bias, false);
}
@Override
public Rectangle2D modelToView2D(JTextComponent tc, int pos,
Position.Bias bias)
throws BadLocationException
{
return modelToView(tc, pos, bias, true);
}
private Rectangle2D modelToView(JTextComponent tc, int pos,
Position.Bias bias, boolean useFPAPI)
throws BadLocationException
{
Document doc = editor.getDocument();
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readLock();
@ -1076,7 +1105,7 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
rootView.setSize(alloc.width, alloc.height);
Shape s = rootView.modelToView(pos, alloc, bias);
if (s != null) {
return s.getBounds();
return useFPAPI ? s.getBounds2D() : s.getBounds();
}
}
} finally {
@ -1099,7 +1128,12 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
* @return the offset from the start of the document >= 0,
* -1 if not painted
* @see TextUI#viewToModel
*
* @deprecated replaced by
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
*/
@Deprecated(since = "9")
@Override
public int viewToModel(JTextComponent tc, Point pt) {
return viewToModel(tc, pt, discardBias);
}
@ -1116,9 +1150,25 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
* @return the offset from the start of the document >= 0,
* -1 if the component doesn't yet have a positive size.
* @see TextUI#viewToModel
*
* @deprecated replaced by
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
*/
@Deprecated(since = "9")
@Override
public int viewToModel(JTextComponent tc, Point pt,
Position.Bias[] biasReturn) {
return viewToModel(tc, pt.x, pt.y, biasReturn);
}
@Override
public int viewToModel2D(JTextComponent tc, Point2D pt,
Position.Bias[] biasReturn) {
return viewToModel(tc, (float) pt.getX(), (float) pt.getY(), biasReturn);
}
private int viewToModel(JTextComponent tc, float x, float y,
Position.Bias[] biasReturn) {
int offs = -1;
Document doc = editor.getDocument();
if (doc instanceof AbstractDocument) {
@ -1128,7 +1178,7 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
Rectangle alloc = getVisibleEditorRect();
if (alloc != null) {
rootView.setSize(alloc.width, alloc.height);
offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
offs = rootView.viewToModel(x, y, alloc, biasReturn);
}
} finally {
if (doc instanceof AbstractDocument) {

View File

@ -38,6 +38,8 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.accessibility.Accessible;
/**
@ -97,7 +99,11 @@ public class MultiTextUI extends TextUI {
*
* @return the value obtained from the first UI, which is
* the UI obtained from the default <code>LookAndFeel</code>
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
@Deprecated(since = "9")
public Rectangle modelToView(JTextComponent a, int b)
throws BadLocationException {
Rectangle returnValue =
@ -113,7 +119,12 @@ public class MultiTextUI extends TextUI {
*
* @return the value obtained from the first UI, which is
* the UI obtained from the default <code>LookAndFeel</code>
*
* @deprecated replaced by
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
*/
@Deprecated(since = "9")
@Override
public Rectangle modelToView(JTextComponent a, int b, Position.Bias c)
throws BadLocationException {
Rectangle returnValue =
@ -124,12 +135,24 @@ public class MultiTextUI extends TextUI {
return returnValue;
}
@Override
public Rectangle2D modelToView2D(JTextComponent a, int b, Position.Bias c) throws BadLocationException {
Rectangle2D returnValue =
((TextUI) (uis.elementAt(0))).modelToView2D(a,b,c);
for (int i = 1; i < uis.size(); i++) {
((TextUI) (uis.elementAt(i))).modelToView2D(a,b,c);
}
return returnValue;
}
/**
* Invokes the <code>viewToModel</code> method on each UI handled by this object.
*
* @return the value obtained from the first UI, which is
* the UI obtained from the default <code>LookAndFeel</code>
*/
@Deprecated(since = "9")
@Override
public int viewToModel(JTextComponent a, Point b) {
int returnValue =
((TextUI) (uis.elementAt(0))).viewToModel(a,b);
@ -145,6 +168,8 @@ public class MultiTextUI extends TextUI {
* @return the value obtained from the first UI, which is
* the UI obtained from the default <code>LookAndFeel</code>
*/
@Deprecated(since = "9")
@Override
public int viewToModel(JTextComponent a, Point b, Position.Bias[] c) {
int returnValue =
((TextUI) (uis.elementAt(0))).viewToModel(a,b,c);
@ -154,6 +179,16 @@ public class MultiTextUI extends TextUI {
return returnValue;
}
@Override
public int viewToModel2D(JTextComponent a, Point2D b, Position.Bias[] c) {
int returnValue =
((TextUI) (uis.elementAt(0))).viewToModel2D(a,b,c);
for (int i = 1; i < uis.size(); i++) {
((TextUI) (uis.elementAt(i))).viewToModel2D(a,b,c);
}
return returnValue;
}
/**
* Invokes the <code>getNextVisualPositionFrom</code> method on each UI handled by this object.
*

View File

@ -98,26 +98,27 @@ class GlyphPainter1 extends GlyphView.GlyphPainter {
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
// determine the x coordinate to render the glyphs
int x = alloc.x;
float x = alloc.x;
int p = v.getStartOffset();
int[] justificationData = getJustificationData(v);
if (p != p0) {
text = v.getText(p, p0);
int width = Utilities.getTabbedTextWidth(v, text, metrics, x, expander, p,
justificationData);
float width = Utilities.getTabbedTextWidth(v, text, metrics, x,
expander, p,
justificationData);
x += width;
SegmentCache.releaseSharedSegment(text);
}
// determine the y coordinate to render the glyphs
int y = alloc.y + metrics.getHeight() - metrics.getDescent();
float y = alloc.y + metrics.getHeight() - metrics.getDescent();
// render the glyphs
text = v.getText(p0, p1);
g.setFont(metrics.getFont());
Utilities.drawTabbedText(v, text, x, y, g, expander,p0,
justificationData);
justificationData, true);
SegmentCache.releaseSharedSegment(text);
}
@ -210,9 +211,9 @@ class GlyphPainter1 extends GlyphView.GlyphPainter {
TabExpander expander = v.getTabExpander();
Segment s = v.getText(p0, v.getEndOffset());
int[] justificationData = getJustificationData(v);
int index = Utilities.getTabbedTextOffset(v, s, metrics, (int)x, (int)(x+len),
int index = Utilities.getTabbedTextOffset(v, s, metrics, x, (x+len),
expander, p0, false,
justificationData);
justificationData, true);
SegmentCache.releaseSharedSegment(s);
int p1 = p0 + index;
return p1;

View File

@ -145,8 +145,9 @@ class GlyphPainter2 extends GlyphView.GlyphPainter {
// vertical at the baseline, should use slope and check if glyphs
// are being rendered vertically.
alloc.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight());
return alloc;
Rectangle2D rect = new Rectangle2D.Float();
rect.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight());
return rect;
}
/**

View File

@ -49,6 +49,8 @@ import java.awt.im.InputContext;
import java.awt.im.InputMethodRequests;
import java.awt.font.TextHitInfo;
import java.awt.font.TextAttribute;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
@ -1370,11 +1372,37 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see TextUI#modelToView
*
* @deprecated replaced by
* {@link #modelToView2D(int)}
*/
@Deprecated(since = "9")
public Rectangle modelToView(int pos) throws BadLocationException {
return getUI().modelToView(this, pos);
}
/**
* Converts the given location in the model to a place in
* the view coordinate system.
* The component must have a positive size for
* this translation to be computed (i.e. layout cannot
* be computed until the component has been sized). The
* component does not have to be visible or painted.
*
* @param pos the position {@code >= 0}
* @return the coordinates as a rectangle, with (r.x, r.y) as the location
* in the coordinate system, or null if the component does
* not yet have a positive size.
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see TextUI#modelToView2D
*
* @since 9
*/
public Rectangle2D modelToView2D(int pos) throws BadLocationException {
return getUI().modelToView2D(this, pos, Position.Bias.Forward);
}
/**
* Converts the given place in the view coordinate system
* to the nearest representative location in the model.
@ -1388,11 +1416,35 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
* or -1 if the component does not yet have a positive
* size.
* @see TextUI#viewToModel
*
* @deprecated replaced by
* {@link #viewToModel2D(Point2D)}
*/
@Deprecated(since = "9")
public int viewToModel(Point pt) {
return getUI().viewToModel(this, pt);
}
/**
* Converts the given place in the view coordinate system
* to the nearest representative location in the model.
* The component must have a positive size for
* this translation to be computed (i.e. layout cannot
* be computed until the component has been sized). The
* component does not have to be visible or painted.
*
* @param pt the location in the view to translate
* @return the offset {@code >= 0} from the start of the document,
* or {@code -1} if the component does not yet have a positive
* size.
* @see TextUI#viewToModel2D
*
* @since 9
*/
public int viewToModel2D(Point2D pt) {
return getUI().viewToModel2D(this, pt, new Position.Bias[1]);
}
/**
* Transfers the currently selected range in the associated
* text model to the system clipboard, removing the contents

View File

@ -27,6 +27,7 @@ package javax.swing.text;
import java.util.Arrays;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import javax.swing.event.*;
import javax.swing.SizeRequirements;
@ -888,10 +889,9 @@ public class ParagraphView extends FlowView implements TabExpander {
int height = r.height;
int y = r.y;
Shape loc = super.modelToView(pos, a, b);
r = loc.getBounds();
r.height = height;
r.y = y;
return r;
Rectangle2D bounds = loc.getBounds2D();
bounds.setRect(bounds.getX(), y, bounds.getWidth(), height);
return bounds;
}
/**

View File

@ -26,7 +26,11 @@ package javax.swing.text;
import sun.swing.SwingUtilities2;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.JPasswordField;
import static javax.swing.text.PlainView.isFPMethodOverriden;
/**
* Implements a View suitable for use in JPasswordField
@ -61,15 +65,40 @@ public class PasswordView extends FieldView {
* @param p1 the ending offset in the model &gt;= p0
* @return the X location of the end of the range &gt;= 0
* @exception BadLocationException if p0 or p1 are out of range
*
* @deprecated replaced by
* {@link #drawUnselectedText(Graphics2D, float, float, int, int)}
*/
@Deprecated(since = "9")
@Override
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
return (int) drawUnselectedTextImpl(g, x, y, p0, p1, false);
}
@Override
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1)
throws BadLocationException
{
return drawUnselectedTextImpl(g, x, y, p0, p1, true);
}
private float drawUnselectedTextImpl(Graphics g, float x, float y,
int p0, int p1,
boolean useFPAPI)
throws BadLocationException
{
Container c = getContainer();
if (c instanceof JPasswordField) {
JPasswordField f = (JPasswordField) c;
if (! f.echoCharIsSet()) {
return super.drawUnselectedText(g, x, y, p0, p1);
if (!f.echoCharIsSet()) {
boolean useDrawUnselectedFPAPI = useFPAPI
&& drawUnselectedTextOverridden
&& g instanceof Graphics2D;
return (useDrawUnselectedFPAPI )
? super.drawUnselectedText((Graphics2D) g, x, y, p0, p1)
: super.drawUnselectedText(g, (int) x, (int) y, p0, p1);
}
if (f.isEnabled()) {
g.setColor(f.getForeground());
@ -79,8 +108,13 @@ public class PasswordView extends FieldView {
}
char echoChar = f.getEchoChar();
int n = p1 - p0;
boolean useEchoCharFPAPI = useFPAPI
&& drawEchoCharacterOverridden
&& g instanceof Graphics2D;
for (int i = 0; i < n; i++) {
x = drawEchoCharacter(g, x, y, echoChar);
x = (useEchoCharFPAPI)
? drawEchoCharacter((Graphics2D) g, x, y, echoChar)
: drawEchoCharacter(g, (int) x, (int) y, echoChar);
}
}
return x;
@ -100,20 +134,50 @@ public class PasswordView extends FieldView {
* @param p1 the ending offset in the model &gt;= p0
* @return the X location of the end of the range &gt;= 0
* @exception BadLocationException if p0 or p1 are out of range
*
* @deprecated replaced by
* {@link #drawSelectedText(Graphics2D, float, float, int, int)}
*/
@Deprecated(since = "9")
@Override
protected int drawSelectedText(Graphics g, int x,
int y, int p0, int p1) throws BadLocationException {
return (int) drawSelectedTextImpl(g, x, y, p0, p1, false);
}
@Override
protected float drawSelectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException
{
return drawSelectedTextImpl(g, x, y, p0, p1, true);
}
private float drawSelectedTextImpl(Graphics g, float x, float y,
int p0, int p1,
boolean useFPAPI)
throws BadLocationException {
g.setColor(selected);
Container c = getContainer();
if (c instanceof JPasswordField) {
JPasswordField f = (JPasswordField) c;
if (! f.echoCharIsSet()) {
return super.drawSelectedText(g, x, y, p0, p1);
if (!f.echoCharIsSet()) {
boolean useDrawUnselectedFPAPI = useFPAPI
&& drawSelectedTextOverridden
&& g instanceof Graphics2D;
return (useFPAPI)
? super.drawSelectedText((Graphics2D) g, x, y, p0, p1)
: super.drawSelectedText(g, (int) x, (int) y, p0, p1);
}
char echoChar = f.getEchoChar();
int n = p1 - p0;
boolean useEchoCharFPAPI = useFPAPI
&& drawEchoCharacterOverridden
&& g instanceof Graphics2D;
for (int i = 0; i < n; i++) {
x = drawEchoCharacter(g, x, y, echoChar);
x = (useEchoCharFPAPI)
? drawEchoCharacter((Graphics2D) g, x, y, echoChar)
: drawEchoCharacter(g, (int) x, (int) y, echoChar);
}
}
return x;
@ -130,12 +194,13 @@ public class PasswordView extends FieldView {
* @param y the starting Y coordinate &gt;= 0
* @param c the echo character
* @return the updated X position &gt;= 0
*
* @deprecated replaced by
* {@link #drawEchoCharacter(Graphics2D, float, float, char)}
*/
@Deprecated(since = "9")
protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
ONE[0] = c;
SwingUtilities2.drawChars(Utilities.getJComponent(this),
g, ONE, 0, 1, x, y);
return x + g.getFontMetrics().charWidth(c);
return (int) drawEchoCharacterImpl(g, x, y, c, false);
}
/**
@ -144,18 +209,29 @@ public class PasswordView extends FieldView {
* object is set to the appropriate foreground color for selected
* or unselected text.
*
* @implSpec This implementation calls
* {@link #drawEchoCharacter(Graphics, int, int, char)
* drawEchoCharacter((Graphics) g, (int) x, (int) y, c)}.
*
* @param g the graphics context
* @param x the starting X coordinate {@code >= 0}
* @param y the starting Y coordinate {@code >= 0}
* @param c the echo character
* @return the updated X position {@code >= 0}
*
* @since 9
*/
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
return drawEchoCharacter((Graphics) g, (int) x, (int) y, c);
return drawEchoCharacterImpl(g, x, y, c, true);
}
private float drawEchoCharacterImpl(Graphics g, float x, float y,
char c, boolean useFPAPI) {
ONE[0] = c;
SwingUtilities2.drawChars(Utilities.getJComponent(this),
g, ONE, 0, 1, x, y);
if (useFPAPI) {
return x + g.getFontMetrics().charWidth(c);
} else {
FontRenderContext frc = g.getFontMetrics().getFontRenderContext();
return x + (float) g.getFont().getStringBounds(ONE, 0, 1, frc).getWidth();
}
}
/**
@ -253,4 +329,23 @@ public class PasswordView extends FieldView {
}
static char[] ONE = new char[1];
private final boolean drawEchoCharacterOverridden;
{
final Class<?> CLS = getClass();
final Class<?> INT = Integer.TYPE;
final Class<?> FP = Float.TYPE;
final Class<?> CHAR = Character.TYPE;
drawEchoCharacterOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {Graphics.class, INT, INT, CHAR};
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, CHAR};
return isFPMethodOverriden("drawEchoCharacter", CLS, intTypes, fpTypes);
}
});
}
}

View File

@ -24,11 +24,14 @@
*/
package javax.swing.text;
import java.util.Vector;
import java.util.Properties;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Objects;
import javax.swing.event.*;
import java.lang.reflect.Module;
/**
* Implements View interface for a simple multi-line text view
@ -60,17 +63,6 @@ public class PlainView extends View implements TabExpander {
return size;
}
/**
* Returns the tab size set for the document, defaulting to 8.
*
* @implSpec This implementation calls {@link #getTabSize() getTabSize()}.
*
* @return the tab size
*/
protected float getFractionalTabSize() {
return getTabSize();
}
/**
* Renders a line of text, suppressing whitespace at the end
* and expanding any tabs. This is implemented to make calls
@ -84,8 +76,16 @@ public class PlainView extends View implements TabExpander {
* @param y the starting Y position &gt;= 0
* @see #drawUnselectedText
* @see #drawSelectedText
*
* @deprecated replaced by
* {@link #drawLine(int, Graphics2D, float, float)}
*/
@Deprecated(since = "9")
protected void drawLine(int lineIndex, Graphics g, int x, int y) {
drawLineImpl(lineIndex, g, x, y);
}
private void drawLineImpl(int lineIndex, Graphics g, float x, float y) {
Element line = getElement().getElement(lineIndex);
Element elem;
@ -112,22 +112,23 @@ public class PlainView extends View implements TabExpander {
* {@code drawSelectedText} so that the way selected and
* unselected text are rendered can be customized.
*
* @implSpec This implementation calls
* {@link #drawLine(int, Graphics, int, int)
* drawLine(lineIndex, (Graphics)g, (int) x, (int) y)}.
*
* @param lineIndex the line to draw {@code >= 0}
* @param g the {@code Graphics} context
* @param x the starting X position {@code >= 0}
* @param y the starting Y position {@code >= 0}
* @see #drawUnselectedText
* @see #drawSelectedText
*
* @since 9
*/
protected void drawLine(int lineIndex, Graphics2D g, float x, float y) {
drawLine(lineIndex, (Graphics)g, (int) x, (int) y);
drawLineImpl(lineIndex, g, x, y);
}
private int drawElement(int lineIndex, Element elem, Graphics g, int x, int y) throws BadLocationException {
private float drawElement(int lineIndex, Element elem, Graphics g,
float x, float y)
throws BadLocationException
{
int p0 = elem.getStartOffset();
int p1 = elem.getEndOffset();
p1 = Math.min(getDocument().getLength(), p1);
@ -144,23 +145,23 @@ public class PlainView extends View implements TabExpander {
} else {
if (sel0 == sel1 || selected == unselected) {
// no selection, or it is invisible
x = drawUnselectedText(g, x, y, p0, p1);
x = callDrawUnselectedText(g, x, y, p0, p1);
} else if ((p0 >= sel0 && p0 <= sel1) && (p1 >= sel0 && p1 <= sel1)) {
x = drawSelectedText(g, x, y, p0, p1);
x = callDrawSelectedText(g, x, y, p0, p1);
} else if (sel0 >= p0 && sel0 <= p1) {
if (sel1 >= p0 && sel1 <= p1) {
x = drawUnselectedText(g, x, y, p0, sel0);
x = drawSelectedText(g, x, y, sel0, sel1);
x = drawUnselectedText(g, x, y, sel1, p1);
x = callDrawUnselectedText(g, x, y, p0, sel0);
x = callDrawSelectedText(g, x, y, sel0, sel1);
x = callDrawUnselectedText(g, x, y, sel1, p1);
} else {
x = drawUnselectedText(g, x, y, p0, sel0);
x = drawSelectedText(g, x, y, sel0, p1);
x = callDrawUnselectedText(g, x, y, p0, sel0);
x = callDrawSelectedText(g, x, y, sel0, p1);
}
} else if (sel1 >= p0 && sel1 <= p1) {
x = drawSelectedText(g, x, y, p0, sel1);
x = drawUnselectedText(g, x, y, sel1, p1);
x = callDrawSelectedText(g, x, y, p0, sel1);
x = callDrawUnselectedText(g, x, y, sel1, p1);
} else {
x = drawUnselectedText(g, x, y, p0, p1);
x = callDrawUnselectedText(g, x, y, p0, p1);
}
}
@ -178,14 +179,36 @@ public class PlainView extends View implements TabExpander {
* @param p1 the ending position in the model &gt;= 0
* @return the X location of the end of the range &gt;= 0
* @exception BadLocationException if the range is invalid
*
* @deprecated replaced by
* {@link #drawUnselectedText(Graphics2D, float, float, int, int)}
*/
@Deprecated(since = "9")
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
return (int) drawUnselectedTextImpl(g, x, y, p0, p1, false);
}
private float callDrawUnselectedText(Graphics g, float x, float y,
int p0, int p1)
throws BadLocationException
{
return drawUnselectedTextOverridden && (g instanceof Graphics2D)
? drawUnselectedText((Graphics2D) g, x, y, p0, p1)
: drawUnselectedText(g, (int) x, (int) y, p0, p1);
}
private float drawUnselectedTextImpl(Graphics g, float x, float y,
int p0, int p1,
boolean useFPAPI)
throws BadLocationException
{
g.setColor(unselected);
Document doc = getDocument();
Segment s = SegmentCache.getSharedSegment();
doc.getText(p0, p1 - p0, s);
int ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0);
float ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0, null,
useFPAPI);
SegmentCache.releaseSharedSegment(s);
return ret;
}
@ -194,10 +217,6 @@ public class PlainView extends View implements TabExpander {
* Renders the given range in the model as normal unselected
* text. Uses the foreground or disabled color to render the text.
*
* @implSpec This implementation calls
* {@link #drawUnselectedText(Graphics, int, int, int, int)
* drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
*
* @param g the graphics context
* @param x the starting X coordinate {@code >= 0}
* @param y the starting Y coordinate {@code >= 0}
@ -205,10 +224,12 @@ public class PlainView extends View implements TabExpander {
* @param p1 the ending position in the model {@code >= 0}
* @return the X location of the end of the range {@code >= 0}
* @exception BadLocationException if the range is invalid
*
* @since 9
*/
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
return drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1);
return drawUnselectedTextImpl(g, x, y, p0, p1, true);
}
/**
@ -224,14 +245,38 @@ public class PlainView extends View implements TabExpander {
* @param p1 the ending position in the model &gt;= 0
* @return the location of the end of the range
* @exception BadLocationException if the range is invalid
*
* @deprecated replaced by
* {@link #drawSelectedText(Graphics2D, float, float, int, int)}
*/
@Deprecated(since = "9")
protected int drawSelectedText(Graphics g, int x,
int y, int p0, int p1) throws BadLocationException {
int y, int p0, int p1)
throws BadLocationException
{
return (int) drawSelectedTextImpl(g, x, y, p0, p1, false);
}
float callDrawSelectedText(Graphics g, float x, float y,
int p0, int p1)
throws BadLocationException
{
return drawSelectedTextOverridden && g instanceof Graphics2D
? drawSelectedText((Graphics2D) g, x, y, p0, p1)
: drawSelectedText(g, (int) x, (int) y, p0, p1);
}
private float drawSelectedTextImpl(Graphics g, float x, float y,
int p0, int p1,
boolean useFPAPI)
throws BadLocationException
{
g.setColor(selected);
Document doc = getDocument();
Segment s = SegmentCache.getSharedSegment();
doc.getText(p0, p1 - p0, s);
int ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0);
float ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0, null,
useFPAPI);
SegmentCache.releaseSharedSegment(s);
return ret;
}
@ -242,10 +287,6 @@ public class PlainView extends View implements TabExpander {
* the hosting component. It assumes the highlighter will render
* the selected background.
*
* @implSpec This implementation calls
* {@link #drawSelectedText(Graphics, int, int, int, int)
* drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
*
* @param g the graphics context
* @param x the starting X coordinate {@code >= 0}
* @param y the starting Y coordinate {@code >= 0}
@ -253,11 +294,12 @@ public class PlainView extends View implements TabExpander {
* @param p1 the ending position in the model {@code >= 0}
* @return the location of the end of the range
* @exception BadLocationException if the range is invalid
*
* @since 9
*/
protected float drawSelectedText(Graphics2D g, float x,
float y, int p0, int p1) throws BadLocationException {
return drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1);
return drawSelectedTextImpl(g, x, y, p0, p1, true);
}
/**
@ -287,7 +329,13 @@ public class PlainView extends View implements TabExpander {
// The font changed, we need to recalculate the
// longest line.
calculateLongestLine();
tabSize = getTabSize() * metrics.charWidth('m');
if (useFloatingPointAPI) {
FontRenderContext frc = metrics.getFontRenderContext();
float tabWidth = (float) font.getStringBounds("m", frc).getWidth();
tabSize = getTabSize() * tabWidth;
} else {
tabSize = getTabSize() * metrics.charWidth('m');
}
}
}
@ -388,7 +436,11 @@ public class PlainView extends View implements TabExpander {
originalA, host, this);
}
}
drawLine(line, g, x, y);
if (drawLineOverridden && (g instanceof Graphics2D)) {
drawLine(line, (Graphics2D) g, (float) x, (float) y);
} else {
drawLine(line, g, x, y);
}
y += fontHeight;
if (line == 0) {
// This should never really happen, in so far as if
@ -435,6 +487,13 @@ public class PlainView extends View implements TabExpander {
int p0 = line.getStartOffset();
Segment s = SegmentCache.getSharedSegment();
doc.getText(p0, pos - p0, s);
if (useFloatingPointAPI) {
float xOffs = Utilities.getTabbedTextWidth(s, metrics, (float) tabBase, this, p0);
SegmentCache.releaseSharedSegment(s);
return new Rectangle2D.Float(lineArea.x + xOffs, lineArea.y, 1, metrics.getHeight());
}
int xOffs = Utilities.getTabbedTextWidth(s, metrics, tabBase, this,p0);
SegmentCache.releaseSharedSegment(s);
@ -456,14 +515,13 @@ public class PlainView extends View implements TabExpander {
* given point in the view &gt;= 0
* @see View#viewToModel
*/
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
// PENDING(prinz) properly calculate bias
bias[0] = Position.Bias.Forward;
Rectangle alloc = a.getBounds();
Document doc = getDocument();
int x = (int) fx;
int y = (int) fy;
if (y < alloc.y) {
// above the area covered by this icon, so the position
// is assumed to be the start of the coverage for this view.
@ -481,7 +539,7 @@ public class PlainView extends View implements TabExpander {
Element map = doc.getDefaultRootElement();
int fontHeight = metrics.getHeight();
int lineIndex = (fontHeight > 0 ?
Math.abs((y - alloc.y) / fontHeight) :
(int)Math.abs((y - alloc.y) / fontHeight) :
map.getElementCount() - 1);
if (lineIndex >= map.getElementCount()) {
return getEndOffset() - 1;
@ -507,7 +565,7 @@ public class PlainView extends View implements TabExpander {
doc.getText(p0, p1 - p0, s);
tabBase = alloc.x;
int offs = p0 + Utilities.getTabbedTextOffset(s, metrics,
tabBase, x, this, p0);
tabBase, x, this, p0, true);
SegmentCache.releaseSharedSegment(s);
return offs;
} catch (BadLocationException e) {
@ -586,7 +644,7 @@ public class PlainView extends View implements TabExpander {
if (tabSize == 0) {
return x;
}
int ntabs = (((int) x) - tabBase) / tabSize;
float ntabs = (x - tabBase) / tabSize;
return tabBase + ((ntabs + 1) * tabSize);
}
@ -758,6 +816,28 @@ public class PlainView extends View implements TabExpander {
return w;
}
static boolean isFPMethodOverriden(String method,
Class<?> cls,
Class<?>[] intTypes,
Class<?>[] fpTypes)
{
Module thisModule = PlainView.class.getModule();
while (!thisModule.equals(cls.getModule())) {
try {
cls.getDeclaredMethod(method, fpTypes);
return true;
} catch (Exception e1) {
try {
cls.getDeclaredMethod(method, intTypes);
return false;
} catch (Exception e2) {
cls = cls.getSuperclass();
}
}
}
return true;
}
// --- member variables -----------------------------------------------
/**
@ -780,7 +860,7 @@ public class PlainView extends View implements TabExpander {
Font font;
Segment lineBuffer;
int tabSize;
float tabSize;
int tabBase;
int sel0;
@ -796,4 +876,46 @@ public class PlainView extends View implements TabExpander {
*/
int firstLineOffset;
final boolean drawLineOverridden;
final boolean drawSelectedTextOverridden;
final boolean drawUnselectedTextOverridden;
final boolean useFloatingPointAPI;
{
final Class<?> CLS = getClass();
final Class<?> INT = Integer.TYPE;
final Class<?> FP = Float.TYPE;
drawLineOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {INT, Graphics.class, INT, INT};
Class<?>[] fpTypes = {INT, Graphics2D.class, FP, FP};
return isFPMethodOverriden("drawLine", CLS, intTypes, fpTypes);
}
});
drawUnselectedTextOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
return isFPMethodOverriden("drawUnselectedText", CLS, intTypes, fpTypes);
}
});
drawSelectedTextOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
return isFPMethodOverriden("drawSelectedText", CLS, intTypes, fpTypes);
}
});
useFloatingPointAPI = drawUnselectedTextOverridden || drawSelectedTextOverridden;
}
}

View File

@ -24,24 +24,23 @@
*/
package javax.swing.text;
import java.lang.reflect.Method;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.text.*;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.text.ParagraphView.Row;
import sun.swing.SwingUtilities2;
import static sun.swing.SwingUtilities2.drawChars;
import static sun.swing.SwingUtilities2.getFontCharWidth;
import static sun.swing.SwingUtilities2.getFontCharsWidth;
/**
* A collection of methods to deal with various text
@ -78,7 +77,11 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document &gt;= 0
* @return the X location at the end of the rendered text
*
* @deprecated replaced by
* {@link #drawTabbedText(Segment, float, float, Graphics2D, TabExpander, int)}
*/
@Deprecated(since = "9")
public static final int drawTabbedText(Segment s, int x, int y, Graphics g,
TabExpander e, int startOffset) {
return drawTabbedText(null, s, x, y, g, e, startOffset);
@ -96,6 +99,8 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document {@code >= 0}
* @return the X location at the end of the rendered text
*
* @since 9
*/
public static final float drawTabbedText(Segment s, float x, float y,
Graphics2D g,
@ -138,9 +143,19 @@ public class Utilities {
Segment s, int x, int y, Graphics g,
TabExpander e, int startOffset,
int [] justificationData) {
return (int) drawTabbedText(view, s, x, y, g, e, startOffset,
justificationData, false);
}
static final float drawTabbedText(View view,
Segment s, float x, float y, Graphics g,
TabExpander e, int startOffset,
int [] justificationData,
boolean useFPAPI)
{
JComponent component = getJComponent(view);
FontMetrics metrics = SwingUtilities2.getFontMetrics(component, g);
int nextX = x;
float nextX = x;
char[] txt = s.array;
int txtOffset = s.offset;
int flushLen = 0;
@ -174,19 +189,19 @@ public class Utilities {
&& i <= endJustifiableContent
)) {
if (flushLen > 0) {
nextX = SwingUtilities2.drawChars(component, g, txt,
flushIndex, flushLen, x, y);
nextX = drawChars(component, g, txt, flushIndex, flushLen, x, y);
flushLen = 0;
}
flushIndex = i + 1;
if (txt[i] == '\t') {
if (e != null) {
nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
} else {
nextX += metrics.charWidth(' ');
nextX += getFontCharWidth(' ', metrics, useFPAPI);
}
} else if (txt[i] == ' ') {
nextX += metrics.charWidth(' ') + spaceAddon;
float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI);
nextX += spaceWidth + spaceAddon;
if (i <= spaceAddonLeftoverEnd) {
nextX++;
}
@ -194,8 +209,8 @@ public class Utilities {
x = nextX;
} else if ((txt[i] == '\n') || (txt[i] == '\r')) {
if (flushLen > 0) {
nextX = SwingUtilities2.drawChars(component, g, txt,
flushIndex, flushLen, x, y);
nextX = drawChars(component, g, txt, flushIndex, flushLen,
x, y, useFPAPI);
flushLen = 0;
}
flushIndex = i + 1;
@ -205,8 +220,7 @@ public class Utilities {
}
}
if (flushLen > 0) {
nextX = SwingUtilities2.drawChars(component, g,txt, flushIndex,
flushLen, x, y);
nextX = drawChars(component, g,txt, flushIndex, flushLen, x, y, useFPAPI);
}
return nextX;
}
@ -223,7 +237,11 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document &gt;= 0
* @return the width of the text
*
* @deprecated replaced by
* {@link #getTabbedTextWidth(Segment, FontMetrics, float, TabExpander, int)}
*/
@Deprecated(since = "9")
public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x,
TabExpander e, int startOffset) {
return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);
@ -240,11 +258,13 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document {@code >= 0}
* @return the width of the text
*
* @since 9
*/
public static final float getTabbedTextWidth(Segment s, FontMetrics metrics,
float x, TabExpander e,
int startOffset) {
return getTabbedTextWidth(s, metrics, (int) x, e, startOffset);
return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);
}
// In addition to the previous method it can extend spaces for
@ -254,10 +274,32 @@ public class Utilities {
// one:
// @param justificationData justificationData for the row.
// if null not justification is needed
static final int getTabbedTextWidth(View view, Segment s, FontMetrics metrics, int x,
static final int getTabbedTextWidth(View view, Segment s,
FontMetrics metrics, int x,
TabExpander e, int startOffset,
int[] justificationData) {
int nextX = x;
int[] justificationData)
{
return (int) getTabbedTextWidth(view, s, metrics, x, e, startOffset,
justificationData, false);
}
static final float getTabbedTextWidth(View view, Segment s,
FontMetrics metrics, float x,
TabExpander e, int startOffset,
int[] justificationData)
{
return getTabbedTextWidth(view, s, metrics, x, e, startOffset,
justificationData, true);
}
static final float getTabbedTextWidth(View view, Segment s,
FontMetrics metrics, float x,
TabExpander e, int startOffset,
int[] justificationData,
boolean useFPAPI) {
float nextX = x;
char[] txt = s.array;
int txtOffset = s.offset;
int n = s.offset + s.count;
@ -294,13 +336,13 @@ public class Utilities {
charCount = 0;
if (txt[i] == '\t') {
if (e != null) {
nextX = (int) e.nextTabStop((float) nextX,
startOffset + i - txtOffset);
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
} else {
nextX += metrics.charWidth(' ');
nextX += getFontCharWidth(' ', metrics, useFPAPI);
}
} else if (txt[i] == ' ') {
nextX += metrics.charWidth(' ') + spaceAddon;
float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI);
nextX += spaceWidth + spaceAddon;
if (i <= spaceAddonLeftoverEnd) {
nextX++;
}
@ -308,13 +350,15 @@ public class Utilities {
} else if(txt[i] == '\n') {
// Ignore newlines, they take up space and we shouldn't be
// counting them.
nextX += metrics.charsWidth(txt, i - charCount, charCount);
nextX += getFontCharsWidth(txt, i - charCount, charCount,
metrics, useFPAPI);
charCount = 0;
} else {
charCount++;
}
}
nextX += metrics.charsWidth(txt, n - charCount, charCount);
nextX += getFontCharsWidth(txt, n - charCount, charCount,
metrics, useFPAPI);
return nextX - x;
}
@ -334,7 +378,12 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document &gt;= 0
* @return the offset into the text &gt;= 0
*
* @deprecated replaced by
* {@link #getTabbedTextOffset(Segment, FontMetrics, float, float,
* TabExpander, int, boolean)}
*/
@Deprecated(since = "9")
public static final int getTabbedTextOffset(Segment s, FontMetrics metrics,
int x0, int x, TabExpander e,
int startOffset) {
@ -346,7 +395,7 @@ public class Utilities {
int startOffset,
int[] justificationData) {
return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true,
justificationData);
justificationData, false);
}
/**
@ -365,13 +414,19 @@ public class Utilities {
* @param startOffset starting offset of the text in the document &gt;= 0
* @param round whether or not to round
* @return the offset into the text &gt;= 0
*
* @deprecated replaced by
* {@link #getTabbedTextOffset(Segment, FontMetrics, float, float,
* TabExpander, int, boolean)}
*/
@Deprecated(since = "9")
public static final int getTabbedTextOffset(Segment s,
FontMetrics metrics,
int x0, int x, TabExpander e,
int startOffset,
boolean round) {
return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, round, null);
return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset,
round, null, false);
}
/**
@ -390,6 +445,8 @@ public class Utilities {
* @param startOffset starting offset of the text in the document {@code >= 0}
* @param round whether or not to round
* @return the offset into the text {@code >= 0}
*
* @since 9
*/
public static final int getTabbedTextOffset(Segment s,
FontMetrics metrics,
@ -398,8 +455,8 @@ public class Utilities {
int startOffset,
boolean round)
{
return getTabbedTextOffset(null, s, metrics, (int) x0, (int) x, e,
startOffset, round, null);
return getTabbedTextOffset(null, s, metrics, x0, x, e,
startOffset, round, null, true);
}
// In addition to the previous method it can extend spaces for
@ -412,15 +469,16 @@ public class Utilities {
static final int getTabbedTextOffset(View view,
Segment s,
FontMetrics metrics,
int x0, int x, TabExpander e,
float x0, float x, TabExpander e,
int startOffset,
boolean round,
int[] justificationData) {
int[] justificationData,
boolean useFPAPI) {
if (x0 >= x) {
// x before x0, return.
return 0;
}
int nextX = x0;
float nextX = x0;
// s may be a shared segment, so it is copied prior to calling
// the tab expander
char[] txt = s.array;
@ -456,19 +514,19 @@ public class Utilities {
)){
if (txt[i] == '\t') {
if (e != null) {
nextX = (int) e.nextTabStop((float) nextX,
startOffset + i - txtOffset);
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
} else {
nextX += metrics.charWidth(' ');
nextX += getFontCharWidth(' ', metrics, useFPAPI);
}
} else if (txt[i] == ' ') {
nextX += metrics.charWidth(' ') + spaceAddon;
nextX += getFontCharWidth(' ', metrics, useFPAPI);
nextX += spaceAddon;
if (i <= spaceAddonLeftoverEnd) {
nextX++;
}
}
} else {
nextX += metrics.charWidth(txt[i]);
nextX += getFontCharWidth(txt[i], metrics, useFPAPI);
}
if (x < nextX) {
// found the hit position... return the appropriate side
@ -480,12 +538,15 @@ public class Utilities {
if (round) {
offset = i + 1 - txtOffset;
int width = metrics.charsWidth(txt, txtOffset, offset);
int span = x - x0;
float width = getFontCharsWidth(txt, txtOffset, offset,
metrics, useFPAPI);
float span = x - x0;
if (span < width) {
while (offset > 0) {
int nextWidth = offset > 1 ? metrics.charsWidth(txt, txtOffset, offset - 1) : 0;
float charsWidth = getFontCharsWidth(txt, txtOffset,
offset - 1, metrics, useFPAPI);
float nextWidth = offset > 1 ? charsWidth : 0;
if (span >= nextWidth) {
if (span - nextWidth < width - span) {
@ -502,7 +563,9 @@ public class Utilities {
} else {
offset = i - txtOffset;
while (offset > 0 && metrics.charsWidth(txt, txtOffset, offset) > (x - x0)) {
while (offset > 0 && getFontCharsWidth(txt, txtOffset, offset,
metrics, useFPAPI)
> (x - x0)) {
offset--;
}
}
@ -528,15 +591,26 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset in the document of the text
* @return the offset into the given text
*
* @deprecated replaced by
* {@link #getBreakLocation(Segment, FontMetrics, float, float,
* TabExpander, int)}
*/
@Deprecated(since = "9")
public static final int getBreakLocation(Segment s, FontMetrics metrics,
int x0, int x, TabExpander e,
int startOffset) {
return getBreakLocation(s, metrics, x0, x, e, startOffset, false);
}
static final int getBreakLocation(Segment s, FontMetrics metrics,
float x0, float x, TabExpander e,
int startOffset, boolean useFPIAPI) {
char[] txt = s.array;
int txtOffset = s.offset;
int txtCount = s.count;
int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,
e, startOffset, false);
int index = getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset,
false, null, useFPIAPI);
if (index >= txtCount - 1) {
return txtCount;
@ -577,11 +651,13 @@ public class Utilities {
* tabs will be expanded as a space character.
* @param startOffset starting offset in the document of the text
* @return the offset into the given text
*
* @since 9
*/
public static final int getBreakLocation(Segment s, FontMetrics metrics,
float x0, float x, TabExpander e,
int startOffset) {
return getBreakLocation(s, metrics, (int) x0, (int) x, e, startOffset);
return getBreakLocation(s, metrics, x0, x, e, startOffset, false);
}
/**
@ -627,16 +703,16 @@ public class Utilities {
* @exception BadLocationException if the offset is out of range
*/
public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException {
Rectangle r = c.modelToView(offs);
Rectangle2D r = c.modelToView2D(offs);
if (r == null) {
return -1;
}
int n = c.getDocument().getLength();
int lastOffs = offs;
int y = r.y;
while ((r != null) && (y == r.y)) {
double y = r.getY();
while ((r != null) && (y == r.getY())) {
// Skip invisible elements
if (r.height !=0) {
if (r.getHeight() !=0) {
offs = lastOffs;
}
lastOffs += 1;
@ -657,27 +733,44 @@ public class Utilities {
* @return the position &gt;= 0 if the request can be computed, otherwise
* a value of -1 will be returned.
* @exception BadLocationException if the offset is out of range
*
* @deprecated replaced by
* {@link #getPositionAbove(JTextComponent, int, float)}
*/
public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException {
@Deprecated(since = "9")
public static final int getPositionAbove(JTextComponent c, int offs, int x)
throws BadLocationException
{
return getPositionAbove(c, offs, x, false);
}
static final int getPositionAbove(JTextComponent c, int offs, float x,
boolean useFPAPI) throws BadLocationException
{
int lastOffs = getRowStart(c, offs) - 1;
if (lastOffs < 0) {
return -1;
}
int bestSpan = Integer.MAX_VALUE;
int y = 0;
Rectangle r = null;
double bestSpan = Integer.MAX_VALUE;
double y = 0;
Rectangle2D r = null;
if (lastOffs >= 0) {
r = c.modelToView(lastOffs);
y = r.y;
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
y = r.getY();
}
while ((r != null) && (y == r.y)) {
int span = Math.abs(r.x - x);
while ((r != null) && (y == r.getY())) {
double span = Math.abs(r.getX() - x);
if (span < bestSpan) {
offs = lastOffs;
bestSpan = span;
}
lastOffs -= 1;
r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
if ((lastOffs >= 0)) {
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
} else {
r = null;
}
}
return offs;
}
@ -694,10 +787,12 @@ public class Utilities {
* @return the position {@code >= 0} if the request can be computed, otherwise
* a value of -1 will be returned.
* @exception BadLocationException if the offset is out of range
*
* @since 9
*/
public static final int getPositionAbove(JTextComponent c, int offs, float x)
throws BadLocationException {
return getPositionAbove(c, offs, (int) x);
return getPositionAbove(c, offs, x, true);
}
/**
@ -712,28 +807,45 @@ public class Utilities {
* @return the position &gt;= 0 if the request can be computed, otherwise
* a value of -1 will be returned.
* @exception BadLocationException if the offset is out of range
*
* @deprecated replaced by
* {@link #getPositionBelow(JTextComponent, int, float)}
*/
public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException {
@Deprecated(since = "9")
public static final int getPositionBelow(JTextComponent c, int offs, int x)
throws BadLocationException
{
return getPositionBelow(c, offs, x, false);
}
static final int getPositionBelow(JTextComponent c, int offs, float x,
boolean useFPAPI) throws BadLocationException
{
int lastOffs = getRowEnd(c, offs) + 1;
if (lastOffs <= 0) {
return -1;
}
int bestSpan = Integer.MAX_VALUE;
double bestSpan = Integer.MAX_VALUE;
int n = c.getDocument().getLength();
int y = 0;
Rectangle r = null;
double y = 0;
Rectangle2D r = null;
if (lastOffs <= n) {
r = c.modelToView(lastOffs);
y = r.y;
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
y = r.getY();
}
while ((r != null) && (y == r.y)) {
int span = Math.abs(x - r.x);
while ((r != null) && (y == r.getY())) {
double span = Math.abs(x - r.getX());
if (span < bestSpan) {
offs = lastOffs;
bestSpan = span;
}
lastOffs += 1;
r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
if (lastOffs <= n) {
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
} else {
r = null;
}
}
return offs;
}
@ -750,10 +862,12 @@ public class Utilities {
* @return the position {@code >= 0} if the request can be computed, otherwise
* a value of -1 will be returned.
* @exception BadLocationException if the offset is out of range
*
* @since 9
*/
public static final int getPositionBelow(JTextComponent c, int offs, float x)
throws BadLocationException {
return getPositionBelow(c, offs, (int) x);
return getPositionBelow(c, offs, x, true);
}
/**
@ -1029,7 +1143,23 @@ public class Utilities {
*/
static int drawComposedText(View view, AttributeSet attr, Graphics g,
int x, int y, int p0, int p1)
throws BadLocationException {
throws BadLocationException
{
return (int) drawComposedText(view, attr, g, x, y, p0, p1, false);
}
static float drawComposedText(View view, AttributeSet attr, Graphics g,
float x, float y, int p0, int p1)
throws BadLocationException
{
return drawComposedText(view, attr, g, x, y, p0, p1, true);
}
static float drawComposedText(View view, AttributeSet attr, Graphics g,
float x, float y, int p0, int p1,
boolean useFPAPI)
throws BadLocationException
{
Graphics2D g2d = (Graphics2D)g;
AttributedString as = (AttributedString)attr.getAttribute(
StyleConstants.ComposedTextAttribute);
@ -1039,8 +1169,7 @@ public class Utilities {
return x;
AttributedCharacterIterator aci = as.getIterator(null, p0, p1);
return x + (int)SwingUtilities2.drawString(
getJComponent(view), g2d,aci,x,y);
return x + SwingUtilities2.drawString(getJComponent(view), g2d, aci, x, y);
}
/**

View File

@ -25,8 +25,12 @@
package javax.swing.text;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.event.*;
import static javax.swing.text.PlainView.isFPMethodOverriden;
/**
* View of plain text (text with only one font and color)
@ -86,17 +90,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
return size;
}
/**
* Returns the tab size set for the document, defaulting to 8.
*
* @implSpec This implementation calls {@link #getTabSize() getTabSize()}.
*
* @return the tab size
*/
protected float getFractionalTabSize() {
return getTabSize();
}
/**
* Renders a line of text, suppressing whitespace at the end
* and expanding any tabs. This is implemented to make calls
@ -111,8 +104,17 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param y the starting Y position &gt;= 0
* @see #drawUnselectedText
* @see #drawSelectedText
*
* @deprecated replaced by
* {@link #drawLine(int, int, Graphics2D, float, float)}
*/
@Deprecated(since = "9")
protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
drawLineImpl(p0, p1, g, x, y, false);
}
private void drawLineImpl(int p0, int p1, Graphics g, float x, float y,
boolean useFPAPI) {
Element lineMap = getElement();
Element line = lineMap.getElement(lineMap.getElementIndex(p0));
Element elem;
@ -143,10 +145,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* <code>drawSelectedText</code> so that the way selected and
* unselected text are rendered can be customized.
*
* @implSpec This implementation calls
* {@link #drawLine(int, int, Graphics, int, int)
* drawLine(p0, p1, (Graphics) g, (int) x, (int) y)}.
*
* @param p0 the starting document location to use &gt;= 0
* @param p1 the ending document location to use &gt;= p1
* @param g the graphics context
@ -154,12 +152,17 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param y the starting Y position &gt;= 0
* @see #drawUnselectedText
* @see #drawSelectedText
*
* @since 9
*/
protected void drawLine(int p0, int p1, Graphics2D g, float x, float y) {
drawLine(p0, p1, (Graphics) g, (int) x, (int) y);
drawLineImpl(p0, p1, g, x, y, true);
}
private int drawText(Element elem, int p0, int p1, Graphics g, int x, int y) throws BadLocationException {
private float drawText(Element elem, int p0, int p1, Graphics g,
float x, float y)
throws BadLocationException
{
p1 = Math.min(getDocument().getLength(), p1);
AttributeSet attr = elem.getAttributes();
@ -171,23 +174,23 @@ public class WrappedPlainView extends BoxView implements TabExpander {
} else {
if (sel0 == sel1 || selected == unselected) {
// no selection, or it is invisible
x = drawUnselectedText(g, x, y, p0, p1);
x = callDrawUnselectedText(g, x, y, p0, p1);
} else if ((p0 >= sel0 && p0 <= sel1) && (p1 >= sel0 && p1 <= sel1)) {
x = drawSelectedText(g, x, y, p0, p1);
x = callDrawSelectedText(g, x, y, p0, p1);
} else if (sel0 >= p0 && sel0 <= p1) {
if (sel1 >= p0 && sel1 <= p1) {
x = drawUnselectedText(g, x, y, p0, sel0);
x = drawSelectedText(g, x, y, sel0, sel1);
x = drawUnselectedText(g, x, y, sel1, p1);
x = callDrawUnselectedText(g, x, y, p0, sel0);
x = callDrawSelectedText(g, x, y, sel0, sel1);
x = callDrawUnselectedText(g, x, y, sel1, p1);
} else {
x = drawUnselectedText(g, x, y, p0, sel0);
x = drawSelectedText(g, x, y, sel0, p1);
x = callDrawUnselectedText(g, x, y, p0, sel0);
x = callDrawSelectedText(g, x, y, sel0, p1);
}
} else if (sel1 >= p0 && sel1 <= p1) {
x = drawSelectedText(g, x, y, p0, sel1);
x = drawUnselectedText(g, x, y, sel1, p1);
x = callDrawSelectedText(g, x, y, p0, sel1);
x = callDrawUnselectedText(g, x, y, sel1, p1);
} else {
x = drawUnselectedText(g, x, y, p0, p1);
x = callDrawUnselectedText(g, x, y, p0, p1);
}
}
@ -205,14 +208,36 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param p1 the ending position in the model &gt;= p0
* @return the X location of the end of the range &gt;= 0
* @exception BadLocationException if the range is invalid
*
* @deprecated replaced by
* {@link #drawUnselectedText(Graphics2D, float, float, int, int)}
*/
@Deprecated(since = "9")
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
int p0, int p1) throws BadLocationException
{
return (int) drawUnselectedTextImpl(g, x, y, p0, p1, false);
}
private float callDrawUnselectedText(Graphics g, float x, float y,
int p0, int p1)
throws BadLocationException
{
return drawUnselectedTextOverridden && g instanceof Graphics2D
? drawUnselectedText((Graphics2D) g, x, y, p0, p1)
: drawUnselectedText(g, (int) x, (int) y, p0, p1);
}
private float drawUnselectedTextImpl(Graphics g, float x, float y,
int p0, int p1, boolean useFPAPI)
throws BadLocationException
{
g.setColor(unselected);
Document doc = getDocument();
Segment segment = SegmentCache.getSharedSegment();
doc.getText(p0, p1 - p0, segment);
int ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0);
float ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0,
null, useFPAPI);
SegmentCache.releaseSharedSegment(segment);
return ret;
}
@ -221,10 +246,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* Renders the given range in the model as normal unselected
* text.
*
* @implSpec This implementation calls
* {@link #drawUnselectedText(Graphics, int, int, int, int)
* drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
*
* @param g the graphics context
* @param x the starting X coordinate &gt;= 0
* @param y the starting Y coordinate &gt;= 0
@ -232,10 +253,12 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param p1 the ending position in the model &gt;= p0
* @return the X location of the end of the range &gt;= 0
* @exception BadLocationException if the range is invalid
*
* @since 9
*/
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
return drawUnselectedText((Graphics) g, (int) x, (int) y, p0, p1);
return drawUnselectedTextImpl(g, x, y, p0, p1, true);
}
/**
* Renders the given range in the model as selected text. This
@ -250,14 +273,37 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param p1 the ending position in the model &gt;= p0
* @return the location of the end of the range.
* @exception BadLocationException if the range is invalid
*
* @deprecated replaced by
* {@link #drawSelectedText(Graphics2D, float, float, int, int)}
*/
protected int drawSelectedText(Graphics g, int x,
int y, int p0, int p1) throws BadLocationException {
@Deprecated(since = "9")
protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
throws BadLocationException
{
return (int) drawSelectedTextImpl(g, x, y, p0, p1, false);
}
private float callDrawSelectedText(Graphics g, float x, float y,
int p0, int p1)
throws BadLocationException
{
return drawSelectedTextOverridden && g instanceof Graphics2D
? drawSelectedText((Graphics2D) g, x, y, p0, p1)
: drawSelectedText(g, (int) x, (int) y, p0, p1);
}
private float drawSelectedTextImpl(Graphics g, float x, float y,
int p0, int p1,
boolean useFPAPI)
throws BadLocationException
{
g.setColor(selected);
Document doc = getDocument();
Segment segment = SegmentCache.getSharedSegment();
doc.getText(p0, p1 - p0, segment);
int ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0);
float ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0,
null, useFPAPI);
SegmentCache.releaseSharedSegment(segment);
return ret;
}
@ -268,10 +314,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* the hosting component. It assumes the highlighter will render
* the selected background.
*
* @implSpec This implementation calls
* {@link #drawSelectedText(Graphics, int, int, int, int)
* drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
*
* @param g the graphics context
* @param x the starting X coordinate &gt;= 0
* @param y the starting Y coordinate &gt;= 0
@ -279,10 +321,12 @@ public class WrappedPlainView extends BoxView implements TabExpander {
* @param p1 the ending position in the model &gt;= p0
* @return the location of the end of the range.
* @exception BadLocationException if the range is invalid
*
* @since 9
*/
protected float drawSelectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
return drawSelectedText((Graphics) g, (int) x, (int) y, p0, p1);
return drawSelectedTextImpl(g, x, y, p0, p1, true);
}
/**
* Gives access to a buffer that can be used to fetch
@ -395,7 +439,13 @@ public class WrappedPlainView extends BoxView implements TabExpander {
Component host = getContainer();
Font f = host.getFont();
metrics = host.getFontMetrics(f);
tabSize = getTabSize() * metrics.charWidth('m');
if (useFloatingPointAPI) {
FontRenderContext frc = metrics.getFontRenderContext();
float tabWidth = (float) f.getStringBounds("m", frc).getWidth();
tabSize = getTabSize() * tabWidth;
} else {
tabSize = getTabSize() * metrics.charWidth('m');
}
}
// --- TabExpander methods ------------------------------------------
@ -413,7 +463,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
public float nextTabStop(float x, int tabOffset) {
if (tabSize == 0)
return x;
int ntabs = ((int) x - tabBase) / tabSize;
float ntabs = (x - tabBase) / tabSize;
return tabBase + ((ntabs + 1) * tabSize);
}
@ -591,7 +641,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
Segment lineBuffer;
boolean widthChanging;
int tabBase;
int tabSize;
float tabSize;
boolean wordWrap;
int sel0;
@ -668,6 +718,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
int end = getEndOffset();
int p0 = start;
int[] lineEnds = getLineEnds();
boolean useDrawLineFP = drawLineOverridden && g instanceof Graphics2D;
for (int i = 0; i < lineCount; i++) {
int p1 = (lineEnds == null) ? end :
start + lineEnds[i];
@ -677,8 +728,11 @@ public class WrappedPlainView extends BoxView implements TabExpander {
: p1;
dh.paintLayeredHighlights(g, p0, hOffset, a, host, this);
}
drawLine(p0, p1, g, x, y);
if (useDrawLineFP) {
drawLine(p0, p1, (Graphics2D) g, (float) x, (float) y);
} else {
drawLine(p0, p1, g, x, y);
}
p0 = p1;
y += metrics.getHeight();
}
@ -929,4 +983,47 @@ public class WrappedPlainView extends BoxView implements TabExpander {
int lineCount;
SoftReference<int[]> lineCache = null;
}
private final boolean drawLineOverridden;
private final boolean drawSelectedTextOverridden;
private final boolean drawUnselectedTextOverridden;
private final boolean useFloatingPointAPI;
{
final Class<?> CLS = getClass();
final Class<?> INT = Integer.TYPE;
final Class<?> FP = Float.TYPE;
drawLineOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {INT, INT, Graphics.class, INT, INT};
Class<?>[] fpTypes = {INT, INT, Graphics2D.class, FP, FP};
return isFPMethodOverriden("drawLine", CLS, intTypes, fpTypes);
}
});
drawUnselectedTextOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
return isFPMethodOverriden("drawUnselectedText", CLS, intTypes, fpTypes);
}
});
drawSelectedTextOverridden = AccessController
.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
return isFPMethodOverriden("drawSelectedText", CLS, intTypes, fpTypes);
}
});
useFloatingPointAPI = drawUnselectedTextOverridden || drawSelectedTextOverridden;
}
}

View File

@ -30,6 +30,7 @@ import java.awt.*;
import static java.awt.RenderingHints.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import static java.awt.geom.AffineTransform.TYPE_FLIP;
import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
@ -723,10 +724,31 @@ public class SwingUtilities2 {
int length,
int x,
int y) {
return (int) drawChars(c, g, data, offset, length, x, y, false);
}
public static float drawChars(JComponent c, Graphics g,
char[] data,
int offset,
int length,
float x,
float y) {
return drawChars(c, g, data, offset, length, x, y, true);
}
public static float drawChars(JComponent c, Graphics g,
char[] data,
int offset,
int length,
float x,
float y,
boolean useFPAPI) {
if ( length <= 0 ) { //no need to paint empty strings
return x;
}
int nextX = x + getFontMetrics(c, g).charsWidth(data, offset, length);
float nextX = x + getFontCharsWidth(data, offset, length,
getFontMetrics(c, g),
useFPAPI);
if (isPrinting(g)) {
Graphics2D g2d = getGraphics2D(g);
if (g2d != null) {
@ -766,8 +788,14 @@ public class SwingUtilities2 {
Object aaHint = (c == null)
? null
: c.getClientProperty(KEY_TEXT_ANTIALIASING);
if (aaHint != null && (g instanceof Graphics2D)) {
Graphics2D g2 = (Graphics2D)g;
if (!(g instanceof Graphics2D)) {
g.drawChars(data, offset, length, (int) x, (int) y);
return nextX;
}
Graphics2D g2 = (Graphics2D) g;
if (aaHint != null) {
Object oldContrast = null;
Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
@ -788,7 +816,7 @@ public class SwingUtilities2 {
}
}
g.drawChars(data, offset, length, x, y);
g2.drawString(new String(data, offset, length), x, y);
if (oldAAValue != null) {
g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
@ -798,19 +826,59 @@ public class SwingUtilities2 {
}
}
else {
g.drawChars(data, offset, length, x, y);
g2.drawString(new String(data, offset, length), x, y);
}
return nextX;
}
public static float getFontCharWidth(char c, FontMetrics fm,
boolean useFPAPI)
{
return getFontCharsWidth(new char[]{c}, 0, 1, fm, useFPAPI);
}
public static float getFontCharsWidth(char[] data, int offset, int len,
FontMetrics fm,
boolean useFPAPI)
{
return len == 0 ? 0 : getFontStringWidth(new String(data, offset, len),
fm, useFPAPI);
}
public static float getFontStringWidth(String data, FontMetrics fm,
boolean useFPAPI)
{
if (useFPAPI) {
Rectangle2D bounds = fm.getFont()
.getStringBounds(data, fm.getFontRenderContext());
return (float) bounds.getWidth();
} else {
return fm.stringWidth(data);
}
}
/*
* see documentation for drawChars
* returns the advance
*/
public static float drawString(JComponent c, Graphics g,
AttributedCharacterIterator iterator,
int x,
int y) {
int x, int y)
{
return drawStringImpl(c, g, iterator, x, y);
}
public static float drawString(JComponent c, Graphics g,
AttributedCharacterIterator iterator,
float x, float y)
{
return drawStringImpl(c, g, iterator, x, y);
}
private static float drawStringImpl(JComponent c, Graphics g,
AttributedCharacterIterator iterator,
float x, float y)
{
float retVal;
boolean isPrinting = isPrinting(g);
@ -825,8 +893,8 @@ public class SwingUtilities2 {
Graphics2D g2d = getGraphics2D(g);
if (g2d == null) {
g.drawString(iterator,x,y); //for the cases where advance
//matters it should not happen
g.drawString(iterator, (int)x, (int)y); //for the cases where advance
//matters it should not happen
retVal = x;
} else {

View File

@ -41,13 +41,13 @@ import javax.swing.plaf.TreeUI;
import javax.accessibility.*;
import com.sun.java.accessibility.util.*;
import java.awt.geom.Rectangle2D;
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
/*
* Note: This class has to be public. It's loaded from the VM like this:
@ -1754,7 +1754,7 @@ final public class AccessBridge {
if (child instanceof JTextComponent) {
JTextComponent text = (JTextComponent) child;
try {
r = text.modelToView(text.getCaretPosition());
r = text.modelToView2D(text.getCaretPosition()).getBounds();
if (r != null) {
Point p = text.getLocationOnScreen();
r.translate(p.x, p.y);

View File

@ -0,0 +1,447 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
/*
* @test
* @bug 8163175
* @summary PlainView.modelToView() method should return Rectangle2D
* @run main/manual CaretFloatingPointAPITest
*/
public class CaretFloatingPointAPITest {
private static volatile boolean testResult = false;
private static volatile CountDownLatch countDownLatch;
private static final String INSTRUCTIONS = "INSTRUCTIONS:\n\n"
+ "Verify that cursor position is not rounded on HiDPI display.\n\n"
+ "If the display does not support HiDPI mode press PASS.\n\n"
+ "1. Press the Right-Arrow key several times to move the red caret"
+ " in the text field.\n"
+ "2. Check that the caret has the same position between chars"
+ " in diffrent locations.\n\n"
+ "If so, press PASS, else press FAIL.\n";
public static void main(String args[]) throws Exception {
countDownLatch = new CountDownLatch(1);
SwingUtilities.invokeLater(CaretFloatingPointAPITest::createUI);
countDownLatch.await(15, TimeUnit.MINUTES);
if (!testResult) {
throw new RuntimeException("Test fails!");
}
}
private static void createUI() {
final JFrame mainFrame = new JFrame("Metal L&F icons test");
GridBagLayout layout = new GridBagLayout();
JPanel mainControlPanel = new JPanel(layout);
JPanel resultButtonPanel = new JPanel(layout);
GridBagConstraints gbc = new GridBagConstraints();
JTextField textField = new JTextField("aaaaaaaaaaaaaaaaaaaaaaa");
Dimension size = new Dimension(400, 100);
textField.setPreferredSize(size);
textField.setFont(textField.getFont().deriveFont(28.0f));
textField.setCaretColor(Color.RED);
textField.setCaret(new CustomCaret());
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(5, 15, 5, 15);
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(textField, gbc);
JTextArea instructionTextArea = new JTextArea();
instructionTextArea.setText(INSTRUCTIONS);
instructionTextArea.setEditable(false);
instructionTextArea.setBackground(Color.white);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(instructionTextArea, gbc);
JButton passButton = new JButton("Pass");
passButton.setActionCommand("Pass");
passButton.addActionListener((ActionEvent e) -> {
testResult = true;
mainFrame.dispose();
countDownLatch.countDown();
});
JButton failButton = new JButton("Fail");
failButton.setActionCommand("Fail");
failButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.dispose();
countDownLatch.countDown();
}
});
gbc.gridx = 0;
gbc.gridy = 0;
resultButtonPanel.add(passButton, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
resultButtonPanel.add(failButton, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
mainControlPanel.add(resultButtonPanel, gbc);
mainFrame.add(mainControlPanel);
mainFrame.pack();
mainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
mainFrame.dispose();
countDownLatch.countDown();
}
});
mainFrame.setVisible(true);
}
static class CustomCaret implements Caret {
private JTextComponent component;
private boolean visible;
private boolean selectionVisible = true;
int blinkRate;
int dot;
int mark;
Position.Bias dotBias;
Position.Bias markBias;
Object selectionTag;
Point2D magicCaretPosition;
private MouseListener mouseListener = new CaretMouseListener();
@Override
public void install(JTextComponent c) {
this.component = c;
c.addMouseListener(mouseListener);
}
@Override
public void deinstall(JTextComponent c) {
c.removeMouseListener(mouseListener);
this.component = null;
}
@Override
public void paint(Graphics g) {
if (component == null) {
return;
}
int dot = getDot();
Rectangle2D r = null;
try {
r = component.modelToView2D(dot);
} catch (BadLocationException e) {
return;
}
if (r == null) {
return;
}
Rectangle2D cr = getCaretRectangle(r);
repaint(cr.getBounds());
g.setColor(component.getCaretColor());
float cx = (float) cr.getX();
float cy = (float) cr.getY();
float cw = (float) cr.getWidth();
float ch = (float) cr.getHeight();
float c = cx + cw / 2;
Graphics2D g2d = (Graphics2D) g;
g2d.draw(new Line2D.Float(c, cy, c, cy + ch));
g2d.draw(new Line2D.Float(cx, cy, cx + cw, cy));
g2d.draw(new Line2D.Float(cx, cy + ch, cx + cw, cy + ch));
}
void repaint(Rectangle r) {
component.repaint(r);
}
Rectangle2D getCaretRectangle(Rectangle2D r) {
int d = 3;
double cx = r.getX() - d;
double cy = r.getY();
double cw = 2 * d;
double ch = r.getHeight();
return new Rectangle2D.Double(cx, cy, cw, ch);
}
@Override
public void addChangeListener(ChangeListener l) {
}
@Override
public void removeChangeListener(ChangeListener l) {
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setVisible(boolean v) {
this.visible = true;
}
@Override
public boolean isSelectionVisible() {
return selectionVisible;
}
@Override
public void setSelectionVisible(boolean v) {
this.selectionVisible = v;
updateSelection();
}
@Override
public void setMagicCaretPosition(Point p) {
magicCaretPosition = p;
}
@Override
public Point getMagicCaretPosition() {
if (magicCaretPosition != null) {
return new Point((int) magicCaretPosition.getX(),
(int) magicCaretPosition.getY());
}
return null;
}
@Override
public void setBlinkRate(int rate) {
this.blinkRate = rate;
}
@Override
public int getBlinkRate() {
return blinkRate;
}
@Override
public int getDot() {
return dot;
}
@Override
public int getMark() {
return mark;
}
@Override
public void setDot(int dot) {
setDot(dot, Position.Bias.Forward);
}
private void setDot(int dot, Position.Bias bias) {
handleSetDot(dot, bias);
updateSelection();
}
@Override
public void moveDot(int dot) {
moveDot(dot, Position.Bias.Forward);
}
private void moveDot(int dot, Position.Bias bias) {
changeCaretPosition(dot, bias);
updateSelection();
}
void handleSetDot(int dot, Position.Bias dotBias) {
if (component == null) {
return;
}
Document doc = component.getDocument();
if (doc != null) {
dot = Math.min(dot, doc.getLength());
}
dot = Math.max(dot, 0);
if (dot == 0) {
dotBias = Position.Bias.Forward;
}
mark = dot;
if (this.dot != dot || this.dotBias != dotBias) {
changeCaretPosition(dot, dotBias);
updateSelection();
}
this.markBias = this.dotBias;
}
void changeCaretPosition(int dot, Position.Bias dotBias) {
this.dot = dot;
this.dotBias = dotBias;
setMagicCaretPosition(null);
SwingUtilities.invokeLater(this::repaintNewCaret);
}
private void updateSelection() {
Highlighter h = component.getHighlighter();
if (h != null) {
int p0 = Math.min(dot, mark);
int p1 = Math.max(dot, mark);
if (p0 == p1 || !selectionVisible) {
if (selectionTag != null) {
h.removeHighlight(selectionTag);
selectionTag = null;
}
} else {
try {
if (selectionTag != null) {
h.changeHighlight(selectionTag, p0, p1);
} else {
Highlighter.HighlightPainter p = getSelectionPainter();
selectionTag = h.addHighlight(p0, p1, p);
}
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
}
}
}
void repaintNewCaret() {
if (component != null) {
TextUI mapper = component.getUI();
Document doc = component.getDocument();
if ((mapper != null) && (doc != null)) {
Rectangle2D newLoc;
try {
newLoc = mapper.modelToView2D(component, this.dot, this.dotBias);
} catch (BadLocationException e) {
newLoc = null;
}
if (newLoc != null) {
adjustVisibility(newLoc.getBounds());
if (getMagicCaretPosition() == null) {
setMagicCaretPosition(new Point((int) newLoc.getX(),
(int) newLoc.getY()));
}
}
damage(newLoc.getBounds());
}
}
}
protected Highlighter.HighlightPainter getSelectionPainter() {
return DefaultHighlighter.DefaultPainter;
}
protected void adjustVisibility(Rectangle nloc) {
if (component == null) {
return;
}
if (SwingUtilities.isEventDispatchThread()) {
component.scrollRectToVisible(nloc);
} else {
SwingUtilities.invokeLater(() -> {
component.scrollRectToVisible(nloc);
});
}
}
protected synchronized void damage(Rectangle r) {
if (r != null && component != null) {
component.repaint(r);
}
}
private class CaretMouseListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
Point pt = new Point(e.getX(), e.getY());
Position.Bias[] biasRet = new Position.Bias[1];
int pos = component.getUI().viewToModel(component, pt, biasRet);
if (biasRet[0] == null) {
biasRet[0] = Position.Bias.Forward;
}
if (pos >= 0) {
setDot(pos);
}
}
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
/**
* @test
* @bug 8156217
* @summary Selected text is shifted on HiDPI display
* @run main/manual/othervm -Dsun.java2d.uiScale=2 TextSelectionTest
*/
public class TextSelectionTest {
private static final String INSTRUCTIONS = "This is a manual test.\n"
+ "\n"
+ "Select the current text from the end to the beginning.\n"
+ "\n"
+ "If the text is slightly shiftted from one side to another\n"
+ "and back during selection press Fail.\n"
+ "Otherwise, press Pass.";
private static final CountDownLatch latch = new CountDownLatch(1);
private static volatile boolean passed = false;
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(TextSelectionTest::createAndShowGUI);
latch.await(3, TimeUnit.MINUTES);
System.out.println("passed: " + passed);
if (!passed) {
throw new RuntimeException("Test fails!");
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Follow the instructions below:");
frame.setSize(700, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
JTextComponent textComponent = new JTextArea(INSTRUCTIONS);
textComponent.setEditable(false);
Font font = textComponent.getFont();
font = font.deriveFont(24.0f);
textComponent.setFont(font);
panel.add(textComponent, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(new FlowLayout());
JButton passButton = new JButton("Pass");
passButton.addActionListener((e) -> {
passed = true;
latch.countDown();
frame.dispose();
});
JButton failsButton = new JButton("Fail");
failsButton.addActionListener((e) -> {
passed = false;
latch.countDown();
frame.dispose();
});
buttonsPanel.add(passButton);
buttonsPanel.add(failsButton);
panel.add(buttonsPanel, BorderLayout.SOUTH);
frame.getContentPane().add(panel);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
latch.countDown();
}
});
frame.setVisible(true);
}
}

View File

@ -0,0 +1,499 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Robot;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.MetalTextFieldUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.PasswordView;
import javax.swing.text.PlainView;
import javax.swing.text.View;
import javax.swing.text.WrappedPlainView;
/**
* @test
* @bug 8156217
* @key headful
* @summary Selected text is shifted on HiDPI display
* @run main FPMethodCalledTest
*/
public class FPMethodCalledTest {
private static JFrame frame;
private static JTextField textField;
public static void main(String[] args) throws Exception {
for (Test test : TESTS) {
test(test);
}
}
static void test(final Test test) throws Exception {
try {
Robot robot = new Robot();
robot.setAutoDelay(50);
SwingUtilities.invokeAndWait(() -> {
createAndShowGUI(test);
});
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
textField.select(1, 3);
});
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
Resultable resultable = test.resultable;
if (!resultable.getResult()) {
throw new RuntimeException("Test fails for: " + resultable);
}
});
} finally {
SwingUtilities.invokeAndWait(() -> {
if (frame != null) {
frame.dispose();
}
});
}
}
static void createAndShowGUI(Test test) {
try {
UIManager.setLookAndFeel(new MetalLookAndFeel());
} catch (Exception e) {
throw new RuntimeException(e);
}
frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new FlowLayout());
String text = "AAAAAAA";
textField = test.isPasswordField()
? new JPasswordField(text)
: new JTextField(text);
textField.setUI(new MetalTextFieldUI() {
@Override
public View create(Element elem) {
return test.createView(elem);
}
});
panel.add(textField);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
private static final Test[] TESTS = {
new Test() {
@Override
View createView(Element elem) {
PlainViewINTAPI view = new PlainViewINTAPI(elem);
resultable = view;
return view;
}
},
new Test() {
@Override
View createView(Element elem) {
PlainViewFPAPI view = new PlainViewFPAPI(elem);
resultable = view;
return view;
}
},
new Test() {
@Override
View createView(Element elem) {
PlainViewMixedAPI view = new PlainViewMixedAPI(elem);
resultable = view;
return view;
}
},
new Test() {
@Override
View createView(Element elem) {
WrappedPlainViewINTAPI view = new WrappedPlainViewINTAPI(elem);
resultable = view;
return view;
}
},
new Test() {
@Override
View createView(Element elem) {
WrappedPlainViewFPAPI view = new WrappedPlainViewFPAPI(elem);
resultable = view;
return view;
}
},
new Test() {
@Override
View createView(Element elem) {
WrappedPlainViewMixedAPI view = new WrappedPlainViewMixedAPI(elem);
resultable = view;
return view;
}
},
new Test(true) {
@Override
View createView(Element elem) {
PasswordViewINTAPI view = new PasswordViewINTAPI(elem);
resultable = view;
return view;
}
},
new Test(true) {
@Override
View createView(Element elem) {
PasswordViewFPAPI view = new PasswordViewFPAPI(elem);
resultable = view;
return view;
}
},
new Test(true) {
@Override
View createView(Element elem) {
PasswordViewMixedAPI view = new PasswordViewMixedAPI(elem);
resultable = view;
return view;
}
}
};
static interface Resultable {
boolean getResult();
}
static abstract class Test {
Resultable resultable;
final boolean isPasswordField;
public Test() {
this(false);
}
public Test(boolean isPasswordField) {
this.isPasswordField = isPasswordField;
}
boolean isPasswordField() {
return isPasswordField;
}
abstract View createView(Element elem);
}
static class PlainViewINTAPI extends PlainView implements Resultable {
boolean drawLine = false;
boolean drawSelected = false;
boolean drawUnselected = false;
public PlainViewINTAPI(Element elem) {
super(elem);
}
@Override
protected void drawLine(int lineIndex, Graphics g, int x, int y) {
drawLine = true;
super.drawLine(lineIndex, g, x, y);
}
@Override
protected int drawSelectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
drawSelected = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
drawUnselected = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return drawLine && drawSelected && drawUnselected;
}
}
static class PlainViewFPAPI extends PlainView implements Resultable {
boolean drawLine = false;
boolean drawSelected = false;
boolean drawUnselected = false;
public PlainViewFPAPI(Element elem) {
super(elem);
}
@Override
protected void drawLine(int lineIndex, Graphics2D g, float x, float y) {
drawLine = true;
super.drawLine(lineIndex, g, x, y);
}
@Override
protected float drawSelectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
drawSelected = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
drawUnselected = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return drawSelected;
}
}
static class PlainViewMixedAPI extends PlainView implements Resultable {
boolean isIntMethodCalled = false;
boolean isFPMethodCalled = false;
public PlainViewMixedAPI(Element elem) {
super(elem);
}
@Override
protected int drawSelectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
isIntMethodCalled = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
protected float drawSelectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
isFPMethodCalled = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return !isIntMethodCalled && isFPMethodCalled;
}
}
static class WrappedPlainViewINTAPI extends WrappedPlainView implements Resultable {
boolean drawLine = false;
boolean drawSelected = false;
boolean drawUnselected = false;
public WrappedPlainViewINTAPI(Element elem) {
super(elem);
}
@Override
protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
drawLine = true;
super.drawLine(p0, p1, g, x, y);
}
@Override
protected int drawSelectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
drawSelected = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
drawUnselected = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return drawLine && drawSelected && drawUnselected;
}
}
static class WrappedPlainViewFPAPI extends WrappedPlainView implements Resultable {
boolean drawLine = false;
boolean drawSelected = false;
boolean drawUnselected = false;
public WrappedPlainViewFPAPI(Element elem) {
super(elem);
}
@Override
protected void drawLine(int p0, int p1, Graphics2D g, float x, float y) {
drawLine = true;
super.drawLine(p0, p1, g, x, y);
}
@Override
protected float drawSelectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
drawSelected = true;
return super.drawSelectedText(g, x, y, p0, p1);
}
@Override
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
drawUnselected = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return drawLine && drawSelected && drawUnselected;
}
}
static class WrappedPlainViewMixedAPI extends WrappedPlainView implements Resultable {
boolean isIntMethodCalled = false;
boolean isFPMethodCalled = false;
public WrappedPlainViewMixedAPI(Element elem) {
super(elem);
}
@Override
protected int drawUnselectedText(Graphics g, int x, int y,
int p0, int p1) throws BadLocationException {
isIntMethodCalled = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
protected float drawUnselectedText(Graphics2D g, float x, float y,
int p0, int p1) throws BadLocationException {
isFPMethodCalled = true;
return super.drawUnselectedText(g, x, y, p0, p1);
}
@Override
public boolean getResult() {
return !isIntMethodCalled && isFPMethodCalled;
}
}
static class PasswordViewINTAPI extends PasswordView implements Resultable {
boolean isIntMethodCalled = false;
public PasswordViewINTAPI(Element elem) {
super(elem);
}
@Override
protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
isIntMethodCalled = true;
return super.drawEchoCharacter(g, x, y, c);
}
@Override
public boolean getResult() {
return isIntMethodCalled;
}
}
static class PasswordViewFPAPI extends PasswordView implements Resultable {
boolean isFPMethodCalled = false;
public PasswordViewFPAPI(Element elem) {
super(elem);
}
@Override
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
isFPMethodCalled = true;
return super.drawEchoCharacter(g, x, y, c);
}
@Override
public boolean getResult() {
return isFPMethodCalled;
}
}
static class PasswordViewMixedAPI extends PasswordView implements Resultable {
boolean isIntMethodCalled = false;
boolean isFPMethodCalled = false;
public PasswordViewMixedAPI(Element elem) {
super(elem);
}
@Override
protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
isIntMethodCalled = true;
return super.drawEchoCharacter(g, x, y, c);
}
@Override
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
isFPMethodCalled = true;
return super.drawEchoCharacter(g, x, y, c);
}
@Override
public boolean getResult() {
return !isIntMethodCalled && isFPMethodCalled;
}
}
}