--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/TextUI.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/TextUI.java Thu Oct 20 12:18:15 2016 +0300
@@ -46,7 +46,11 @@
* @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 @@
* @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 @@
* 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 @@
*
* @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 @@
* @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;
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Thu Oct 20 12:18:15 2016 +0300
@@ -28,6 +28,8 @@
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 @@
* @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 @@
* @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 @@
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 @@
* @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 @@
* @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 @@
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) {
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/multi/MultiTextUI.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/multi/MultiTextUI.java Thu Oct 20 12:18:15 2016 +0300
@@ -38,6 +38,8 @@
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 @@
*
* @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 @@
*
* @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 @@
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 @@
* @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 @@
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.
*
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java Thu Oct 20 12:18:15 2016 +0300
@@ -98,26 +98,27 @@
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 @@
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;
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/GlyphPainter2.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/GlyphPainter2.java Thu Oct 20 12:18:15 2016 +0300
@@ -145,8 +145,9 @@
// 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;
}
/**
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java Thu Oct 20 12:18:15 2016 +0300
@@ -49,6 +49,8 @@
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,12 +1372,38 @@
* @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.
* The component must have a positive size for
@@ -1388,12 +1416,36 @@
* 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
* from the model. The current selection is reset. Does nothing
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/ParagraphView.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/ParagraphView.java Thu Oct 20 12:18:15 2016 +0300
@@ -27,6 +27,7 @@
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 @@
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;
}
/**
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/PasswordView.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/PasswordView.java Thu Oct 20 12:18:15 2016 +0300
@@ -26,7 +26,11 @@
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 @@
* @param p1 the ending offset in the model >= p0
* @return the X location of the end of the range >= 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 @@
}
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 @@
* @param p1 the ending offset in the model >= p0
* @return the X location of the end of the range >= 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 @@
* @param y the starting Y coordinate >= 0
* @param c the echo character
* @return the updated X position >= 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 @@
* 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 @@
}
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);
+ }
+ });
+ }
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java Thu Oct 20 12:18:15 2016 +0300
@@ -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
@@ -61,17 +64,6 @@
}
/**
- * 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
* to the methods <code>drawUnselectedText</code> and
@@ -84,8 +76,16 @@
* @param y the starting Y position >= 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 @@
* {@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 @@
} 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 @@
* @param p1 the ending position in the model >= 0
* @return the X location of the end of the range >= 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 @@
* 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 @@
* @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 @@
* @param p1 the ending position in the model >= 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 @@
* 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 @@
* @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 @@
// 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 @@
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 @@
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 @@
* given point in the view >= 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 @@
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 @@
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 @@
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 @@
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 @@
Font font;
Segment lineBuffer;
- int tabSize;
+ float tabSize;
int tabBase;
int sel0;
@@ -796,4 +876,46 @@
*/
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;
+ }
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/Utilities.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/Utilities.java Thu Oct 20 12:18:15 2016 +0300
@@ -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 @@
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document >= 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 @@
* 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 @@
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 @@
&& 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 @@
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 @@
}
}
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 @@
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document >= 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 @@
* 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 @@
// 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)
+ {
+ 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) {
- int nextX = x;
+ 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 @@
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 @@
} 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 @@
* tabs will be expanded as a space character.
* @param startOffset starting offset of the text in the document >= 0
* @return the offset into the text >= 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 @@
int startOffset,
int[] justificationData) {
return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true,
- justificationData);
+ justificationData, false);
}
/**
@@ -365,13 +414,19 @@
* @param startOffset starting offset of the text in the document >= 0
* @param round whether or not to round
* @return the offset into the text >= 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 @@
* @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 @@
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 @@
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 @@
)){
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 @@
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 @@
} 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 @@
* 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 @@
* 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 @@
* @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 @@
* @return the position >= 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 @@
* @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 @@
* @return the position >= 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 @@
* @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 @@
*/
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 @@
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);
}
/**
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/WrappedPlainView.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/WrappedPlainView.java Thu Oct 20 12:18:15 2016 +0300
@@ -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)
@@ -87,17 +91,6 @@
}
/**
- * 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
* to the methods <code>drawUnselectedText</code> and
@@ -111,8 +104,17 @@
* @param y the starting Y position >= 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 @@
* <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 >= 0
* @param p1 the ending document location to use >= p1
* @param g the graphics context
@@ -154,12 +152,17 @@
* @param y the starting Y position >= 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 @@
} 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 @@
* @param p1 the ending position in the model >= p0
* @return the X location of the end of the range >= 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 @@
* 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 >= 0
* @param y the starting Y coordinate >= 0
@@ -232,10 +253,12 @@
* @param p1 the ending position in the model >= p0
* @return the X location of the end of the range >= 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 @@
* @param p1 the ending position in the model >= 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 @@
* 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 >= 0
* @param y the starting Y coordinate >= 0
@@ -279,10 +321,12 @@
* @param p1 the ending position in the model >= 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 @@
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 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 @@
Segment lineBuffer;
boolean widthChanging;
int tabBase;
- int tabSize;
+ float tabSize;
boolean wordWrap;
int sel0;
@@ -668,6 +718,7 @@
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 @@
: 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 @@
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;
+ }
}
--- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Thu Oct 20 12:18:15 2016 +0300
@@ -30,6 +30,7 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
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 @@
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 {
--- a/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Wed Oct 19 08:06:10 2016 -0700
+++ b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Thu Oct 20 12:18:15 2016 +0300
@@ -41,13 +41,13 @@
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 @@
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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/text/Caret/8163124/CaretFloatingPointAPITest.java Thu Oct 20 12:18:15 2016 +0300
@@ -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);
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/text/JTextComponent/8156217/TextSelectionTest.java Thu Oct 20 12:18:15 2016 +0300
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/text/View/8156217/FPMethodCalledTest.java Thu Oct 20 12:18:15 2016 +0300
@@ -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;
+ }
+ }
+}